using System.Collections.Generic; using System.IO; using System.Linq; using System.Text.RegularExpressions; using AddressableAssetsIntegrationTests; using NUnit.Framework; using UnityEditor.AddressableAssets.Build; using UnityEditor.AddressableAssets.Build.DataBuilders; using UnityEditor.AddressableAssets.Settings; using UnityEditor.AddressableAssets.Settings.GroupSchemas; using UnityEditor.Build.Pipeline; using UnityEngine; using UnityEngine.AddressableAssets; using UnityEngine.AddressableAssets.ResourceLocators; using UnityEngine.ResourceManagement.ResourceProviders; using UnityEngine.TestTools; namespace UnityEditor.AddressableAssets.Tests { public class ContentUpdateTests : AddressableAssetTestBase { protected override bool PersistSettings { get { return true; } } [Test] public void CanCreateContentStateData() { var group = Settings.CreateGroup("LocalStuff", false, false, false, null); var schema = group.AddSchema(); schema.BuildPath.SetVariableByName(Settings, AddressableAssetSettings.kLocalBuildPath); schema.LoadPath.SetVariableByName(Settings, AddressableAssetSettings.kLocalLoadPath); schema.BundleMode = BundledAssetGroupSchema.BundlePackingMode.PackTogether; group.AddSchema().StaticContent = true; Settings.CreateOrMoveEntry(m_AssetGUID, group); var context = new AddressablesDataBuilderInput(Settings); var op = Settings.ActivePlayerDataBuilder.BuildData(context); Assert.IsTrue(string.IsNullOrEmpty(op.Error), op.Error); var tempPath = Path.GetDirectoryName(Application.dataPath) + "/" + Addressables.LibraryPath + PlatformMappingService.GetPlatformPathSubFolder() + "/addressables_content_state.bin"; var cacheData = ContentUpdateScript.LoadContentState(tempPath); Assert.NotNull(cacheData); Settings.RemoveGroup(group); } [Test] public void ContentState_WithDisabledGroups_DoesNotInclude_EntriesFromGroup() { var group = Settings.CreateGroup("RemoteStuff", false, false, false, null); var schema = group.AddSchema(); schema.BuildPath.SetVariableByName(Settings, AddressableAssetSettings.kRemoteBuildPath); schema.LoadPath.SetVariableByName(Settings, AddressableAssetSettings.kRemoteLoadPath); schema.BundleMode = BundledAssetGroupSchema.BundlePackingMode.PackTogether; group.AddSchema().StaticContent = true; Settings.CreateOrMoveEntry(m_AssetGUID, group); var group2 = Settings.CreateGroup("LocalStuff", false, false, false, null); var schema2 = group2.AddSchema(); schema2.BuildPath.SetVariableByName(Settings, AddressableAssetSettings.kRemoteBuildPath); schema2.LoadPath.SetVariableByName(Settings, AddressableAssetSettings.kRemoteLoadPath); schema2.BundleMode = BundledAssetGroupSchema.BundlePackingMode.PackTogether; group2.AddSchema().StaticContent = true; Settings.CreateOrMoveEntry(m_SceneGuids[0], group2); var context = new AddressablesDataBuilderInput(Settings); var op = Settings.ActivePlayerDataBuilder.BuildData(context); Assert.IsTrue(string.IsNullOrEmpty(op.Error), op.Error); var tempPath = Path.GetDirectoryName(Application.dataPath) + "/" + Addressables.LibraryPath + PlatformMappingService.GetPlatformPathSubFolder() + "/addressables_content_state.bin"; var cacheData = ContentUpdateScript.LoadContentState(tempPath); Assert.NotNull(cacheData); Assert.NotNull(cacheData.cachedInfos.FirstOrDefault(s => s.asset.guid.ToString() == m_AssetGUID)); schema.IncludeInBuild = false; context = new AddressablesDataBuilderInput(Settings); op = Settings.ActivePlayerDataBuilder.BuildData(context); Assert.IsTrue(string.IsNullOrEmpty(op.Error), op.Error); tempPath = Path.GetDirectoryName(Application.dataPath) + "/" + Addressables.LibraryPath + PlatformMappingService.GetPlatformPathSubFolder() + "/addressables_content_state.bin"; cacheData = ContentUpdateScript.LoadContentState(tempPath); Assert.NotNull(cacheData); Assert.IsNull(cacheData.cachedInfos.FirstOrDefault(s => s.asset.guid.ToString() == m_AssetGUID)); Settings.RemoveGroup(group); Settings.RemoveGroup(group2); } [Test] public void CreateCustomLocator_ReturnsLocatorWithUniqueId() { ContentCatalogData ccd = new ContentCatalogData(); ccd.SetData(new List()); IResourceLocator map = ccd.CreateCustomLocator("test"); Assert.AreEqual("test", map.LocatorId); } [Test] public void DownloadBinFileToTempLocation_DoesNotThrowError_WhenDownloadFails() { Assert.DoesNotThrow(() => { var returnValue = ContentUpdateScript.DownloadBinFileToTempLocation("http://notarealurl.com/addressable_state.bin"); Assert.AreEqual(ContentUpdateScript.PreviousContentStateFileCachePath, returnValue); }); } [Test] public void PrepareContentUpdate() { var group = Settings.CreateGroup("LocalStuff2", false, false, false, null); var schema = group.AddSchema(); schema.BuildPath.SetVariableByName(Settings, AddressableAssetSettings.kLocalBuildPath); schema.LoadPath.SetVariableByName(Settings, AddressableAssetSettings.kLocalLoadPath); schema.BundleMode = BundledAssetGroupSchema.BundlePackingMode.PackTogether; group.AddSchema().StaticContent = true; var entry = Settings.CreateOrMoveEntry(m_AssetGUID, group); entry.address = "test"; var context = new AddressablesDataBuilderInput(Settings); Settings.ActivePlayerDataBuilder.BuildData(context); var path = AssetDatabase.GUIDToAssetPath(m_AssetGUID); var obj = AssetDatabase.LoadAssetAtPath(path); obj.GetComponent().SetPositionAndRotation(new Vector3(10, 10, 10), Quaternion.identity); PrefabUtility.SavePrefabAsset(obj); AssetDatabase.SaveAssets(); var tempPath = Path.GetDirectoryName(Application.dataPath) + "/" + Addressables.LibraryPath + PlatformMappingService.GetPlatformPathSubFolder() + "/addressables_content_state.bin"; var modifiedEntries = ContentUpdateScript.GatherModifiedEntries(Settings, tempPath); Assert.IsNotNull(modifiedEntries); Assert.GreaterOrEqual(modifiedEntries.Count, 1); ContentUpdateScript.CreateContentUpdateGroup(Settings, modifiedEntries, "Content Update"); var contentGroup = Settings.FindGroup("Content Update"); Assert.IsNotNull(contentGroup); var movedEntry = contentGroup.GetAssetEntry(m_AssetGUID); Assert.AreSame(movedEntry, entry); Settings.RemoveGroup(group); } [Test] public void GatherModifiedEntries_WhenDependencyBundleNameIsSame_DependencyIsNotFlaggedAsModified() { // Create assets GameObject mainObject = new GameObject("mainObject"); Material mat = new Material(Shader.Find("Transparent/Diffuse")); mainObject.AddComponent().material = mat; string mainAssetPath = GetAssetPath("mainObject.prefab"); string staticAssetPath = GetAssetPath("staticObject.mat"); AssetDatabase.CreateAsset(mat, staticAssetPath); PrefabUtility.SaveAsPrefabAsset(mainObject, mainAssetPath); AssetDatabase.SaveAssets(); // Create addressables AddressableAssetGroup mainAssetGroup = Settings.CreateGroup("PrefabGroup", false, false, false, null); AddressableAssetGroup staticContentGroup = Settings.CreateGroup("MatGroup", false, false, false, null); var schema = mainAssetGroup.AddSchema(); schema.BuildPath.SetVariableByName(Settings, AddressableAssetSettings.kLocalBuildPath); schema.LoadPath.SetVariableByName(Settings, AddressableAssetSettings.kLocalLoadPath); schema.BundleMode = BundledAssetGroupSchema.BundlePackingMode.PackTogether; mainAssetGroup.AddSchema().StaticContent = true; schema = staticContentGroup.AddSchema(); schema.BuildPath.SetVariableByName(Settings, AddressableAssetSettings.kLocalBuildPath); schema.LoadPath.SetVariableByName(Settings, AddressableAssetSettings.kLocalLoadPath); schema.BundleMode = BundledAssetGroupSchema.BundlePackingMode.PackTogether; staticContentGroup.AddSchema().StaticContent = true; AddressableAssetEntry mainEntry = Settings.CreateOrMoveEntry(AssetDatabase.AssetPathToGUID(mainAssetPath), mainAssetGroup); AddressableAssetEntry staticEntry = Settings.CreateOrMoveEntry(AssetDatabase.AssetPathToGUID(staticAssetPath), staticContentGroup); // Build var context = new AddressablesDataBuilderInput(Settings); Settings.ActivePlayerDataBuilder.BuildData(context); // Modify assets var mainAsset = AssetDatabase.LoadAssetAtPath(mainAssetPath); mainAsset.GetComponent().SetPositionAndRotation(new Vector3(10, 10, 10), Quaternion.identity); PrefabUtility.SavePrefabAsset(mainAsset); AssetDatabase.SaveAssets(); // Test var tempPath = Path.GetDirectoryName(Application.dataPath) + "/Library/com.unity.addressables/" + PlatformMappingService.GetPlatformPathSubFolder() + "/addressables_content_state.bin"; var modifiedEntries = ContentUpdateScript.GatherModifiedEntries(Settings, tempPath); Assert.AreEqual(1, modifiedEntries.Count); Assert.AreSame(modifiedEntries[0], mainEntry); // Cleanup GameObject.DestroyImmediate(mainObject); Settings.RemoveGroup(mainAssetGroup); Settings.RemoveGroup(staticContentGroup); AssetDatabase.DeleteAsset(mainAssetPath); AssetDatabase.DeleteAsset(staticAssetPath); } [Test] public void GatherModifiedEntries_WhenDependencyBundleNameIsChanged_DependencyIsFlaggedAsModified() { // Create assets GameObject mainObject = new GameObject("mainObject"); Material mat = new Material(Shader.Find("Transparent/Diffuse")); mainObject.AddComponent().material = mat; string mainAssetPath = GetAssetPath("mainObject.prefab"); string staticAssetPath = GetAssetPath("staticObject.mat"); AssetDatabase.CreateAsset(mat, staticAssetPath); PrefabUtility.SaveAsPrefabAsset(mainObject, mainAssetPath); AssetDatabase.SaveAssets(); // Create addressables AddressableAssetGroup mainAssetGroup = Settings.CreateGroup("PrefabGroup2", false, false, false, null); AddressableAssetGroup staticContentGroup = Settings.CreateGroup("MatGroup2", false, false, false, null); var schema = mainAssetGroup.AddSchema(); schema.BuildPath.SetVariableByName(Settings, AddressableAssetSettings.kLocalBuildPath); schema.LoadPath.SetVariableByName(Settings, AddressableAssetSettings.kLocalLoadPath); schema.BundleMode = BundledAssetGroupSchema.BundlePackingMode.PackTogether; mainAssetGroup.AddSchema().StaticContent = true; schema = staticContentGroup.AddSchema(); schema.BuildPath.SetVariableByName(Settings, AddressableAssetSettings.kLocalBuildPath); schema.LoadPath.SetVariableByName(Settings, AddressableAssetSettings.kLocalLoadPath); schema.BundleMode = BundledAssetGroupSchema.BundlePackingMode.PackTogether; staticContentGroup.AddSchema().StaticContent = true; AddressableAssetEntry mainEntry = Settings.CreateOrMoveEntry(AssetDatabase.AssetPathToGUID(mainAssetPath), mainAssetGroup); AddressableAssetEntry staticEntry = Settings.CreateOrMoveEntry(AssetDatabase.AssetPathToGUID(staticAssetPath), staticContentGroup); // Build var context = new AddressablesDataBuilderInput(Settings); Settings.ActivePlayerDataBuilder.BuildData(context); // Modify assets var mainAsset = AssetDatabase.LoadAssetAtPath(mainAssetPath); mainAsset.GetComponent().SetPositionAndRotation(new Vector3(10, 10, 10), Quaternion.identity); PrefabUtility.SavePrefabAsset(mainAsset); staticContentGroup.GetSchema().InternalBundleIdMode = BundledAssetGroupSchema.BundleInternalIdMode.GroupGuidProjectIdEntriesHash; AssetDatabase.SaveAssets(); // Test var tempPath = Path.GetDirectoryName(Application.dataPath) + "/Library/com.unity.addressables/" + PlatformMappingService.GetPlatformPathSubFolder() + "/addressables_content_state.bin"; var modifiedEntries = ContentUpdateScript.GatherModifiedEntries(Settings, tempPath); Assert.AreEqual(2, modifiedEntries.Count); Assert.IsTrue(modifiedEntries.Contains(staticEntry)); // Cleanup GameObject.DestroyImmediate(mainObject); Settings.RemoveGroup(mainAssetGroup); Settings.RemoveGroup(staticContentGroup); AssetDatabase.DeleteAsset(mainAssetPath); AssetDatabase.DeleteAsset(staticAssetPath); } [Test] public void GetStaticContentDependenciesOfModifiedEntries_FlagsEntryDependencies_WithStaticContentEnabled() { var mainAssetGroup = Settings.CreateGroup("MainAssetGroup", false, false, false, null, typeof(ContentUpdateGroupSchema), typeof(BundledAssetGroupSchema)); var staticContentGroup = Settings.CreateGroup("StaticContentGroup", false, false, false, null, typeof(ContentUpdateGroupSchema), typeof(BundledAssetGroupSchema)); mainAssetGroup.GetSchema().StaticContent = true; staticContentGroup.GetSchema().StaticContent = true; GameObject mainObject = new GameObject("mainObject"); Material mat = new Material(Shader.Find("Transparent/Diffuse")); mainObject.AddComponent().material = mat; string mainAssetPath = GetAssetPath("mainObject.prefab"); string staticAssetPath = GetAssetPath("staticObject.mat"); AssetDatabase.CreateAsset(mat, staticAssetPath); PrefabUtility.SaveAsPrefabAsset(mainObject, mainAssetPath); AssetDatabase.SaveAssets(); var mainEntry = Settings.CreateOrMoveEntry(AssetDatabase.AssetPathToGUID(mainAssetPath), mainAssetGroup); var staticEntry = Settings.CreateOrMoveEntry(AssetDatabase.AssetPathToGUID(staticAssetPath), staticContentGroup); List modifiedEntries = new List() { mainEntry }; Dictionary> staticDependencies = new Dictionary>() { {mainEntry, new List()} }; ContentUpdateScript.GetStaticContentDependenciesForEntries(Settings, ref staticDependencies, null); Assert.AreEqual(1, staticDependencies.Count); Assert.AreEqual(1, staticDependencies[mainEntry].Count); Assert.IsTrue(staticDependencies[mainEntry].Contains(staticEntry)); //Cleanup GameObject.DestroyImmediate(mainObject); Settings.RemoveGroup(mainAssetGroup); Settings.RemoveGroup(staticContentGroup); AssetDatabase.DeleteAsset(mainAssetPath); AssetDatabase.DeleteAsset(staticAssetPath); } [Test] public void GetStaticContentDependenciesOfModifiedEntries_DoesNotFlagEntryDependencies_WithStaticContentDisabled() { var mainAssetGroup = Settings.CreateGroup("MainAssetGroup", false, false, false, null, typeof(ContentUpdateGroupSchema), typeof(BundledAssetGroupSchema)); var dynamicContentGroup = Settings.CreateGroup("DynamicContentGroup", false, false, false, null, typeof(ContentUpdateGroupSchema), typeof(BundledAssetGroupSchema)); mainAssetGroup.GetSchema().StaticContent = true; dynamicContentGroup.GetSchema().StaticContent = false; GameObject mainObject = new GameObject("mainObject"); Material mat = new Material(Shader.Find("Transparent/Diffuse")); mainObject.AddComponent().material = mat; string mainAssetPath = GetAssetPath("mainObject.prefab"); string dynamicAssetPath = GetAssetPath("dynamicObject.mat"); AssetDatabase.CreateAsset(mat, dynamicAssetPath); PrefabUtility.SaveAsPrefabAsset(mainObject, mainAssetPath); AssetDatabase.SaveAssets(); var mainEntry = Settings.CreateOrMoveEntry(AssetDatabase.AssetPathToGUID(mainAssetPath), mainAssetGroup); Settings.CreateOrMoveEntry(AssetDatabase.AssetPathToGUID(dynamicAssetPath), dynamicContentGroup); List modifiedEntries = new List() { mainEntry }; Dictionary> staticDependencies = new Dictionary>() { {mainEntry, new List()} }; ContentUpdateScript.GetStaticContentDependenciesForEntries(Settings, ref staticDependencies, null); Assert.AreEqual(1, staticDependencies.Count); Assert.AreEqual(0, staticDependencies[mainEntry].Count); //Cleanup GameObject.DestroyImmediate(mainObject); Settings.RemoveGroup(mainAssetGroup); Settings.RemoveGroup(dynamicContentGroup); AssetDatabase.DeleteAsset(mainAssetPath); AssetDatabase.DeleteAsset(dynamicAssetPath); } [Test] public void GetStaticContentDependenciesOfModifiedEntries_WhenFolderHasModifiedImplicitAsset_DependenciesFlaggedAsModified() { var mainAssetGroup = Settings.CreateGroup("MainAssetGroup", false, false, false, null, typeof(ContentUpdateGroupSchema), typeof(BundledAssetGroupSchema)); mainAssetGroup.GetSchema().StaticContent = true; mainAssetGroup.GetSchema().InternalBundleIdMode = BundledAssetGroupSchema.BundleInternalIdMode.GroupGuidProjectIdEntriesHash; GameObject mainObject = new GameObject("mainObject"); Material mat = new Material(Shader.Find("Transparent/Diffuse")); mainObject.AddComponent().material = mat; string folderName = "ContentUpdateFolder"; string folderGuid = AssetDatabase.CreateFolder(ConfigFolder, folderName); string folderPath = Path.Combine(ConfigFolder, folderName).Replace("\\", "/"); string mainAssetPath = Path.Combine(folderPath, "mainAsset.prefab").Replace("\\", "/"); string subAssetPath = Path.Combine(folderPath, "subAsset.mat").Replace("\\", "/"); AssetDatabase.CreateAsset(mat, subAssetPath); PrefabUtility.SaveAsPrefabAsset(mainObject, mainAssetPath); AssetDatabase.SaveAssets(); string mainAssetGuid = AssetDatabase.AssetPathToGUID(mainAssetPath); string subAssetGuid = AssetDatabase.AssetPathToGUID(subAssetPath); var folderEntry = Settings.CreateOrMoveEntry(folderGuid, mainAssetGroup); var entries = new List(); folderEntry.GatherAllAssets(entries, false, false, true); AddressableAssetEntry mainAssetEntry = entries.Find(x => x.guid == mainAssetGuid); var staticDependencies = new Dictionary>() { {mainAssetEntry, new List()} }; string cachedBundleName = "cachedBundleName"; var groupGuidToCacheBundleName = new Dictionary() { {folderGuid, cachedBundleName}, {mainAssetGuid, cachedBundleName}, {subAssetGuid, cachedBundleName}, }; ContentUpdateScript.GetStaticContentDependenciesForEntries(Settings, ref staticDependencies, groupGuidToCacheBundleName); Assert.AreEqual(1, staticDependencies.Count); Assert.AreEqual(2, staticDependencies[mainAssetEntry].Count); //Cleanup GameObject.DestroyImmediate(mainObject); Settings.RemoveGroup(mainAssetGroup); AssetDatabase.DeleteAsset(folderPath); } static IResourceLocator GetLocatorFromCatalog(IEnumerable paths) { foreach (var p in paths) { if(Path.GetFileNameWithoutExtension(p).EndsWith("catalog")) return ContentCatalogData.LoadFromFile(p).CreateCustomLocator(); } return null; } #if !ENABLE_BINARY_CATALOG [Test] public void WhenContentUpdated_NewCatalogRetains_OldCatalogBundleLoadData() { var group = Settings.CreateGroup("LocalStuff3", false, false, false, null); Settings.BuildRemoteCatalog = true; Settings.RemoteCatalogBuildPath = new ProfileValueReference(); Settings.RemoteCatalogBuildPath.SetVariableByName(Settings, AddressableAssetSettings.kRemoteBuildPath); Settings.RemoteCatalogLoadPath = new ProfileValueReference(); Settings.RemoteCatalogLoadPath.SetVariableByName(Settings, AddressableAssetSettings.kRemoteLoadPath); var schema = group.AddSchema(); schema.BuildPath.SetVariableByName(Settings, AddressableAssetSettings.kLocalBuildPath); schema.LoadPath.SetVariableByName(Settings, AddressableAssetSettings.kLocalLoadPath); schema.BundleMode = BundledAssetGroupSchema.BundlePackingMode.PackTogether; schema.UseAssetBundleCrc = true; schema.UseAssetBundleCache = true; group.AddSchema().StaticContent = true; Settings.CreateOrMoveEntry(m_AssetGUID, group); var context = new AddressablesDataBuilderInput(Settings); var op = Settings.ActivePlayerDataBuilder.BuildData(context); Assert.IsTrue(string.IsNullOrEmpty(op.Error), op.Error); var origLocator = GetLocatorFromCatalog(op.FileRegistry.GetFilePaths()); Assert.NotNull(origLocator); var tempPath = Path.GetDirectoryName(Application.dataPath) + "/" + Addressables.LibraryPath + PlatformMappingService.GetPlatformPathSubFolder() + "/addressables_content_state.bin"; var contentState = ContentUpdateScript.LoadContentState(tempPath); Assert.NotNull(contentState); Assert.NotNull(contentState.cachedBundles); Assert.AreEqual(1, contentState.cachedBundles.Length); var buildOp = ContentUpdateScript.BuildContentUpdate(Settings, tempPath); Assert.IsNotNull(buildOp); Assert.IsTrue(string.IsNullOrEmpty(buildOp.Error)); var updatedLocator = GetLocatorFromCatalog(buildOp.FileRegistry.GetFilePaths()); Assert.IsNotNull(updatedLocator); foreach (var k in updatedLocator.Keys) { if (origLocator.Locate(k, typeof(IAssetBundleResource), out var origLocs)) { Assert.IsTrue(updatedLocator.Locate(k, typeof(IAssetBundleResource), out var updatedLocs)); Assert.AreEqual(1, origLocs.Count); Assert.AreEqual(1, updatedLocs.Count); var oLoc = origLocs[0]; var uLoc = updatedLocs[0]; Assert.NotNull(oLoc.Data); Assert.NotNull(uLoc.Data); var oData = oLoc.Data as AssetBundleRequestOptions; var uData = uLoc.Data as AssetBundleRequestOptions; Assert.NotNull(oData); Assert.NotNull(uData); Assert.AreEqual(oData.Hash, uData.Hash); Assert.AreEqual(oData.Crc, uData.Crc); } } Settings.RemoveGroup(group); } #endif [Test] public void IsCacheDataValid_WhenNoPreviousRemoteCatalogPath_ReturnsFalseWithError() { AddressablesContentState cacheData = new AddressablesContentState(); cacheData.editorVersion = Application.unityVersion; Assert.IsFalse(ContentUpdateScript.IsCacheDataValid(Settings, cacheData)); LogAssert.Expect(LogType.Error, new Regex("Previous build had 'Build Remote Catalog' disabled.*")); } [Test] public void IsCacheDataValid_WhenRemoteCatalogDisabled_ReturnsFalseWithError() { AddressablesContentState cacheData = new AddressablesContentState(); cacheData.editorVersion = Application.unityVersion; cacheData.remoteCatalogLoadPath = "somePath"; var oldSetting = Settings.BuildRemoteCatalog; Settings.BuildRemoteCatalog = false; Assert.IsFalse(ContentUpdateScript.IsCacheDataValid(Settings, cacheData)); LogAssert.Expect(LogType.Error, new Regex("Current settings have 'Build Remote Catalog' disabled.*")); Settings.BuildRemoteCatalog = oldSetting; } [Test] public void IsCacheDataValid_WhenMismatchedCatalogPaths_ReturnsFalseWithError() { AddressablesContentState cacheData = new AddressablesContentState(); cacheData.editorVersion = Application.unityVersion; cacheData.remoteCatalogLoadPath = "somePath"; var oldSetting = Settings.BuildRemoteCatalog; Settings.BuildRemoteCatalog = true; Assert.IsFalse(ContentUpdateScript.IsCacheDataValid(Settings, cacheData)); LogAssert.Expect(LogType.Error, new Regex("Current 'Remote Catalog Load Path' does not match load path of original player.*")); Settings.BuildRemoteCatalog = oldSetting; } [Test] public void IsCacheDataValid_WhenMismatchedEditorVersions_LogsWarning() { AddressablesContentState cacheData = new AddressablesContentState(); cacheData.editorVersion = "invalid"; Assert.IsFalse(ContentUpdateScript.IsCacheDataValid(Settings, cacheData)); LogAssert.Expect(LogType.Warning, new Regex(".*with version `" + cacheData.editorVersion + "`.*")); LogAssert.Expect(LogType.Error, new Regex("Previous.*")); } [Test] public void BuildContentUpdate_DoesNotDeleteBuiltData() { var oldSetting = Settings.BuildRemoteCatalog; Settings.BuildRemoteCatalog = true; var group = Settings.CreateGroup("LocalStuff3", false, false, false, null); var schema = group.AddSchema(); schema.BuildPath.SetVariableByName(Settings, AddressableAssetSettings.kLocalBuildPath); schema.LoadPath.SetVariableByName(Settings, AddressableAssetSettings.kLocalLoadPath); schema.BundleMode = BundledAssetGroupSchema.BundlePackingMode.PackTogether; group.AddSchema().StaticContent = true; Settings.CreateOrMoveEntry(m_AssetGUID, group); var context = new AddressablesDataBuilderInput(Settings); var op = Settings.ActivePlayerDataBuilder.BuildData(context); Assert.IsTrue(string.IsNullOrEmpty(op.Error), op.Error); var tempPath = Path.GetDirectoryName(Application.dataPath) + "/" + Addressables.LibraryPath + PlatformMappingService.GetPlatformPathSubFolder() + "/addressables_content_state.bin"; ContentUpdateScript.BuildContentUpdate(Settings, tempPath); Assert.IsTrue(Directory.Exists(Addressables.BuildPath)); Settings.BuildRemoteCatalog = oldSetting; } [Test] public void ContentUpdateScenes_PackedTogether_MarksAllScenesModified() { AddressableAssetGroup group = Settings.CreateGroup("SceneGroup", false, false, false, null, typeof(BundledAssetGroupSchema)); group.GetSchema().BundleMode = BundledAssetGroupSchema.BundlePackingMode.PackTogether; AddressableAssetEntry scene1 = new AddressableAssetEntry(m_SceneGuids[0], "scene1", group, false); AddressableAssetEntry scene2 = new AddressableAssetEntry(m_SceneGuids[1], "scene2", group, false); AddressableAssetEntry scene3 = new AddressableAssetEntry(m_SceneGuids[2], "scene3", group, false); group.AddAssetEntry(scene1, false); group.AddAssetEntry(scene2, false); group.AddAssetEntry(scene3, false); List modifedEnteries = new List() { scene1 }; ContentUpdateScript.AddAllDependentScenesFromModifiedEntries(modifedEnteries); Assert.AreEqual(3, modifedEnteries.Count); Assert.AreEqual(scene1, modifedEnteries[0]); Assert.AreEqual(scene2, modifedEnteries[1]); Assert.AreEqual(scene3, modifedEnteries[2]); Settings.RemoveGroup(group); } [Test] public void ContentUpdateScenes_PackedTogetherByLabel_MarksAllScenesModifiedWithSharedLabel() { AddressableAssetGroup group = Settings.CreateGroup("SceneGroup", false, false, false, null, typeof(BundledAssetGroupSchema)); group.GetSchema().BundleMode = BundledAssetGroupSchema.BundlePackingMode.PackTogetherByLabel; AddressableAssetEntry scene1 = new AddressableAssetEntry(m_SceneGuids[0], "scene1", group, false); AddressableAssetEntry scene2 = new AddressableAssetEntry(m_SceneGuids[1], "scene2", group, false); AddressableAssetEntry scene3 = new AddressableAssetEntry(m_SceneGuids[2], "scene3", group, false); group.AddAssetEntry(scene1, false); group.AddAssetEntry(scene2, false); group.AddAssetEntry(scene3, false); scene1.SetLabel("label", true, true); scene3.SetLabel("label", true, true); List modifedEnteries = new List() { scene1 }; ContentUpdateScript.AddAllDependentScenesFromModifiedEntries(modifedEnteries); Assert.AreEqual(2, modifedEnteries.Count); Assert.AreEqual(scene1, modifedEnteries[0]); Assert.AreEqual(scene3, modifedEnteries[1]); Settings.RemoveGroup(group); } [Test] public void ContentUpdateScenes_PackedSeperately_MarksNoAdditionalScenes() { AddressableAssetGroup group = Settings.CreateGroup("SceneGroup", false, false, false, null, typeof(BundledAssetGroupSchema)); group.GetSchema().BundleMode = BundledAssetGroupSchema.BundlePackingMode.PackSeparately; AddressableAssetEntry scene1 = new AddressableAssetEntry(m_SceneGuids[0], "scene1", group, false); AddressableAssetEntry scene2 = new AddressableAssetEntry(m_SceneGuids[1], "scene2", group, false); AddressableAssetEntry scene3 = new AddressableAssetEntry(m_SceneGuids[2], "scene3", group, false); group.AddAssetEntry(scene1, false); group.AddAssetEntry(scene2, false); group.AddAssetEntry(scene3, false); List modifedEnteries = new List() { scene1 }; ContentUpdateScript.AddAllDependentScenesFromModifiedEntries(modifedEnteries); Assert.AreEqual(1, modifedEnteries.Count); Assert.AreEqual(scene1, modifedEnteries[0]); Settings.RemoveGroup(group); } private ContentUpdateScript.ContentUpdateContext GetContentUpdateContext(string contentUpdateTestAssetGUID, string contentUpdateTestCachedAssetHash, string contentUpdateTestNewInternalBundleName, string contentUpdateTestNewBundleName, string contentUpdateTestCachedBundlePath, string contentUpdateTestGroupGuid, string contentUpdateTestFileName) { var context = new ContentUpdateScript.ContentUpdateContext() { WriteData = new BundleWriteData(), BundleToInternalBundleIdMap = new Dictionary(), GuidToPreviousAssetStateMap = new Dictionary(), IdToCatalogDataEntryMap = new Dictionary(), ContentState = new AddressablesContentState(), PreviousAssetStateCarryOver = new List(), Registry = new FileRegistry() }; AddToContentUpdateContext(context, contentUpdateTestAssetGUID, contentUpdateTestCachedAssetHash, contentUpdateTestNewInternalBundleName, contentUpdateTestNewBundleName, contentUpdateTestCachedBundlePath, contentUpdateTestGroupGuid, contentUpdateTestFileName); return context; } private void AddToContentUpdateContext(ContentUpdateScript.ContentUpdateContext context, string contentUpdateTestAssetGUID, string contentUpdateTestCachedAssetHash, string contentUpdateTestNewInternalBundleName, string contentUpdateTestNewBundleName, string contentUpdateTestCachedBundlePath, string contentUpdateTestGroupGuid, string contentUpdateTestFileName) { context.BundleToInternalBundleIdMap.Add(contentUpdateTestNewInternalBundleName, contentUpdateTestNewBundleName); context.GuidToPreviousAssetStateMap.Add(contentUpdateTestAssetGUID, new CachedAssetState() { bundleFileId = contentUpdateTestCachedBundlePath, asset = new AssetState() { guid = new GUID(contentUpdateTestAssetGUID), hash = Hash128.Parse(contentUpdateTestCachedAssetHash) }, dependencies = new AssetState[] { }, data = null, groupGuid = contentUpdateTestGroupGuid } ); context.IdToCatalogDataEntryMap.Add(contentUpdateTestNewBundleName, new ContentCatalogDataEntry(typeof(IAssetBundleResource), contentUpdateTestNewBundleName, typeof(AssetBundleProvider).FullName, new[] {contentUpdateTestNewBundleName})); context.IdToCatalogDataEntryMap.Add(contentUpdateTestAssetGUID, new ContentCatalogDataEntry(typeof(IAssetBundleResource), contentUpdateTestAssetGUID, typeof(AssetBundleProvider).FullName, new[] {contentUpdateTestAssetGUID})); context.WriteData.AssetToFiles.Add(new GUID(contentUpdateTestAssetGUID), new List() {contentUpdateTestFileName}); context.WriteData.FileToBundle.Add(contentUpdateTestFileName, contentUpdateTestNewInternalBundleName); } private AddressableAssetEntry CreateAssetEntry(string guid, AddressableAssetGroup group) { return new AddressableAssetEntry(guid, guid, group, false); } readonly string m_ContentUpdateTestAssetGUID = GUID.Generate().ToString(); const string k_ContentUpdateTestCachedAssetHash = "8888888888888888888"; const string k_ContentUpdateTestNewInternalBundleName = "bundle"; const string k_ContentUpdateTestNewBundleName = "fullbundlepath"; const string k_ContentUpdateTestCachedBundlePath = "cachedBundle"; const string k_ContentUpdateTestFileName = "testfile"; [Test] public void DetermineRequiredAssetEntryUpdates_AssetWithoutDependenciesAndChangedHashInStaticGroup_ReturnsOnlyRevertToCachedState() { File.WriteAllText(k_ContentUpdateTestCachedBundlePath, "TestCachedAssetBundle"); File.WriteAllText(k_ContentUpdateTestNewBundleName, "TestNewBundle"); var group = Settings.CreateGroup("ContentUpdateTests", false, false, false, null, typeof(BundledAssetGroupSchema), typeof(ContentUpdateGroupSchema)); string contentUpdateTestGroupGuid = group.Guid; group.GetSchema().StaticContent = true; var assetEntry = CreateAssetEntry(m_ContentUpdateTestAssetGUID, group); assetEntry.m_cachedAssetPath = "path"; group.AddAssetEntry(assetEntry); var context = GetContentUpdateContext(m_ContentUpdateTestAssetGUID, k_ContentUpdateTestCachedAssetHash, k_ContentUpdateTestNewInternalBundleName, k_ContentUpdateTestNewBundleName, k_ContentUpdateTestCachedBundlePath, contentUpdateTestGroupGuid, k_ContentUpdateTestFileName); var ops = RevertUnchangedAssetsToPreviousAssetState.DetermineRequiredAssetEntryUpdates(group, context); Assert.AreEqual(assetEntry, ops[0].AssetEntry); Assert.AreEqual(k_ContentUpdateTestNewBundleName, ops[0].CurrentBuildPath); Assert.AreEqual(k_ContentUpdateTestCachedBundlePath, ops[0].PreviousBuildPath); File.Delete(k_ContentUpdateTestCachedBundlePath); File.Delete(k_ContentUpdateTestNewBundleName); Settings.RemoveGroup(group); } [Test] public void DetermineRequiredAssetEntryUpdates_AssetWithDependenciesAndChangedHashInStaticGroup_ReturnsRevertToCachedStateAndDependencies() { File.WriteAllText(k_ContentUpdateTestCachedBundlePath, "TestCachedAssetBundle"); File.WriteAllText(k_ContentUpdateTestNewBundleName, "TestNewBundle"); var group = Settings.CreateGroup("ContentUpdateTests", false, false, false, null, typeof(BundledAssetGroupSchema), typeof(ContentUpdateGroupSchema)); string contentUpdateTestGroupGuid = group.Guid; group.GetSchema().StaticContent = true; var assetEntry = CreateAssetEntry(m_ContentUpdateTestAssetGUID, group); assetEntry.m_cachedAssetPath = "path"; group.AddAssetEntry(assetEntry); var context = GetContentUpdateContext(m_ContentUpdateTestAssetGUID, k_ContentUpdateTestCachedAssetHash, k_ContentUpdateTestNewInternalBundleName, k_ContentUpdateTestNewBundleName, k_ContentUpdateTestCachedBundlePath, contentUpdateTestGroupGuid, k_ContentUpdateTestFileName); context.GuidToPreviousAssetStateMap[m_ContentUpdateTestAssetGUID].dependencies = new AssetState[] { new AssetState() { guid = GUID.Generate(), hash = Hash128.Parse("00000000000000") } }; var ops = RevertUnchangedAssetsToPreviousAssetState.DetermineRequiredAssetEntryUpdates(group, context); Assert.AreEqual(assetEntry, ops[0].AssetEntry); Assert.AreEqual(k_ContentUpdateTestNewBundleName, ops[0].CurrentBuildPath); Assert.AreEqual(k_ContentUpdateTestCachedBundlePath, ops[0].PreviousBuildPath); File.Delete(k_ContentUpdateTestCachedBundlePath); File.Delete(k_ContentUpdateTestNewBundleName); Settings.RemoveGroup(group); } [Test] public void DetermineRequiredAssetEntryUpdates_AssetWithChangedGroup_TakesNoAction() { File.WriteAllText(k_ContentUpdateTestCachedBundlePath, "TestCachedAssetBundle"); var group = Settings.CreateGroup("ContentUpdateTests", false, false, false, null, typeof(BundledAssetGroupSchema), typeof(ContentUpdateGroupSchema)); string contentUpdateTestGroupGuid = GUID.Generate().ToString(); group.GetSchema().StaticContent = true; var assetEntry = CreateAssetEntry(m_ContentUpdateTestAssetGUID, group); group.AddAssetEntry(assetEntry); var context = GetContentUpdateContext(m_ContentUpdateTestAssetGUID, k_ContentUpdateTestCachedAssetHash, k_ContentUpdateTestNewInternalBundleName, k_ContentUpdateTestNewBundleName, k_ContentUpdateTestCachedBundlePath, contentUpdateTestGroupGuid, k_ContentUpdateTestFileName); var ops = RevertUnchangedAssetsToPreviousAssetState.DetermineRequiredAssetEntryUpdates(group, context); Assert.IsTrue(ops.Count == 0); File.Delete(k_ContentUpdateTestCachedBundlePath); Settings.RemoveGroup(group); } [Test] public void DetermineRequiredAssetEntryUpdates_AssetWithChangedHashInNonStaticGroup_TakesNoAction() { File.WriteAllText(k_ContentUpdateTestCachedBundlePath, "TestCachedAssetBundle"); var group = Settings.CreateGroup("ContentUpdateTests", false, false, false, null, typeof(BundledAssetGroupSchema), typeof(ContentUpdateGroupSchema)); string contentUpdateTestGroupGuid = GUID.Generate().ToString(); group.GetSchema().StaticContent = false; var assetEntry = CreateAssetEntry(m_ContentUpdateTestAssetGUID, group); group.AddAssetEntry(assetEntry); var context = GetContentUpdateContext(m_ContentUpdateTestAssetGUID, k_ContentUpdateTestCachedAssetHash, k_ContentUpdateTestNewInternalBundleName, k_ContentUpdateTestNewBundleName, k_ContentUpdateTestCachedBundlePath, contentUpdateTestGroupGuid, k_ContentUpdateTestFileName); var ops = RevertUnchangedAssetsToPreviousAssetState.DetermineRequiredAssetEntryUpdates(group, context); Assert.IsTrue(ops.Count == 0); File.Delete(k_ContentUpdateTestCachedBundlePath); Settings.RemoveGroup(group); } [Test] public void DetermineRequiredAssetEntryUpdates_EntriesThatTakeNoAction_StillSetBundleFileId() { File.WriteAllText(k_ContentUpdateTestCachedBundlePath, "TestCachedAssetBundle"); var group = Settings.CreateGroup("ContentUpdateTests", false, false, false, null, typeof(BundledAssetGroupSchema), typeof(ContentUpdateGroupSchema)); string contentUpdateTestGroupGuid = GUID.Generate().ToString(); group.GetSchema().StaticContent = false; var assetEntry = CreateAssetEntry(m_ContentUpdateTestAssetGUID, group); assetEntry.m_cachedAssetPath = "path"; group.AddAssetEntry(assetEntry); var context = GetContentUpdateContext(m_ContentUpdateTestAssetGUID, k_ContentUpdateTestCachedAssetHash, k_ContentUpdateTestNewInternalBundleName, k_ContentUpdateTestNewBundleName, k_ContentUpdateTestCachedBundlePath, contentUpdateTestGroupGuid, k_ContentUpdateTestFileName); var ops = RevertUnchangedAssetsToPreviousAssetState.DetermineRequiredAssetEntryUpdates(group, context); Assert.IsTrue(ops.Count == 0); Assert.AreEqual(k_ContentUpdateTestNewBundleName, assetEntry.BundleFileId); File.Delete(k_ContentUpdateTestCachedBundlePath); Settings.RemoveGroup(group); } [Test] public void DetermineRequiredAssetEntryUpdates_WithMissingBundleFileId_LogsErrorAndTakesNoAction() { File.WriteAllText(k_ContentUpdateTestCachedBundlePath, "TestCachedAssetBundle"); var group = Settings.CreateGroup("ContentUpdateTests", false, false, false, null, typeof(BundledAssetGroupSchema), typeof(ContentUpdateGroupSchema)); string contentUpdateTestGroupGuid = group.Guid; group.GetSchema().StaticContent = true; var assetEntry = CreateAssetEntry(m_ContentUpdateTestAssetGUID, group); assetEntry.m_cachedAssetPath = "path"; group.AddAssetEntry(assetEntry); var context = GetContentUpdateContext(m_ContentUpdateTestAssetGUID, k_ContentUpdateTestCachedAssetHash, k_ContentUpdateTestNewInternalBundleName, k_ContentUpdateTestNewBundleName, "", contentUpdateTestGroupGuid, k_ContentUpdateTestFileName); var ops = RevertUnchangedAssetsToPreviousAssetState.DetermineRequiredAssetEntryUpdates(group, context); LogAssert.Expect(LogType.Error, $"CachedAssetState found for {assetEntry.AssetPath} but the bundleFileId was never set on the previous build."); Assert.IsTrue(ops.Count == 0); File.Delete(k_ContentUpdateTestCachedBundlePath); Settings.RemoveGroup(group); } [Test] public void DetermineRequiredAssetEntryUpdates_WithMissingPreviousBundle_LogsWarningAndReturnsRevertOperation() { var group = Settings.CreateGroup("ContentUpdateTests", false, false, false, null, typeof(BundledAssetGroupSchema), typeof(ContentUpdateGroupSchema)); string contentUpdateTestGroupGuid = group.Guid; group.GetSchema().StaticContent = true; var assetEntry = CreateAssetEntry(m_ContentUpdateTestAssetGUID, group); assetEntry.m_cachedAssetPath = "path"; group.AddAssetEntry(assetEntry); var context = GetContentUpdateContext(m_ContentUpdateTestAssetGUID, k_ContentUpdateTestCachedAssetHash, k_ContentUpdateTestNewInternalBundleName, k_ContentUpdateTestNewBundleName, k_ContentUpdateTestCachedBundlePath, contentUpdateTestGroupGuid, k_ContentUpdateTestFileName); var ops = RevertUnchangedAssetsToPreviousAssetState.DetermineRequiredAssetEntryUpdates(group, context); LogAssert.Expect(LogType.Warning, $"CachedAssetState found for {assetEntry.AssetPath} but the previous bundle at {k_ContentUpdateTestCachedBundlePath} cannot be found. " + $"This will not affect loading the bundle in previously built players, but loading the missing bundle in Play Mode using the play mode script " + $"\"Use Existing Build (requires built groups)\" will fail. This most often occurs because you are running a content update on a build where you " + $"made changes to a group marked with \"Prevent Updates\""); Assert.IsTrue(ops.Count == 1); Settings.RemoveGroup(group); } [Test] public void DetermineRequiredAssetEntryUpdates_WithIdenticalBundleId_LogsWarningAndReturnsRevertOperation() { var group = Settings.CreateGroup("ContentUpdateTests", false, false, false, null, typeof(BundledAssetGroupSchema), typeof(ContentUpdateGroupSchema)); string contentUpdateTestGroupGuid = group.Guid; group.GetSchema().StaticContent = true; var assetEntry = CreateAssetEntry(m_ContentUpdateTestAssetGUID, group); assetEntry.m_cachedAssetPath = "path"; group.AddAssetEntry(assetEntry); var context = GetContentUpdateContext(m_ContentUpdateTestAssetGUID, k_ContentUpdateTestCachedAssetHash, k_ContentUpdateTestNewInternalBundleName, k_ContentUpdateTestNewBundleName, k_ContentUpdateTestNewBundleName, contentUpdateTestGroupGuid, k_ContentUpdateTestFileName); var ops = RevertUnchangedAssetsToPreviousAssetState.DetermineRequiredAssetEntryUpdates(group, context); LogAssert.Expect(LogType.Warning, $"CachedAssetState found for {assetEntry.AssetPath} but the previous bundle at {k_ContentUpdateTestNewBundleName} cannot be found. " + $"This will not affect loading the bundle in previously built players, but loading the missing bundle in Play Mode using the play mode script " + $"\"Use Existing Build (requires built groups)\" will fail. This most often occurs because you are running a content update on a build where you " + $"made changes to a group marked with \"Prevent Updates\""); Assert.IsTrue(ops.Count == 1); Settings.RemoveGroup(group); } [Test] public void DetermineRequiredAssetEntryUpdates_AssetWithMatchingCachedInternalBundleId_TakesNoAction() { File.WriteAllText(k_ContentUpdateTestCachedBundlePath, "TestCachedAssetBundle"); var group = Settings.CreateGroup("ContentUpdateTests", false, false, false, null, typeof(BundledAssetGroupSchema), typeof(ContentUpdateGroupSchema)); string contentUpdateTestGroupGuid = group.Guid; group.GetSchema().StaticContent = true; var assetEntry = CreateAssetEntry(m_ContentUpdateTestAssetGUID, group); group.AddAssetEntry(assetEntry); var context = GetContentUpdateContext(m_ContentUpdateTestAssetGUID, k_ContentUpdateTestCachedAssetHash, k_ContentUpdateTestNewInternalBundleName, k_ContentUpdateTestNewBundleName, k_ContentUpdateTestNewBundleName, contentUpdateTestGroupGuid, k_ContentUpdateTestFileName); var ops = RevertUnchangedAssetsToPreviousAssetState.DetermineRequiredAssetEntryUpdates(group, context); Assert.IsTrue(ops.Count == 0); File.Delete(k_ContentUpdateTestCachedBundlePath); Settings.RemoveGroup(group); } [Test] public void ApplyAssetEntryUpdates_InvalidKeyForGuidToPreviousAssetStateMap_DoesNotThrow() { File.WriteAllText(k_ContentUpdateTestCachedBundlePath, "TestCachedAssetBundle"); var group = Settings.CreateGroup("ContentUpdateTests", false, false, false, null, typeof(BundledAssetGroupSchema), typeof(ContentUpdateGroupSchema)); string contentUpdateTestGroupGuid = GUID.Generate().ToString(); group.GetSchema().StaticContent = false; var assetEntry = CreateAssetEntry(m_ContentUpdateTestAssetGUID, group); group.AddAssetEntry(assetEntry); var context = GetContentUpdateContext(m_ContentUpdateTestAssetGUID, k_ContentUpdateTestCachedAssetHash, k_ContentUpdateTestNewInternalBundleName, k_ContentUpdateTestNewBundleName, k_ContentUpdateTestCachedBundlePath, contentUpdateTestGroupGuid, k_ContentUpdateTestFileName); context.GuidToPreviousAssetStateMap[m_ContentUpdateTestAssetGUID].dependencies = new AssetState[] { new AssetState() { guid = GUID.Generate(), hash = Hash128.Parse("9823749283742") } }; var ops = new List() { new RevertUnchangedAssetsToPreviousAssetState.AssetEntryRevertOperation() { PreviousBuildPath = k_ContentUpdateTestCachedBundlePath, AssetEntry = assetEntry, BundleCatalogEntry = context.IdToCatalogDataEntryMap[m_ContentUpdateTestAssetGUID], CurrentBuildPath = k_ContentUpdateTestNewBundleName, PreviousAssetState = context.GuidToPreviousAssetStateMap[m_ContentUpdateTestAssetGUID] } }; context.ContentState.cachedBundles = new CachedBundleState[] {new CachedBundleState() {bundleFileId = "cachedBundle", data = "string"}}; Assert.DoesNotThrow(() => RevertUnchangedAssetsToPreviousAssetState.ApplyAssetEntryUpdates(ops, context)); File.Delete(k_ContentUpdateTestCachedBundlePath); Settings.RemoveGroup(group); } [Test] public void ApplyAssetEntryUpdates_DeletesCorrectBundle() { File.WriteAllText(k_ContentUpdateTestCachedBundlePath, "TestCachedAssetBundle"); File.WriteAllText(k_ContentUpdateTestNewBundleName, "TestAssetBundle"); var group = Settings.CreateGroup("ContentUpdateTests", false, false, false, null, typeof(BundledAssetGroupSchema), typeof(ContentUpdateGroupSchema)); string contentUpdateTestGroupGuid = GUID.Generate().ToString(); group.GetSchema().StaticContent = false; var assetEntry = CreateAssetEntry(m_ContentUpdateTestAssetGUID, group); group.AddAssetEntry(assetEntry); var context = GetContentUpdateContext(m_ContentUpdateTestAssetGUID, k_ContentUpdateTestCachedAssetHash, k_ContentUpdateTestNewInternalBundleName, k_ContentUpdateTestNewBundleName, k_ContentUpdateTestCachedBundlePath, contentUpdateTestGroupGuid, k_ContentUpdateTestFileName); var ops = new List() { new RevertUnchangedAssetsToPreviousAssetState.AssetEntryRevertOperation() { PreviousBuildPath = k_ContentUpdateTestCachedBundlePath, AssetEntry = assetEntry, BundleCatalogEntry = context.IdToCatalogDataEntryMap[m_ContentUpdateTestAssetGUID], CurrentBuildPath = k_ContentUpdateTestNewBundleName, PreviousAssetState = context.GuidToPreviousAssetStateMap[m_ContentUpdateTestAssetGUID] }, new RevertUnchangedAssetsToPreviousAssetState.AssetEntryRevertOperation() { PreviousBuildPath = k_ContentUpdateTestCachedBundlePath, AssetEntry = assetEntry, BundleCatalogEntry = context.IdToCatalogDataEntryMap[m_ContentUpdateTestAssetGUID], CurrentBuildPath = k_ContentUpdateTestNewBundleName, PreviousAssetState = context.GuidToPreviousAssetStateMap[m_ContentUpdateTestAssetGUID] } }; context.ContentState.cachedBundles = new CachedBundleState[] {new CachedBundleState() {bundleFileId = "cachedBundle", data = "string"}}; RevertUnchangedAssetsToPreviousAssetState.ApplyAssetEntryUpdates(ops, context); Assert.IsTrue(File.Exists(k_ContentUpdateTestCachedBundlePath)); Assert.IsFalse(File.Exists(k_ContentUpdateTestNewBundleName)); File.Delete(k_ContentUpdateTestCachedBundlePath); Settings.RemoveGroup(group); } [Test] public void ApplyAssetEntryUpdates_RegistryOnlyContainsCachedBundleEntry() { var group = Settings.CreateGroup("ContentUpdateTests", false, false, false, null, typeof(BundledAssetGroupSchema), typeof(ContentUpdateGroupSchema)); string contentUpdateTestGroupGuid = GUID.Generate().ToString(); group.GetSchema().StaticContent = false; var assetEntry = CreateAssetEntry(m_ContentUpdateTestAssetGUID, group); group.AddAssetEntry(assetEntry); var context = GetContentUpdateContext(m_ContentUpdateTestAssetGUID, k_ContentUpdateTestCachedAssetHash, k_ContentUpdateTestNewInternalBundleName, k_ContentUpdateTestNewBundleName, k_ContentUpdateTestCachedBundlePath, contentUpdateTestGroupGuid, k_ContentUpdateTestFileName); var ops = new List() { new RevertUnchangedAssetsToPreviousAssetState.AssetEntryRevertOperation() { PreviousBuildPath = k_ContentUpdateTestCachedBundlePath, AssetEntry = assetEntry, BundleCatalogEntry = context.IdToCatalogDataEntryMap[m_ContentUpdateTestAssetGUID], CurrentBuildPath = k_ContentUpdateTestNewBundleName, PreviousAssetState = context.GuidToPreviousAssetStateMap[m_ContentUpdateTestAssetGUID] }, new RevertUnchangedAssetsToPreviousAssetState.AssetEntryRevertOperation() { PreviousBuildPath = k_ContentUpdateTestCachedBundlePath, AssetEntry = assetEntry, BundleCatalogEntry = context.IdToCatalogDataEntryMap[m_ContentUpdateTestAssetGUID], CurrentBuildPath = k_ContentUpdateTestNewBundleName, PreviousAssetState = context.GuidToPreviousAssetStateMap[m_ContentUpdateTestAssetGUID] } }; context.ContentState.cachedBundles = new CachedBundleState[] {new CachedBundleState() {bundleFileId = "cachedBundle", data = "string"}}; RevertUnchangedAssetsToPreviousAssetState.ApplyAssetEntryUpdates(ops, context); var registryPaths = context.Registry.GetFilePaths(); Assert.AreEqual(1, registryPaths.Count()); Assert.AreEqual(k_ContentUpdateTestCachedBundlePath, registryPaths.ElementAt(0)); Settings.RemoveGroup(group); } [Test] public void ApplyAssetEntryUpdates_WhenAssetAndDependencyAreModifiedAndInSeparateGroups_SetCatalogEntryToCachedBundles() { GUID depAssetGuid = GUID.Generate(); string oldDepGroupCachedAssetHash = "1888888888888888888"; string newDepGroupCachedAssetHash = "1188888888888888888"; string depGroupNewInternalBundleName = "bundle2"; string depGroupNewBundleName = "fullbundlepath2"; string depGroupCachedBundlePath = "cachedBundle2"; string depGroupFileName = "testfile2"; AddressableAssetGroup group = Settings.CreateGroup("ContentUpdateTests", false, false, false, null, typeof(BundledAssetGroupSchema), typeof(ContentUpdateGroupSchema)); string contentUpdateTestGroupGuid = GUID.Generate().ToString(); AddressableAssetGroup depGroup = Settings.CreateGroup("ContentUpdateTests2", false, false, false, null, typeof(BundledAssetGroupSchema), typeof(ContentUpdateGroupSchema)); GUID depGroupGuid = GUID.Generate(); group.GetSchema().StaticContent = false; AddressableAssetEntry assetEntry = CreateAssetEntry(m_ContentUpdateTestAssetGUID, group); group.AddAssetEntry(assetEntry); depGroup.GetSchema().StaticContent = false; AddressableAssetEntry depAssetEntry = CreateAssetEntry(depAssetGuid.ToString(), depGroup); depGroup.AddAssetEntry(assetEntry); ContentUpdateScript.ContentUpdateContext context = GetContentUpdateContext(m_ContentUpdateTestAssetGUID, k_ContentUpdateTestCachedAssetHash, k_ContentUpdateTestNewInternalBundleName, k_ContentUpdateTestNewBundleName, k_ContentUpdateTestCachedBundlePath, contentUpdateTestGroupGuid, k_ContentUpdateTestFileName); AddToContentUpdateContext(context, depAssetGuid.ToString(), oldDepGroupCachedAssetHash, depGroupNewInternalBundleName, depGroupNewBundleName, depGroupCachedBundlePath, depGroupGuid.ToString(), depGroupFileName); var previousDep = new AssetState() { guid = depAssetGuid, hash = Hash128.Parse(oldDepGroupCachedAssetHash) }; var currentDep = new AssetState() { guid = GUID.Generate(), hash = Hash128.Parse(newDepGroupCachedAssetHash) }; context.GuidToPreviousAssetStateMap[m_ContentUpdateTestAssetGUID].dependencies = new AssetState[] { previousDep }; context.IdToCatalogDataEntryMap[m_ContentUpdateTestAssetGUID].Dependencies.Add(currentDep); var ops = new List() { new RevertUnchangedAssetsToPreviousAssetState.AssetEntryRevertOperation() { PreviousBuildPath = k_ContentUpdateTestCachedBundlePath, AssetEntry = assetEntry, BundleCatalogEntry = context.IdToCatalogDataEntryMap[m_ContentUpdateTestAssetGUID], CurrentBuildPath = k_ContentUpdateTestNewBundleName, PreviousAssetState = context.GuidToPreviousAssetStateMap[m_ContentUpdateTestAssetGUID] }, new RevertUnchangedAssetsToPreviousAssetState.AssetEntryRevertOperation() { PreviousBuildPath = depGroupCachedBundlePath, AssetEntry = depAssetEntry, BundleCatalogEntry = context.IdToCatalogDataEntryMap[depAssetGuid.ToString()], CurrentBuildPath = depGroupNewBundleName, PreviousAssetState = context.GuidToPreviousAssetStateMap[depAssetGuid.ToString()] } }; var locations = new List(); context.ContentState.cachedBundles = new CachedBundleState[] { new CachedBundleState() {bundleFileId = "cachedBundle", data = "string"}, new CachedBundleState() {bundleFileId = "cachedBundle2", data = "string"} }; RevertUnchangedAssetsToPreviousAssetState.ApplyAssetEntryUpdates(ops, context); Assert.AreEqual(k_ContentUpdateTestCachedBundlePath, context.IdToCatalogDataEntryMap[m_ContentUpdateTestAssetGUID].InternalId); Assert.AreEqual(depGroupCachedBundlePath, context.IdToCatalogDataEntryMap[depAssetGuid.ToString()].InternalId); Settings.RemoveGroup(group); Settings.RemoveGroup(depGroup); } [Test] public void RevertBundleDataCorrectlyReplacesToCacheLoadingData() { string bundleName = "cachedBundleName_containKey"; string assetBundleProvider = "UnityEngine.ResourceManagement.ResourceProviders.AssetBundleProvider"; ContentUpdateScript.ContentUpdateContext updateContext = new ContentUpdateScript.ContentUpdateContext(); updateContext.ContentState = new AddressablesContentState(); AssetBundleRequestOptions cachedRequestOptions = new AssetBundleRequestOptions() {Crc = 123, Hash = "abc", BundleName = bundleName, BundleSize = 10}; updateContext.ContentState.cachedBundles = new CachedBundleState[] { new CachedBundleState() {bundleFileId = "cachedInternalId", data = cachedRequestOptions} }; AddressableAssetsBuildContext aaContext = new AddressableAssetsBuildContext(); aaContext.locations = new List(2); List keys = new List(); keys.Add("stringLoadKey"); AssetBundleRequestOptions newLocationData1 = new AssetBundleRequestOptions() {Crc = 456, Hash = "def", BundleName = bundleName, BundleSize = 20}; aaContext.locations.Add(new ContentCatalogDataEntry(typeof(AssetBundleResource), "newInternalID", assetBundleProvider, keys, null, newLocationData1)); AssetBundleRequestOptions newLocationData2 = new AssetBundleRequestOptions() {Crc = 456, Hash = "def", BundleName = "nonCachedBundleName", BundleSize = 20}; aaContext.locations.Add(new ContentCatalogDataEntry(typeof(AssetBundleResource), "newInternalID", assetBundleProvider, keys, null, newLocationData2)); bool reverted = RevertUnchangedAssetsToPreviousAssetState.RevertBundleByNameContains("_containKey", updateContext, aaContext); Assert.IsTrue(reverted, "Failed to revert the bundle containing _containsKey"); // first entry is reverted AssetBundleRequestOptions catalogRequestOptions = aaContext.locations[0].Data as AssetBundleRequestOptions; Assert.AreEqual(cachedRequestOptions.Crc, catalogRequestOptions.Crc, "Reverted Catalog CRC expected to be the same as the cached value"); Assert.AreEqual(cachedRequestOptions.Hash, catalogRequestOptions.Hash, "Reverted Catalog Hash expected to be the same as the cached value"); Assert.AreEqual(cachedRequestOptions.BundleName, catalogRequestOptions.BundleName, "Reverted Catalog BundleName expected to be the same as the cached value"); Assert.AreEqual(cachedRequestOptions.BundleSize, catalogRequestOptions.BundleSize, "Reverted Catalog BundleSize expected to be the same as the cached value"); // second entry is not reverted catalogRequestOptions = aaContext.locations[1].Data as AssetBundleRequestOptions; Assert.AreNotEqual(cachedRequestOptions.Crc, catalogRequestOptions.Crc, "Noncached Catalog CRC expected to be different as the cached value"); Assert.AreNotEqual(cachedRequestOptions.Hash, catalogRequestOptions.Hash, "Noncached Catalog Hash expected to be different as the cached value"); Assert.AreNotEqual(cachedRequestOptions.BundleName, catalogRequestOptions.BundleName, "Noncached Catalog BundleName expected to be different as the cached value"); Assert.AreNotEqual(cachedRequestOptions.BundleSize, catalogRequestOptions.BundleSize, "Noncached Catalog BundleSize expected to be different as the cached value"); } // not found in cache // not found in catalog // bundeOptions missing [Test] public void RevertBundleFails_WhenBundleMissingFromCacheButInCatalog() { string assetBundleProvider = "UnityEngine.ResourceManagement.ResourceProviders.AssetBundleProvider"; ContentUpdateScript.ContentUpdateContext updateContext = new ContentUpdateScript.ContentUpdateContext(); updateContext.ContentState = new AddressablesContentState(); AssetBundleRequestOptions cachedRequestOptions = new AssetBundleRequestOptions() {Crc = 123, Hash = "abc", BundleName = "cachedBundleName", BundleSize = 10}; updateContext.ContentState.cachedBundles = new CachedBundleState[] { new CachedBundleState() {bundleFileId = "cachedInternalId", data = cachedRequestOptions} }; AddressableAssetsBuildContext aaContext = new AddressableAssetsBuildContext(); aaContext.locations = new List(1); List keys = new List(); keys.Add("stringLoadKey"); AssetBundleRequestOptions locData = new AssetBundleRequestOptions() {Crc = 456, Hash = "def", BundleName = "catalogBundleName", BundleSize = 20}; string internalId = "catalogInternalId"; aaContext.locations.Add(new ContentCatalogDataEntry(typeof(AssetBundleResource), internalId, assetBundleProvider, keys, null, locData)); LogAssert.Expect(LogType.Error, $"Matching cached update state for {internalId} failed. Content not found in original build."); bool reverted = RevertUnchangedAssetsToPreviousAssetState.RevertBundleByNameContains("catalogBundleName", updateContext, aaContext); Assert.IsFalse(reverted, "Expected to Fail finding \"_containsKey\" where cached content does not have an entry with that bundle Name"); } [Test] public void RevertBundleSucceeds_WhenBundleMissingFromCatalogButInCache() { string assetBundleProvider = "UnityEngine.ResourceManagement.ResourceProviders.AssetBundleProvider"; ContentUpdateScript.ContentUpdateContext updateContext = new ContentUpdateScript.ContentUpdateContext(); updateContext.ContentState = new AddressablesContentState(); AssetBundleRequestOptions cachedRequestOptions = new AssetBundleRequestOptions() {Crc = 123, Hash = "abc", BundleName = "cachedBundleName", BundleSize = 10}; updateContext.ContentState.cachedBundles = new CachedBundleState[] { new CachedBundleState() {bundleFileId = "cachedInternalId", data = cachedRequestOptions} }; AddressableAssetsBuildContext aaContext = new AddressableAssetsBuildContext(); aaContext.locations = new List(1); List keys = new List(); keys.Add("stringLoadKey"); AssetBundleRequestOptions locData = new AssetBundleRequestOptions() {Crc = 456, Hash = "def", BundleName = "catalogBundleName", BundleSize = 20}; aaContext.locations.Add(new ContentCatalogDataEntry(typeof(AssetBundleResource), "newInternalID", assetBundleProvider, keys, null, locData)); bool reverted = RevertUnchangedAssetsToPreviousAssetState.RevertBundleByNameContains("cachedBundleName", updateContext, aaContext); Assert.IsTrue(reverted, "Expected to succeed where cache exists but not included in current build to be reverted"); } [Test] public void RevertBundleSucceeds_WhenBundleMissingFromCacheAndCatalog() { string assetBundleProvider = "UnityEngine.ResourceManagement.ResourceProviders.AssetBundleProvider"; ContentUpdateScript.ContentUpdateContext updateContext = new ContentUpdateScript.ContentUpdateContext(); updateContext.ContentState = new AddressablesContentState(); updateContext.ContentState.cachedBundles = new CachedBundleState[] { new CachedBundleState() {bundleFileId = "cachedInternalId", data = null} }; AddressableAssetsBuildContext aaContext = new AddressableAssetsBuildContext(); aaContext.locations = new List(1); List keys = new List(); keys.Add("stringLoadKey"); aaContext.locations.Add(new ContentCatalogDataEntry(typeof(AssetBundleResource), "newInternalID", assetBundleProvider, keys, null, null)); bool reverted = RevertUnchangedAssetsToPreviousAssetState.RevertBundleByNameContains("catalogBundleName", updateContext, aaContext); Assert.IsTrue(reverted, "Expected to succeed where bundle to revert was not in either cache or current builds"); } [Test] [TestCase("{Addressables.RuntimePath}/TargetPlatform/prefabA.bundle", "{Addressables.RuntimePath}/TargetPlatform", "Library/aa/TargetPlatform")] [TestCase("{Addressables.RuntimePath}\\TargetPlatform\\prefabA.bundle", "{Addressables.RuntimePath}/TargetPlatform", "Library/aa/TargetPlatform")] [TestCase("http://localhost/TargetPlatform/prefabA.bundle", "http://localhost/TargetPlatform", "ServerData/TargetPlatform")] public void BundleIdToBuildPath_ReturnsBundleBuildPath(string bundleId, string rootLoadPath, string rootBuildPath) { string buildPath = RevertUnchangedAssetsToPreviousAssetState.BundleIdToBuildPath(bundleId, rootLoadPath, rootBuildPath); Assert.IsTrue(buildPath.StartsWith(rootBuildPath)); } [Test] public void GatherModifiedEntries_IncludesDependants() { // Create assets GameObject mainObject = new GameObject("mainObject"); Material mat = new Material(Shader.Find("Transparent/Diffuse")); mainObject.AddComponent().material = mat; string mainAssetPath = GetAssetPath("mainObject.prefab"); string refAssetPath = GetAssetPath("refObject.prefab"); string materialAssetPath = GetAssetPath("testMaterial.mat"); AssetDatabase.CreateAsset(mat, materialAssetPath); var prefab = PrefabUtility.SaveAsPrefabAsset(mainObject, mainAssetPath); GameObject refObject = new GameObject("refObject"); refObject.AddComponent().m_ObjectReference = prefab; PrefabUtility.SaveAsPrefabAsset(refObject, refAssetPath); AssetDatabase.SaveAssets(); AddressableAssetGroup mainAssetGroup = null; try { // Create addressables mainAssetGroup = Settings.CreateGroup("TestGroup", false, false, false, null); var schema = mainAssetGroup.AddSchema(); schema.BuildPath.SetVariableByName(Settings, AddressableAssetSettings.kLocalBuildPath); schema.LoadPath.SetVariableByName(Settings, AddressableAssetSettings.kLocalLoadPath); schema.BundleMode = BundledAssetGroupSchema.BundlePackingMode.PackTogether; mainAssetGroup.AddSchema().StaticContent = true; AddressableAssetEntry mainEntry = Settings.CreateOrMoveEntry(AssetDatabase.AssetPathToGUID(mainAssetPath), mainAssetGroup); AddressableAssetEntry refEntry = Settings.CreateOrMoveEntry(AssetDatabase.AssetPathToGUID(refAssetPath), mainAssetGroup); // Build var context = new AddressablesDataBuilderInput(Settings); Settings.ActivePlayerDataBuilder.BuildData(context); // Modify assets var ma = AssetDatabase.LoadAssetAtPath(materialAssetPath); ma.color = new Color(Random.value, Random.value, Random.value); EditorUtility.SetDirty(ma); AssetDatabase.SaveAssets(); // Test var tempPath = Path.GetDirectoryName(Application.dataPath) + "/Library/com.unity.addressables/" + PlatformMappingService.GetPlatformPathSubFolder() + "/addressables_content_state.bin"; var modifiedEntries = ContentUpdateScript.GatherModifiedEntries(Settings, tempPath); Assert.AreEqual(2, modifiedEntries.Count); Assert.IsTrue(modifiedEntries.Contains(mainEntry), "Modified Entries does not include the main prefab"); Assert.IsTrue(modifiedEntries.Contains(refEntry), "Modified Entries does not include the prefab that is dependant on Prefab that has changed material"); } finally { // Cleanup GameObject.DestroyImmediate(mainObject); GameObject.DestroyImmediate(refObject); Settings.RemoveGroup(mainAssetGroup); AssetDatabase.DeleteAsset(mainAssetPath); AssetDatabase.DeleteAsset(refAssetPath); AssetDatabase.DeleteAsset(materialAssetPath); } } } }