diff --git a/CHANGELOG.md b/CHANGELOG.md index 755ab34b..43a13110 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,12 @@ All notable changes to this package will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +## [2.1.0] - 2024-03-19 +- Fix "Unable to verify target bucket for Remote Catalog: Not Found. Object could not be found" error +- Fixed caching to prevent unnecessary refreshing of bucket data. +- Sort collections on serialization to prevent unecessary merge conflicts +- Add warnings and documentation to make it clear you need to release the handle from LoadDependenciesAsync + ## [2.0.8] - 2024-01-19 - Documents the behavior of using WaitForCompletion while a bundle is being unloaded. - Prevent a KeyNotFoundException from being logged to the console. diff --git a/Editor/Build/AddressableAnalytics.cs b/Editor/Build/AddressableAnalytics.cs index aa6f453c..9215e586 100644 --- a/Editor/Build/AddressableAnalytics.cs +++ b/Editor/Build/AddressableAnalytics.cs @@ -20,7 +20,10 @@ internal static class AddressableAnalytics private const string UsageEvent = "addressablesUsageEvent"; private const string BuildEvent = "addressablesBuildEvent"; + +#if UNITY_2023_1_OR_NEWER private static PackageManager.PackageInfo packageInfo = PackageManager.PackageInfo.FindForPackageName("com.unity.addressables"); +#endif private const string packageName = "com.unity.addressables"; #if UNITY_2023_3_OR_NEWER diff --git a/Editor/Build/CcdBuildEvents.cs b/Editor/Build/CcdBuildEvents.cs index f0259847..4e1924f7 100644 --- a/Editor/Build/CcdBuildEvents.cs +++ b/Editor/Build/CcdBuildEvents.cs @@ -432,6 +432,12 @@ internal async Task verifyTargetBucket(AddressableAssetSettings settings, return true; } + if (settings.m_CcdManagedData.IsConfigured()) + { + // this has been configured by a previous run + return true; + } + // existing automatic bucket loaded from cache var bucketIdVariable = dataSource @@ -450,11 +456,15 @@ internal async Task verifyTargetBucket(AddressableAssetSettings settings, } // otherwise try to create - CcdManagement.SetEnvironmentId(settings.m_CcdManagedData.EnvironmentId); - await CreateManagedBucket(EditorUserBuildSettings.activeBuildTarget.ToString()); - // we created a bucket so refresh our data sources so we ensure they're up to date - await RefreshDataSources(); - + var environmentId = ProfileDataSourceSettings.GetSettings().GetEnvironmentId(settings.profileSettings, settings.activeProfileId); + CcdManagement.SetEnvironmentId(environmentId); // this should be getting the value from the active profile + var ccdBucket = await CreateManagedBucket(EditorUserBuildSettings.activeBuildTarget.ToString()); + if (ccdBucket == null) { + // the bucket already exists, we shouldn't be here if refresh was called + ccdBucket = await GetExistingManagedBucket(); + } + var environmentName = ProfileDataSourceSettings.GetSettings().GetEnvironmentName(settings.profileSettings, settings.activeProfileId); + ProfileDataSourceSettings.AddGroupTypeForRemoteBucket(CloudProjectSettings.projectId, environmentId, environmentName, ccdBucket, new List()); PopulateCcdManagedData(settings, settings.activeProfileId); // I should put this value into the data source list @@ -735,12 +745,16 @@ internal ProfileGroupType GetDataSource(AddressableAssetSettings settings, strin return groupType; } - var groupTypes = ProfileDataSourceSettings.GetSettings().GetGroupTypesByPrefix(string.Join( + var environmentId = ProfileDataSourceSettings.GetSettings().GetEnvironmentId(settings.profileSettings, settings.activeProfileId); + // if we haven't setup an automatic group since refresh we do it here + IEnumerable groupTypes = ProfileDataSourceSettings.GetSettings().GetGroupTypesByPrefix(string.Join( ProfileGroupType.k_PrefixSeparator.ToString(), "CCD", CloudProjectSettings.projectId, ProfileDataSourceSettings.GetSettings().GetEnvironmentId(settings.profileSettings, settings.activeProfileId))); + // if we have setup an automatic group we load it here + groupTypes = groupTypes.Concat(ProfileDataSourceSettings.GetSettings().GetGroupTypesByPrefix(AddressableAssetSettings.CcdManagerGroupTypePrefix)); var automaticGroupType = groupTypes.FirstOrDefault(gt => - gt.GetVariableBySuffix($"{nameof(CcdBucket)}{nameof(CcdBucket.Name)}").Value == - EditorUserBuildSettings.activeBuildTarget.ToString()); + gt.GetVariableBySuffix($"{nameof(CcdBucket)}{nameof(CcdBucket.Name)}")?.Value == EditorUserBuildSettings.activeBuildTarget.ToString() + && gt.GetVariableBySuffix($"{nameof(ProfileDataSourceSettings.Environment)}{nameof(ProfileDataSourceSettings.Environment.id)}")?.Value == environmentId); if (automaticGroupType == null) { // the bucket does not yet exist @@ -826,9 +840,7 @@ async Task CreateManagedBucket(string bucketName) { if (e.ErrorCode == CcdManagementErrorCodes.AlreadyExists) { - var buckets = await ProfileDataSourceSettings.GetAllBucketsAsync(); - ccdBucket = buckets.First(bucket => - bucket.Value.Name == EditorUserBuildSettings.activeBuildTarget.ToString()).Value; + return null; } else { @@ -838,6 +850,14 @@ async Task CreateManagedBucket(string bucketName) return ccdBucket; } + async Task GetExistingManagedBucket() + { + var buckets = await ProfileDataSourceSettings.GetAllBucketsAsync(); + var ccdBucket = buckets.First(bucket => + bucket.Value.Name == EditorUserBuildSettings.activeBuildTarget.ToString()).Value; + return ccdBucket; + } + async Task GetEntryByPath(ICcdManagementServiceSdk api, Guid bucketId, string path) { CcdEntry ccdEntry = null; diff --git a/Editor/Settings/AddressableAssetEntry.cs b/Editor/Settings/AddressableAssetEntry.cs index c0ad222a..fc2a9d62 100644 --- a/Editor/Settings/AddressableAssetEntry.cs +++ b/Editor/Settings/AddressableAssetEntry.cs @@ -642,17 +642,19 @@ string GetRelativePath(string file, string path) } /// - /// Implementation of ISerializationCallbackReceiver. Converts data to serializable form before serialization. + /// Implementation of ISerializationCallbackReceiver. Converts data to serializable form before serialization, + /// and sorts collections for deterministic ordering. /// public void OnBeforeSerialize() { m_SerializedLabels = new List(); foreach (var t in m_Labels) m_SerializedLabels.Add(t); + m_SerializedLabels.Sort(string.CompareOrdinal); } /// - /// Implementation of ISerializationCallbackReceiver. Converts data from serializable form after deserialization. + /// Implementation of ISerializationCallbackReceiver. Converts data from serializable form after deserialization. /// public void OnAfterDeserialize() { diff --git a/Editor/Settings/AddressableAssetGroup.cs b/Editor/Settings/AddressableAssetGroup.cs index 007dcc14..cd2a265a 100644 --- a/Editor/Settings/AddressableAssetGroup.cs +++ b/Editor/Settings/AddressableAssetGroup.cs @@ -33,7 +33,7 @@ public class AddressableAssetGroup : ScriptableObject, IComparer m_SerializeEntries = new List(); + internal List m_SerializeEntries = new List(); [FormerlySerializedAs("m_readOnly")] [SerializeField] @@ -395,7 +395,8 @@ internal Hash128 currentHash } /// - /// Converts data to serializable format. + /// Implementation of ISerializationCallbackReceiver. Converts data to serializable form before serialization, + /// and sorts collections for deterministic ordering. /// public void OnBeforeSerialize() { @@ -405,10 +406,11 @@ public void OnBeforeSerialize() foreach (var e in entries) m_SerializeEntries.Add(e); } + m_SerializeEntries.Sort((a, b) => string.CompareOrdinal(a.guid, b.guid)); } /// - /// Converts data from serializable format. + /// Implementation of ISerializationCallbackReceiver. Converts data from serializable format. /// public void OnAfterDeserialize() { @@ -478,14 +480,14 @@ internal void DedupeEnteries() foreach (AddressableAssetEntry e in m_EntryMap.Values) { AddressableAssetEntry lookedUpEntry = m_Settings.FindAssetEntry(e.guid); - if (lookedUpEntry.parentGroup != this) + if (lookedUpEntry?.parentGroup != this) { Debug.LogWarning(e.address + " is already a member of group " - + lookedUpEntry.parentGroup + + lookedUpEntry?.parentGroup + " but group " + m_GroupName - + " contained a reference to it. Removing referece."); + + " contained a reference to it. Removing reference."); removeEntries.Add(e); } } diff --git a/Editor/Settings/AddressableAssetGroupSchema.cs b/Editor/Settings/AddressableAssetGroupSchema.cs index d93b8bda..1d747d2f 100644 --- a/Editor/Settings/AddressableAssetGroupSchema.cs +++ b/Editor/Settings/AddressableAssetGroupSchema.cs @@ -167,5 +167,16 @@ protected void ShowMixedValue(SerializedProperty property, List [Serializable] - public class AddressableAssetGroupSchemaSet + public class AddressableAssetGroupSchemaSet : ISerializationCallbackReceiver { [FormerlySerializedAs("m_schemas")] [SerializeField] @@ -237,5 +237,20 @@ internal bool RenameSchemaAssets(Func pathFunc) return result; } + + /// + /// Implementation of ISerializationCallbackReceiver. Sorts collections for deterministic ordering. + /// + public void OnBeforeSerialize() + { + m_Schemas.Sort(AddressableAssetGroupSchema.Compare); + } + + /// + /// Implementation of ISerializationCallbackReceiver. Does nothing. + /// + public void OnAfterDeserialize() + { + } } } diff --git a/Editor/Settings/AddressableAssetGroupTemplate.cs b/Editor/Settings/AddressableAssetGroupTemplate.cs index 2a3de9a4..847bdc02 100644 --- a/Editor/Settings/AddressableAssetGroupTemplate.cs +++ b/Editor/Settings/AddressableAssetGroupTemplate.cs @@ -10,7 +10,7 @@ namespace UnityEditor.AddressableAssets.Settings /// Used to create template groups to make it easier for the user to create new groups. /// [CreateAssetMenu(fileName = "AddressableAssetGroupTemplate.asset", menuName = "Addressables/Group Templates/Blank Group Template")] - public class AddressableAssetGroupTemplate : ScriptableObject, IGroupTemplate + public class AddressableAssetGroupTemplate : ScriptableObject, IGroupTemplate, ISerializationCallbackReceiver { [SerializeField] private List m_SchemaObjects = new List(); @@ -272,5 +272,20 @@ public int FindSchema(Type type) return -1; } + + /// + /// Implementation of ISerializationCallbackReceiver. Sorts collections for deterministic ordering. + /// + public void OnBeforeSerialize() + { + m_SchemaObjects.Sort(AddressableAssetGroupSchema.Compare); + } + + /// + /// Implementation of ISerializationCallbackReceiver. Does nothing. + /// + public void OnAfterDeserialize() + { + } } } diff --git a/Editor/Settings/AddressableAssetProfileSettings.cs b/Editor/Settings/AddressableAssetProfileSettings.cs index b9c93c04..3547ee87 100644 --- a/Editor/Settings/AddressableAssetProfileSettings.cs +++ b/Editor/Settings/AddressableAssetProfileSettings.cs @@ -15,7 +15,7 @@ namespace UnityEditor.AddressableAssets.Settings /// Contains user defined variables to control build parameters. /// [Serializable] - public class AddressableAssetProfileSettings + public class AddressableAssetProfileSettings : ISerializationCallbackReceiver { internal delegate string ProfileStringEvaluationDelegate(string key); @@ -34,7 +34,7 @@ internal void UnregisterProfileStringEvaluationFunc(ProfileStringEvaluationDeleg } [Serializable] - internal class BuildProfile + internal class BuildProfile : ISerializationCallbackReceiver { [Serializable] internal class ProfileEntry @@ -197,6 +197,15 @@ internal void SetValueById(string variableId, string val) values[i].value = val; m_CurrentHash = default; } + + public void OnBeforeSerialize() + { + m_Values.Sort((a, b) => string.CompareOrdinal(a.id, b.id)); + } + + public void OnAfterDeserialize() + { + } } internal void OnAfterDeserialize(AddressableAssetSettings settings) @@ -228,7 +237,7 @@ public class ProfileIdData { [FormerlySerializedAs("m_id")] [SerializeField] - string m_Id; + internal string m_Id; /// /// The unique ID set to identify a specific profile. @@ -307,7 +316,7 @@ internal ProfileIdData() /// /// The unique ID for this ProfileIdData /// The name of the ProfileIdData. ProfileIdData names should be unique in a given AddressableAssetProfileSettings. - /// False by default, this informs the BuildProifleSettingsEditor on if it should evaluate the ProfileIdData directly (true) + /// False by default, this informs the BuildProifleSettingsEditor on if it should evaluate the ProfileIdData directly (true) /// or get the value by Id first before evaluation (false). public ProfileIdData(string entryId, string entryName, bool inline) { @@ -891,5 +900,19 @@ internal void CreateDuplicateVariableWithNewName(AddressableAssetSettings addres SetDirty(AddressableAssetSettings.ModificationEvent.ProfileModified, profile, true); } } + /// + /// Implementation of ISerializationCallbackReceiver. Sorts collections for deterministic ordering. + /// + public void OnBeforeSerialize() + { + m_ProfileEntryNames.Sort((a, b) => string.CompareOrdinal(a.Id, b.Id)); + } + + /// + /// Implementation of ISerializationCallbackReceiver. Does nothing. + /// + public void OnAfterDeserialize() + { + } } } diff --git a/Editor/Settings/AddressableAssetSettings.cs b/Editor/Settings/AddressableAssetSettings.cs index 95fd5aed..6642618f 100644 --- a/Editor/Settings/AddressableAssetSettings.cs +++ b/Editor/Settings/AddressableAssetSettings.cs @@ -930,13 +930,13 @@ public int SharedBundleSettingsCustomGroupIndex } /// - /// Retrieves the group whose settings are used for shared bundles (Built In and MonoScript bundles). + /// Retrieves the group whose settings are used for shared bundles (Built In and MonoScript bundles). /// /// The group whose settings are used. public AddressableAssetGroup GetSharedBundleGroup() { if (SharedBundleSettings == SharedBundleSettings.CustomGroup && - m_SharedBundleSettingsCustomGroupIndex >= 0 && + m_SharedBundleSettingsCustomGroupIndex >= 0 && m_SharedBundleSettingsCustomGroupIndex < groups.Count) return groups[m_SharedBundleSettingsCustomGroupIndex]; return DefaultGroup; @@ -2309,6 +2309,11 @@ public AddressableAssetEntry FindAssetEntry(string guid, bool includeImplicit) return null; } + internal void ClearFindAssetEntryCache() + { + m_FindAssetEntryCache = null; + } + internal bool IsAssetPathInAddressableDirectory(string assetPath, out string assetName) { if (!string.IsNullOrEmpty(assetPath)) @@ -3253,14 +3258,17 @@ public static bool InvokeAssetGroupCommand(string cmdId, IEnumerable - /// Impementation of ISerializationCallbackReceiver, does nothing. + /// Impementation of ISerializationCallbackReceiver. Sorts collections for deterministic ordering /// public void OnBeforeSerialize() { + // m_InitializationObjects, m_DataBuilders, and m_GroupTemplateObjects are serialized by array index + profileSettings.profiles.Sort((a, b) => string.CompareOrdinal(a.id, b.id)); + m_GroupAssets.Sort((a, b) => string.CompareOrdinal(a.Guid, b.Guid)); } /// - /// Impementation of ISerializationCallbackReceiver, used to set callbacks for ProfileValueReference changes. + /// Impementation of ISerializationCallbackReceiver. Used to set callbacks for ProfileValueReference changes. /// public void OnAfterDeserialize() { diff --git a/Editor/Settings/GroupSchemas/BundledAssetGroupSchema.cs b/Editor/Settings/GroupSchemas/BundledAssetGroupSchema.cs index d958a611..b88abcd8 100644 --- a/Editor/Settings/GroupSchemas/BundledAssetGroupSchema.cs +++ b/Editor/Settings/GroupSchemas/BundledAssetGroupSchema.cs @@ -743,14 +743,14 @@ internal string GetAssetLoadPath(string assetPath, HashSet otherLoadPath } /// - /// Impementation of ISerializationCallbackReceiver, does nothing. + /// Implementation of ISerializationCallbackReceiver. Does nothing. /// public void OnBeforeSerialize() { } /// - /// Impementation of ISerializationCallbackReceiver, used to set callbacks for ProfileValueReference changes. + /// Impementation of ISerializationCallbackReceiver. Used to set callbacks for ProfileValueReference changes. /// public void OnAfterDeserialize() { diff --git a/Editor/Settings/ProfileDataSourceSettings.cs b/Editor/Settings/ProfileDataSourceSettings.cs index ced285ad..11a07f08 100644 --- a/Editor/Settings/ProfileDataSourceSettings.cs +++ b/Editor/Settings/ProfileDataSourceSettings.cs @@ -246,7 +246,7 @@ public static async Task> UpdateCCDDataSourcesAsync(strin Addressables.Log("Syncing CCD Environments, Buckets, and Badges."); } - var profileGroupTypes = new List(); + settings.profileGroupTypes.Clear(); var environments = await GetEnvironments(); @@ -255,7 +255,7 @@ public static async Task> UpdateCCDDataSourcesAsync(strin EditorUtility.DisplayProgressBar("Syncing Profile Data Sources", "Fetching Environments", 0); Addressables.Log($"Successfully fetched {environments.Count} environments."); } - profileGroupTypes.AddRange(CreateDefaultGroupTypes()); + settings.profileGroupTypes.AddRange(CreateDefaultGroupTypes()); try { @@ -275,35 +275,12 @@ public static async Task> UpdateCCDDataSourcesAsync(strin } var badges = await GetAllBadgesAsync(bucket.Id.ToString()); - if (badges.Count == 0) badges.Add(new CcdBadge(name: "latest")); - foreach (var badge in badges) - { - var groupType = - new ProfileGroupType($"CCD{ProfileGroupType.k_PrefixSeparator}{projectId}{ProfileGroupType.k_PrefixSeparator}{environment.id}{ProfileGroupType.k_PrefixSeparator}{bucket.Id}{ProfileGroupType.k_PrefixSeparator}{badge.Name}"); - groupType.AddVariable(new ProfileGroupType.GroupTypeVariable($"{nameof(CcdBucket)}{nameof(CcdBucket.Name)}", bucket.Name)); - groupType.AddVariable(new ProfileGroupType.GroupTypeVariable($"{nameof(CcdBucket)}{nameof(CcdBucket.Id)}", bucket.Id.ToString())); - groupType.AddVariable(new ProfileGroupType.GroupTypeVariable($"{nameof(CcdBadge)}{nameof(CcdBadge.Name)}", badge.Name)); - groupType.AddVariable(new ProfileGroupType.GroupTypeVariable(nameof(CcdBucket.Attributes.PromoteOnly), bucket.Attributes.PromoteOnly.ToString())); - - //Adding environment stub here - groupType.AddVariable(new ProfileGroupType.GroupTypeVariable($"{nameof(Environment)}{nameof(Environment.name)}", environment.name)); - groupType.AddVariable(new ProfileGroupType.GroupTypeVariable($"{nameof(Environment)}{nameof(Environment.id)}", environment.id)); - - string buildPath = $"{AddressableAssetSettings.kCCDBuildDataPath}/{environment.id}/{bucket.Id}/{badge.Name}"; - groupType.AddVariable(new ProfileGroupType.GroupTypeVariable(AddressableAssetSettings.kBuildPath, buildPath)); - - string loadPath = -$"https://{projectId}{m_CcdClientBasePath}/client_api/v1/environments/{environment.name}/buckets/{bucket.Id}/release_by_badge/{badge.Name}/entry_by_path/content/?path="; - groupType.AddVariable(new ProfileGroupType.GroupTypeVariable(AddressableAssetSettings.kLoadPath, loadPath)); - - profileGroupTypes.Add(groupType); - } + AddGroupTypeForRemoteBucket(projectId, environment.id, environment.name, bucket, badges); bucketProgress++; } envProgress++; } - settings.profileGroupTypes = profileGroupTypes; settings.environments = environments.ToList(); if (showInfoLog) Addressables.Log("Successfully synced CCD Buckets and Badges."); EditorUtility.SetDirty(settings); @@ -321,6 +298,35 @@ public static async Task> UpdateCCDDataSourcesAsync(strin return settings.profileGroupTypes; } + internal static void AddGroupTypeForRemoteBucket(string projectId, string environmentId, string environmentName, CcdBucket bucket, List badges) + { + var settings = GetSettings(); + if (badges.Count == 0) badges.Add(new CcdBadge(name: "latest")); + foreach (var badge in badges) + { + var groupType = + new ProfileGroupType( + $"CCD{ProfileGroupType.k_PrefixSeparator}{projectId}{ProfileGroupType.k_PrefixSeparator}{environmentId}{ProfileGroupType.k_PrefixSeparator}{bucket.Id}{ProfileGroupType.k_PrefixSeparator}{badge.Name}"); + groupType.AddVariable(new ProfileGroupType.GroupTypeVariable($"{nameof(CcdBucket)}{nameof(CcdBucket.Name)}", bucket.Name)); + groupType.AddVariable(new ProfileGroupType.GroupTypeVariable($"{nameof(CcdBucket)}{nameof(CcdBucket.Id)}", bucket.Id.ToString())); + groupType.AddVariable(new ProfileGroupType.GroupTypeVariable($"{nameof(CcdBadge)}{nameof(CcdBadge.Name)}", badge.Name)); + groupType.AddVariable(new ProfileGroupType.GroupTypeVariable(nameof(CcdBucket.Attributes.PromoteOnly), bucket.Attributes.PromoteOnly.ToString())); + + //Adding environment stub here + groupType.AddVariable(new ProfileGroupType.GroupTypeVariable($"{nameof(Environment)}{nameof(Environment.name)}", environmentName)); + groupType.AddVariable(new ProfileGroupType.GroupTypeVariable($"{nameof(Environment)}{nameof(Environment.id)}", environmentId)); + + string buildPath = $"{AddressableAssetSettings.kCCDBuildDataPath}/{environmentId}/{bucket.Id}/{badge.Name}"; + groupType.AddVariable(new ProfileGroupType.GroupTypeVariable(AddressableAssetSettings.kBuildPath, buildPath)); + + string loadPath = + $"https://{projectId}{m_CcdClientBasePath}/client_api/v1/environments/{environmentName}/buckets/{bucket.Id}/release_by_badge/{badge.Name}/entry_by_path/content/?path="; + groupType.AddVariable(new ProfileGroupType.GroupTypeVariable(AddressableAssetSettings.kLoadPath, loadPath)); + + settings.profileGroupTypes.Add(groupType); + } + } + internal static async Task> GetAllBucketsAsync() { int page = 1; @@ -470,6 +476,12 @@ internal string GetEnvironmentId(AddressableAssetProfileSettings profileSettings #endif void ISerializationCallbackReceiver.OnBeforeSerialize() { + profileGroupTypes.Sort((x, y) => string.CompareOrdinal(x.GroupTypePrefix, y.GroupTypePrefix)); + foreach (var profileGroupType in profileGroupTypes) + { + profileGroupType.Variables.Sort((x, y) => string.CompareOrdinal(x.Suffix,y.Suffix)); + } + environments.Sort((x, y) => string.CompareOrdinal(x.id, y.id)); } void ISerializationCallbackReceiver.OnAfterDeserialize() diff --git a/Runtime/Addressables.cs b/Runtime/Addressables.cs index 91ca0196..d779f817 100644 --- a/Runtime/Addressables.cs +++ b/Runtime/Addressables.cs @@ -1531,6 +1531,8 @@ public static AsyncOperationHandle GetDownloadSizeAsync(IEnumerable keys) /// You can use the returned by this function to monitor and /// provide feedback on the download progress. /// + /// The returned handle must be released before Assets can be be loaded from the downloaded bundles. + /// /// See [Preloading dependencies](xref:addressables-api-download-dependencies-async) for more details. /// /// The key of the assets to load dependencies for. @@ -1551,6 +1553,8 @@ public static AsyncOperationHandle DownloadDependenciesAsync(object key, bool au /// You can use the returned by this function to monitor and /// provide feedback on the download progress. /// + /// The returned handle must be released before Assets can be be loaded from the downloaded bundles. + /// /// See [Preloading dependencies](xref:addressables-api-download-dependencies-async) for more details. /// /// The locations of the assets. @@ -1574,6 +1578,8 @@ public static AsyncOperationHandle DownloadDependenciesAsync(IList returned by this function to monitor and /// provide feedback on the download progress. /// + /// The returned handle must be released before Assets can be be loaded from the downloaded bundles. + /// /// See [Preloading dependencies](xref:addressables-api-download-dependencies-async) for more details. /// /// List of keys for the locations. diff --git a/Runtime/ResourceManager/ResourceManager.cs b/Runtime/ResourceManager/ResourceManager.cs index a9081d7b..fe9b6df7 100644 --- a/Runtime/ResourceManager/ResourceManager.cs +++ b/Runtime/ResourceManager/ResourceManager.cs @@ -551,6 +551,11 @@ internal int CachedOperationCount() return m_AssetOperationCache.Count; } + internal void ClearOperationCache() + { + m_AssetOperationCache.Clear(); + } + /// /// Creates an operation that has already completed with a specified result and error message./>. /// diff --git a/Runtime/ResourceManager/ResourceProviders/AssetBundleProvider.cs b/Runtime/ResourceManager/ResourceProviders/AssetBundleProvider.cs index 848db111..b5f86920 100644 --- a/Runtime/ResourceManager/ResourceProviders/AssetBundleProvider.cs +++ b/Runtime/ResourceManager/ResourceProviders/AssetBundleProvider.cs @@ -282,6 +282,7 @@ public enum LoadType ulong m_LastDownloadedByteCount = 0; float m_TimeoutTimer = 0; int m_TimeoutOverFrames = 0; + internal bool m_DownloadOnly = false; internal Func m_RequestRetryCallback = x => x.ShouldRetryDownloadError(); @@ -467,6 +468,7 @@ public void Start(ProvideHandle provideHandle, AssetBundleUnloadOperation unload m_ProvideHandle = provideHandle; m_Options = m_ProvideHandle.Location.Data as AssetBundleRequestOptions; m_BytesToDownload = -1; + m_DownloadOnly = m_ProvideHandle.Location is DownloadOnlyLocation; m_ProvideHandle.SetProgressCallback(PercentComplete); m_ProvideHandle.SetDownloadProgressCallbacks(GetDownloadStatus); m_ProvideHandle.SetWaitForCompletionCallback(WaitForCompletionHandler); diff --git a/Runtime/ResourceManager/ResourceProviders/BundledAssetProvider.cs b/Runtime/ResourceManager/ResourceProviders/BundledAssetProvider.cs index 67e5ba30..b1ba7a71 100644 --- a/Runtime/ResourceManager/ResourceProviders/BundledAssetProvider.cs +++ b/Runtime/ResourceManager/ResourceProviders/BundledAssetProvider.cs @@ -44,6 +44,19 @@ internal class InternalOp return bundle as T; } + internal static bool IsDownloadOnly(IList results) + { + foreach (var dep in results) + { + if (dep is AssetBundleResource { m_DownloadOnly: true }) + { + return true; + } + } + + return false; + } + public void Start(ProvideHandle provideHandle) { provideHandle.SetProgressCallback(ProgressCallback); @@ -63,7 +76,14 @@ public void Start(ProvideHandle provideHandle) m_AssetBundle = bundleResource.GetAssetBundle(); if (m_AssetBundle == null) { - m_ProvideHandle.Complete(null, false, new Exception("Unable to load dependent bundle from location " + m_ProvideHandle.Location)); +#if UNITY_EDITOR + // This more detailed error is only displayed in the Editor to help with debugging. It's not + // an Editor only issue. + if (IsDownloadOnly(deps)) + m_ProvideHandle.Complete(null, false, new Exception("Unable to load dependent bundle from location " + m_ProvideHandle.Location + ".\nRelease the handle returned by DownloadDependenciesAsync or call it with autoReleaseHandle set to true.")); + else +#endif + m_ProvideHandle.Complete(null, false, new Exception("Unable to load dependent bundle from location " + m_ProvideHandle.Location)); return; } diff --git a/Tests/Editor/Build/BuildLayoutGenerationTaskTests.cs b/Tests/Editor/Build/BuildLayoutGenerationTaskTests.cs index 636510d0..9d068d60 100644 --- a/Tests/Editor/Build/BuildLayoutGenerationTaskTests.cs +++ b/Tests/Editor/Build/BuildLayoutGenerationTaskTests.cs @@ -316,9 +316,12 @@ public void WhenBundleReferencesAnotherBundle_ExternalReferenceExists() BuildLayout layout = BuildAndExtractLayout(); + var layoutGroup1 = layout.Groups.Find((g) => g.Name == "Group1"); + var layoutGroup2 = layout.Groups.Find((g) => g.Name == "Group2"); + // Test - CollectionAssert.Contains(layout.Groups[0].Bundles[0].Dependencies, layout.Groups[1].Bundles[0]); - Assert.AreEqual(layout.Groups[0].Bundles[0].Files[0].Assets[0].ExternallyReferencedAssets[0], layout.Groups[1].Bundles[0].Files[0].Assets[0]); + CollectionAssert.Contains(layoutGroup1.Bundles[0].Dependencies, layoutGroup2.Bundles[0]); + Assert.AreEqual(layoutGroup1.Bundles[0].Files[0].Assets[0].ExternallyReferencedAssets[0], layoutGroup2.Bundles[0].Files[0].Assets[0]); } finally // cleanup { diff --git a/Tests/Editor/DocExampleCode/MiscellaneousTopics.cs b/Tests/Editor/DocExampleCode/MiscellaneousTopics.cs index f3585148..7469e880 100644 --- a/Tests/Editor/DocExampleCode/MiscellaneousTopics.cs +++ b/Tests/Editor/DocExampleCode/MiscellaneousTopics.cs @@ -138,7 +138,7 @@ AsyncOperationHandle loadAssetHandle opList.Add(loadAssetHandle); } - //create a GroupOperation to wait on all the above loads at once. + //create a GroupOperation to wait on all the above loads at once. var groupOp = Addressables.ResourceManager.CreateGenericGroupOperation(opList); if (!groupOp.IsDone) @@ -170,6 +170,11 @@ private IEnumerator PreloadAssets() { AsyncOperationHandle downloadDependencies = Addressables.DownloadDependenciesAsync(key); yield return downloadDependencies; + if(downloadDependencies.Status == AsyncOperationStatus.Failed) + Debug.LogError("Failed to download dependencies for " + key); + + // we need to release the download handle so the assets can be loaded + Addressables.Release(downloadDependencies); } #endregion diff --git a/Tests/Editor/DocExampleCode/ScriptReference/ContentBuiltCheck.cs b/Tests/Editor/DocExampleCode/ScriptReference/ContentBuiltCheck.cs index ffc9940c..9a82e520 100644 --- a/Tests/Editor/DocExampleCode/ScriptReference/ContentBuiltCheck.cs +++ b/Tests/Editor/DocExampleCode/ScriptReference/ContentBuiltCheck.cs @@ -1,33 +1,40 @@ -#region CONTENT_BUILT_CHECK -#if UNITY_EDITOR -using System.Collections; -using System.Collections.Generic; -using System.IO; -using UnityEditor.AddressableAssets; -using UnityEditor.AddressableAssets.Build; -using UnityEditor.AddressableAssets.Settings.GroupSchemas; -using UnityEditor.Build; -using UnityEditor.Build.Reporting; -using UnityEngine; -using UnityEngine.AddressableAssets; - -public class ContentBuiltCheck : IPreprocessBuildWithReport +namespace AddressableAssets.DocExampleCode { - public int callbackOrder => 1; + #region CONTENT_BUILT_CHECK + +#if UNITY_EDITOR + using System.Collections; + using System.Collections.Generic; + using System.IO; + using UnityEditor.AddressableAssets; + using UnityEditor.AddressableAssets.Build; + using UnityEditor.AddressableAssets.Settings.GroupSchemas; + using UnityEditor.Build; + using UnityEditor.Build.Reporting; + using UnityEngine; + using UnityEngine.AddressableAssets; - public void OnPreprocessBuild(BuildReport report) + public class ContentBuiltCheck : IPreprocessBuildWithReport { - // we don't want to throw the exception in our continuous integration environment - if (Application.isBatchMode) - { - return; - } - var settingsPath = Addressables.BuildPath + "/settings.json"; - if (!File.Exists(settingsPath)) + public int callbackOrder => 1; + + public void OnPreprocessBuild(BuildReport report) { - throw new System.Exception("Player content has not been built. Aborting build until content is built. This can be done from the Addressables window in the Build->Build Player Content menu command."); + // we don't want to throw the exception in our continuous integration environment + if (Application.isBatchMode) + { + return; + } + + var settingsPath = Addressables.BuildPath + "/settings.json"; + if (!File.Exists(settingsPath)) + { + throw new System.Exception( + "Player content has not been built. Aborting build until content is built. This can be done from the Addressables window in the Build->Build Player Content menu command."); + } } } -} #endif -#endregion + + #endregion +} diff --git a/Tests/Editor/Expected.meta b/Tests/Editor/Expected.meta new file mode 100644 index 00000000..f8c9ce80 --- /dev/null +++ b/Tests/Editor/Expected.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 5493214f702dd624da5f3724af421133 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Tests/Editor/Expected/~SerializationTests_AddressableAssetSettings.ccd2.unity b/Tests/Editor/Expected/~SerializationTests_AddressableAssetSettings.ccd2.unity new file mode 100644 index 00000000..fc3e061e --- /dev/null +++ b/Tests/Editor/Expected/~SerializationTests_AddressableAssetSettings.ccd2.unity @@ -0,0 +1,137 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &11400000 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 468a46d0ae32c3544b7d98094e6448a9, type: 3} + m_Name: AddressableAssetSettings.Tests + m_EditorClassIdentifier: + m_DefaultGroup: 73831a73d82c83d4183d7e0477f7e745 + m_currentHash: + serializedVersion: 2 + Hash: 00000000000000000000000000000000 + m_OptimizeCatalogSize: 0 + m_BuildRemoteCatalog: 0 + m_CatalogRequestsTimeout: 0 + m_DisableCatalogUpdateOnStart: 0 + m_InternalIdNamingMode: 0 + m_InternalBundleIdMode: 1 + m_AssetLoadMode: 0 + m_BundledAssetProviderType: + m_AssemblyName: Unity.ResourceManager, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + m_ClassName: UnityEngine.ResourceManagement.ResourceProviders.BundledAssetProvider + m_AssetBundleProviderType: + m_AssemblyName: Unity.ResourceManager, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + m_ClassName: UnityEngine.ResourceManagement.ResourceProviders.AssetBundleProvider + m_IgnoreUnsupportedFilesInBuild: 0 + m_UniqueBundleIds: 0 + m_EnableJsonCatalog: 0 + m_NonRecursiveBuilding: 1 + m_CCDEnabled: 1 + m_maxConcurrentWebRequests: 3 + m_UseUWRForLocalBundles: 0 + m_BundleTimeout: 0 + m_BundleRetryCount: 0 + m_BundleRedirectLimit: -1 + m_SharedBundleSettings: 0 + m_SharedBundleSettingsCustomGroupIndex: 0 + m_ContiguousBundles: 1 + m_StripUnityVersionFromBundleBuild: 0 + m_DisableVisibleSubAssetRepresentations: 0 + m_BuiltInBundleNaming: 0 + mBuiltInBundleCustomNaming: + m_MonoScriptBundleNaming: 0 + m_CheckForContentUpdateRestrictionsOption: 0 + m_BuildAndReleaseBinFileOption: 0 + m_MonoScriptBundleCustomNaming: + m_RemoteCatalogBuildPath: + m_Id: + m_RemoteCatalogLoadPath: + m_Id: + m_ContentStateBuildPathProfileVariableName: + m_CustomContentStateBuildPath: + m_ContentStateBuildPath: + m_BuildAddressablesWithPlayerBuild: 0 + m_overridePlayerVersion: '[UnityEditor.PlayerSettings.bundleVersion]' + m_GroupAssets: + - {fileID: 11400000, guid: e3940d5982f85734ca7aec5a9b7a90ee, type: 2} + - {fileID: 11400000, guid: 2308cd47506141c4aae9737b7d567105, type: 2} + m_BuildSettings: + m_LogResourceManagerExceptions: 1 + m_BundleBuildPath: Temp/com.unity.addressables/AssetBundles + m_ProfileSettings: + m_Profiles: + - m_InheritedParent: + m_Id: 5550bbbe2a7ee8c4f9d4600df43218be + m_ProfileName: Default + m_Values: + - m_Id: 0507cc90998e0a04f94da7055d0cc638 + m_Value: '[UnityEditor.EditorUserBuildSettings.activeBuildTarget]' + - m_Id: 0d5680944a6e4bb47a35cd423b95cb36 + m_Value: 'ServerData/[BuildTarget]' + - m_Id: 42ec52cd576b8b9439d83010f809d5b5 + m_Value: + - m_Id: 44693b9e8b6e8ab4e8bada109cd9e70d + m_Value: '{UnityEngine.AddressableAssets.Addressables.RuntimePath}/[BuildTarget]' + - m_Id: 8f726afcd2923be469c05fdfc9963d44 + m_Value: '[UnityEngine.AddressableAssets.Addressables.BuildPath]/[BuildTarget]' + - m_InheritedParent: + m_Id: c901f922cc200454b815a5b33a8427c6 + m_ProfileName: testProfile + m_Values: + - m_Id: 0507cc90998e0a04f94da7055d0cc638 + m_Value: '[UnityEditor.EditorUserBuildSettings.activeBuildTarget]' + - m_Id: 0d5680944a6e4bb47a35cd423b95cb36 + m_Value: 'ServerData/[BuildTarget]' + - m_Id: 42ec52cd576b8b9439d83010f809d5b5 + m_Value: + - m_Id: 44693b9e8b6e8ab4e8bada109cd9e70d + m_Value: '{UnityEngine.AddressableAssets.Addressables.RuntimePath}/[BuildTarget]' + - m_Id: 8f726afcd2923be469c05fdfc9963d44 + m_Value: '[UnityEngine.AddressableAssets.Addressables.BuildPath]/[BuildTarget]' + m_ProfileEntryNames: + - m_Id: 0507cc90998e0a04f94da7055d0cc638 + m_Name: BuildTarget + m_InlineUsage: 0 + - m_Id: 0d5680944a6e4bb47a35cd423b95cb36 + m_Name: Remote.BuildPath + m_InlineUsage: 0 + - m_Id: 42ec52cd576b8b9439d83010f809d5b5 + m_Name: Remote.LoadPath + m_InlineUsage: 0 + - m_Id: 44693b9e8b6e8ab4e8bada109cd9e70d + m_Name: Local.LoadPath + m_InlineUsage: 0 + - m_Id: 8f726afcd2923be469c05fdfc9963d44 + m_Name: Local.BuildPath + m_InlineUsage: 0 + m_ProfileVersion: 1 + m_LabelTable: + m_LabelNames: [] + m_SchemaTemplates: [] + m_GroupTemplateObjects: + - {fileID: 11400000, guid: 97a492a095c0434448524d71cc7f0b0d, type: 2} + m_InitializationObjects: + - {fileID: 11400000, guid: 0f703ff181d66cb4b9124c32c9095b86, type: 2} + - {fileID: 11400000, guid: 71891bb601fa21a47ac0317a4fe38f45, type: 2} + m_CertificateHandlerType: + m_AssemblyName: + m_ClassName: + m_ActivePlayerDataBuilderIndex: 2 + m_DataBuilders: + - {fileID: 11400000, guid: 271b00b9e756a6d448f4ae7a08b88509, type: 2} + - {fileID: 11400000, guid: 533ad9bddde5e2540a2a76c0203d0acb, type: 2} + - {fileID: 11400000, guid: 1694decfa7f2ffd4983ca3978e171998, type: 2} + m_ActiveProfileId: 5550bbbe2a7ee8c4f9d4600df43218be + m_CcdManagedData: + EnvironmentId: + EnvironmentName: + BucketId: + Badge: + State: 0 diff --git a/Tests/Editor/Expected/~SerializationTests_AddressableAssetSettings.ccd2.unity.meta b/Tests/Editor/Expected/~SerializationTests_AddressableAssetSettings.ccd2.unity.meta new file mode 100644 index 00000000..7a6c1d6a --- /dev/null +++ b/Tests/Editor/Expected/~SerializationTests_AddressableAssetSettings.ccd2.unity.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 0d12ded092455874a9617579ade9b0e7 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Tests/Editor/Expected/~SerializationTests_AddressableAssetSettings.ccd3.unity b/Tests/Editor/Expected/~SerializationTests_AddressableAssetSettings.ccd3.unity new file mode 100644 index 00000000..e1448e71 --- /dev/null +++ b/Tests/Editor/Expected/~SerializationTests_AddressableAssetSettings.ccd3.unity @@ -0,0 +1,139 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &11400000 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 468a46d0ae32c3544b7d98094e6448a9, type: 3} + m_Name: AddressableAssetSettings.Tests + m_EditorClassIdentifier: + m_DefaultGroup: 73831a73d82c83d4183d7e0477f7e745 + m_currentHash: + serializedVersion: 2 + Hash: 00000000000000000000000000000000 + m_OptimizeCatalogSize: 0 + m_BuildRemoteCatalog: 0 + m_CatalogRequestsTimeout: 0 + m_DisableCatalogUpdateOnStart: 0 + m_InternalIdNamingMode: 0 + m_InternalBundleIdMode: 1 + m_AssetLoadMode: 0 + m_BundledAssetProviderType: + m_AssemblyName: Unity.ResourceManager, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + m_ClassName: UnityEngine.ResourceManagement.ResourceProviders.BundledAssetProvider + m_AssetBundleProviderType: + m_AssemblyName: Unity.ResourceManager, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + m_ClassName: UnityEngine.ResourceManagement.ResourceProviders.AssetBundleProvider + m_IgnoreUnsupportedFilesInBuild: 0 + m_UniqueBundleIds: 0 + m_EnableJsonCatalog: 0 + m_NonRecursiveBuilding: 1 + m_CCDEnabled: 1 + m_CcdLogRequests: 0 + m_CcdLogRequestHeaders: 0 + m_maxConcurrentWebRequests: 3 + m_UseUWRForLocalBundles: 0 + m_BundleTimeout: 0 + m_BundleRetryCount: 0 + m_BundleRedirectLimit: -1 + m_SharedBundleSettings: 0 + m_SharedBundleSettingsCustomGroupIndex: 0 + m_ContiguousBundles: 1 + m_StripUnityVersionFromBundleBuild: 0 + m_DisableVisibleSubAssetRepresentations: 0 + m_BuiltInBundleNaming: 0 + mBuiltInBundleCustomNaming: + m_MonoScriptBundleNaming: 0 + m_CheckForContentUpdateRestrictionsOption: 0 + m_BuildAndReleaseBinFileOption: 0 + m_MonoScriptBundleCustomNaming: + m_RemoteCatalogBuildPath: + m_Id: + m_RemoteCatalogLoadPath: + m_Id: + m_ContentStateBuildPathProfileVariableName: + m_CustomContentStateBuildPath: + m_ContentStateBuildPath: + m_BuildAddressablesWithPlayerBuild: 0 + m_overridePlayerVersion: '[UnityEditor.PlayerSettings.bundleVersion]' + m_GroupAssets: + - {fileID: 11400000, guid: e3940d5982f85734ca7aec5a9b7a90ee, type: 2} + - {fileID: 11400000, guid: 2308cd47506141c4aae9737b7d567105, type: 2} + m_BuildSettings: + m_LogResourceManagerExceptions: 1 + m_BundleBuildPath: Temp/com.unity.addressables/AssetBundles + m_ProfileSettings: + m_Profiles: + - m_InheritedParent: + m_Id: 5550bbbe2a7ee8c4f9d4600df43218be + m_ProfileName: Default + m_Values: + - m_Id: 0507cc90998e0a04f94da7055d0cc638 + m_Value: '[UnityEditor.EditorUserBuildSettings.activeBuildTarget]' + - m_Id: 0d5680944a6e4bb47a35cd423b95cb36 + m_Value: 'ServerData/[BuildTarget]' + - m_Id: 42ec52cd576b8b9439d83010f809d5b5 + m_Value: + - m_Id: 44693b9e8b6e8ab4e8bada109cd9e70d + m_Value: '{UnityEngine.AddressableAssets.Addressables.RuntimePath}/[BuildTarget]' + - m_Id: 8f726afcd2923be469c05fdfc9963d44 + m_Value: '[UnityEngine.AddressableAssets.Addressables.BuildPath]/[BuildTarget]' + - m_InheritedParent: + m_Id: c901f922cc200454b815a5b33a8427c6 + m_ProfileName: testProfile + m_Values: + - m_Id: 0507cc90998e0a04f94da7055d0cc638 + m_Value: '[UnityEditor.EditorUserBuildSettings.activeBuildTarget]' + - m_Id: 0d5680944a6e4bb47a35cd423b95cb36 + m_Value: 'ServerData/[BuildTarget]' + - m_Id: 42ec52cd576b8b9439d83010f809d5b5 + m_Value: + - m_Id: 44693b9e8b6e8ab4e8bada109cd9e70d + m_Value: '{UnityEngine.AddressableAssets.Addressables.RuntimePath}/[BuildTarget]' + - m_Id: 8f726afcd2923be469c05fdfc9963d44 + m_Value: '[UnityEngine.AddressableAssets.Addressables.BuildPath]/[BuildTarget]' + m_ProfileEntryNames: + - m_Id: 0507cc90998e0a04f94da7055d0cc638 + m_Name: BuildTarget + m_InlineUsage: 0 + - m_Id: 0d5680944a6e4bb47a35cd423b95cb36 + m_Name: Remote.BuildPath + m_InlineUsage: 0 + - m_Id: 42ec52cd576b8b9439d83010f809d5b5 + m_Name: Remote.LoadPath + m_InlineUsage: 0 + - m_Id: 44693b9e8b6e8ab4e8bada109cd9e70d + m_Name: Local.LoadPath + m_InlineUsage: 0 + - m_Id: 8f726afcd2923be469c05fdfc9963d44 + m_Name: Local.BuildPath + m_InlineUsage: 0 + m_ProfileVersion: 1 + m_LabelTable: + m_LabelNames: [] + m_SchemaTemplates: [] + m_GroupTemplateObjects: + - {fileID: 11400000, guid: 97a492a095c0434448524d71cc7f0b0d, type: 2} + m_InitializationObjects: + - {fileID: 11400000, guid: 0f703ff181d66cb4b9124c32c9095b86, type: 2} + - {fileID: 11400000, guid: 71891bb601fa21a47ac0317a4fe38f45, type: 2} + m_CertificateHandlerType: + m_AssemblyName: + m_ClassName: + m_ActivePlayerDataBuilderIndex: 2 + m_DataBuilders: + - {fileID: 11400000, guid: 271b00b9e756a6d448f4ae7a08b88509, type: 2} + - {fileID: 11400000, guid: 533ad9bddde5e2540a2a76c0203d0acb, type: 2} + - {fileID: 11400000, guid: 1694decfa7f2ffd4983ca3978e171998, type: 2} + m_ActiveProfileId: 5550bbbe2a7ee8c4f9d4600df43218be + m_CcdManagedData: + EnvironmentId: + EnvironmentName: + BucketId: + Badge: + State: 0 diff --git a/Tests/Editor/Expected/~SerializationTests_AddressableAssetSettings.ccd3.unity.meta b/Tests/Editor/Expected/~SerializationTests_AddressableAssetSettings.ccd3.unity.meta new file mode 100644 index 00000000..23ed2c0a --- /dev/null +++ b/Tests/Editor/Expected/~SerializationTests_AddressableAssetSettings.ccd3.unity.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 08079bcd5a38ec5459e95445f2f3554b +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Tests/Editor/Expected/~SerializationTests_AddressableAssetSettings.unity b/Tests/Editor/Expected/~SerializationTests_AddressableAssetSettings.unity new file mode 100644 index 00000000..79d50684 --- /dev/null +++ b/Tests/Editor/Expected/~SerializationTests_AddressableAssetSettings.unity @@ -0,0 +1,130 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &11400000 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 468a46d0ae32c3544b7d98094e6448a9, type: 3} + m_Name: AddressableAssetSettings.Tests + m_EditorClassIdentifier: + m_DefaultGroup: 73831a73d82c83d4183d7e0477f7e745 + m_currentHash: + serializedVersion: 2 + Hash: 00000000000000000000000000000000 + m_OptimizeCatalogSize: 0 + m_BuildRemoteCatalog: 0 + m_CatalogRequestsTimeout: 0 + m_DisableCatalogUpdateOnStart: 0 + m_InternalIdNamingMode: 0 + m_InternalBundleIdMode: 1 + m_AssetLoadMode: 0 + m_BundledAssetProviderType: + m_AssemblyName: Unity.ResourceManager, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + m_ClassName: UnityEngine.ResourceManagement.ResourceProviders.BundledAssetProvider + m_AssetBundleProviderType: + m_AssemblyName: Unity.ResourceManager, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + m_ClassName: UnityEngine.ResourceManagement.ResourceProviders.AssetBundleProvider + m_IgnoreUnsupportedFilesInBuild: 0 + m_UniqueBundleIds: 0 + m_EnableJsonCatalog: 0 + m_NonRecursiveBuilding: 1 + m_CCDEnabled: 0 + m_maxConcurrentWebRequests: 3 + m_UseUWRForLocalBundles: 0 + m_BundleTimeout: 0 + m_BundleRetryCount: 0 + m_BundleRedirectLimit: -1 + m_SharedBundleSettings: 0 + m_SharedBundleSettingsCustomGroupIndex: 0 + m_ContiguousBundles: 1 + m_StripUnityVersionFromBundleBuild: 0 + m_DisableVisibleSubAssetRepresentations: 0 + m_BuiltInBundleNaming: 0 + mBuiltInBundleCustomNaming: + m_MonoScriptBundleNaming: 0 + m_CheckForContentUpdateRestrictionsOption: 0 + m_MonoScriptBundleCustomNaming: + m_RemoteCatalogBuildPath: + m_Id: + m_RemoteCatalogLoadPath: + m_Id: + m_ContentStateBuildPathProfileVariableName: + m_CustomContentStateBuildPath: + m_ContentStateBuildPath: + m_BuildAddressablesWithPlayerBuild: 0 + m_overridePlayerVersion: '[UnityEditor.PlayerSettings.bundleVersion]' + m_GroupAssets: + - {fileID: 11400000, guid: e3940d5982f85734ca7aec5a9b7a90ee, type: 2} + - {fileID: 11400000, guid: 2308cd47506141c4aae9737b7d567105, type: 2} + m_BuildSettings: + m_LogResourceManagerExceptions: 1 + m_BundleBuildPath: Temp/com.unity.addressables/AssetBundles + m_ProfileSettings: + m_Profiles: + - m_InheritedParent: + m_Id: 5550bbbe2a7ee8c4f9d4600df43218be + m_ProfileName: Default + m_Values: + - m_Id: 0507cc90998e0a04f94da7055d0cc638 + m_Value: '[UnityEditor.EditorUserBuildSettings.activeBuildTarget]' + - m_Id: 0d5680944a6e4bb47a35cd423b95cb36 + m_Value: 'ServerData/[BuildTarget]' + - m_Id: 42ec52cd576b8b9439d83010f809d5b5 + m_Value: + - m_Id: 44693b9e8b6e8ab4e8bada109cd9e70d + m_Value: '{UnityEngine.AddressableAssets.Addressables.RuntimePath}/[BuildTarget]' + - m_Id: 8f726afcd2923be469c05fdfc9963d44 + m_Value: '[UnityEngine.AddressableAssets.Addressables.BuildPath]/[BuildTarget]' + - m_InheritedParent: + m_Id: c901f922cc200454b815a5b33a8427c6 + m_ProfileName: testProfile + m_Values: + - m_Id: 0507cc90998e0a04f94da7055d0cc638 + m_Value: '[UnityEditor.EditorUserBuildSettings.activeBuildTarget]' + - m_Id: 0d5680944a6e4bb47a35cd423b95cb36 + m_Value: 'ServerData/[BuildTarget]' + - m_Id: 42ec52cd576b8b9439d83010f809d5b5 + m_Value: + - m_Id: 44693b9e8b6e8ab4e8bada109cd9e70d + m_Value: '{UnityEngine.AddressableAssets.Addressables.RuntimePath}/[BuildTarget]' + - m_Id: 8f726afcd2923be469c05fdfc9963d44 + m_Value: '[UnityEngine.AddressableAssets.Addressables.BuildPath]/[BuildTarget]' + m_ProfileEntryNames: + - m_Id: 0507cc90998e0a04f94da7055d0cc638 + m_Name: BuildTarget + m_InlineUsage: 0 + - m_Id: 0d5680944a6e4bb47a35cd423b95cb36 + m_Name: Remote.BuildPath + m_InlineUsage: 0 + - m_Id: 42ec52cd576b8b9439d83010f809d5b5 + m_Name: Remote.LoadPath + m_InlineUsage: 0 + - m_Id: 44693b9e8b6e8ab4e8bada109cd9e70d + m_Name: Local.LoadPath + m_InlineUsage: 0 + - m_Id: 8f726afcd2923be469c05fdfc9963d44 + m_Name: Local.BuildPath + m_InlineUsage: 0 + m_ProfileVersion: 1 + m_LabelTable: + m_LabelNames: [] + m_SchemaTemplates: [] + m_GroupTemplateObjects: + - {fileID: 11400000, guid: 97a492a095c0434448524d71cc7f0b0d, type: 2} + m_InitializationObjects: + - {fileID: 11400000, guid: 0f703ff181d66cb4b9124c32c9095b86, type: 2} + - {fileID: 11400000, guid: 71891bb601fa21a47ac0317a4fe38f45, type: 2} + m_CertificateHandlerType: + m_AssemblyName: + m_ClassName: + m_ActivePlayerDataBuilderIndex: 2 + m_DataBuilders: + - {fileID: 11400000, guid: 271b00b9e756a6d448f4ae7a08b88509, type: 2} + - {fileID: 11400000, guid: 533ad9bddde5e2540a2a76c0203d0acb, type: 2} + - {fileID: 11400000, guid: 1694decfa7f2ffd4983ca3978e171998, type: 2} + m_ActiveProfileId: 5550bbbe2a7ee8c4f9d4600df43218be diff --git a/Tests/Editor/Expected/~SerializationTests_AddressableAssetSettings.unity.meta b/Tests/Editor/Expected/~SerializationTests_AddressableAssetSettings.unity.meta new file mode 100644 index 00000000..b08956dd --- /dev/null +++ b/Tests/Editor/Expected/~SerializationTests_AddressableAssetSettings.unity.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: c0b327cec738b6e4ea324f6496c73809 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Tests/Editor/Expected/~SerializationTests_Group.unity b/Tests/Editor/Expected/~SerializationTests_Group.unity new file mode 100644 index 00000000..771d3af7 --- /dev/null +++ b/Tests/Editor/Expected/~SerializationTests_Group.unity @@ -0,0 +1,47 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &11400000 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: bbb281ee3bf0b054c82ac2347e9e782c, type: 3} + m_Name: testGroup + m_EditorClassIdentifier: + m_GroupName: testGroup + m_GUID: 16cd2736586abc441a3ef8bffa03b61f + m_SerializeEntries: + - m_GUID: 2269b1fb-67ee-4b32-a936-4647ff4c45b4 + m_Address: firstAsset + m_ReadOnly: 0 + m_SerializedLabels: + - a + - a2 + - a5 + FlaggedDuringContentUpdateRestriction: 0 + - m_GUID: 4df50598-ce2c-4265-a0f9-4e943a2991b0 + m_Address: secondAsset + m_ReadOnly: 0 + m_SerializedLabels: + - a + - b + - c + FlaggedDuringContentUpdateRestriction: 0 + - m_GUID: 9e86b64f-f58e-4d4f-aa9d-6e8be96505ec + m_Address: thirdAsset + m_ReadOnly: 0 + m_SerializedLabels: + - 2 + - 22 + - 5 + FlaggedDuringContentUpdateRestriction: 0 + m_ReadOnly: 0 + m_Settings: {fileID: 11400000, guid: 3bf47571d203fa84d8dd31832e7c9339, type: 2} + m_SchemaSet: + m_Schemas: + - {fileID: 11400000, guid: 798716054e8a18a479c179e6d6f5ad2d, type: 2} + - {fileID: 11400000, guid: 7991916e228786548a8c905c2235f71f, type: 2} diff --git a/Tests/Editor/Expected/~SerializationTests_Group.unity.meta b/Tests/Editor/Expected/~SerializationTests_Group.unity.meta new file mode 100644 index 00000000..0fa02104 --- /dev/null +++ b/Tests/Editor/Expected/~SerializationTests_Group.unity.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 5ff39781fd7edcc479cec1dfcfe2d9ef +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Tests/Editor/Expected/~SerializationTests_GroupTemplate.unity b/Tests/Editor/Expected/~SerializationTests_GroupTemplate.unity new file mode 100644 index 00000000..eefba6ee --- /dev/null +++ b/Tests/Editor/Expected/~SerializationTests_GroupTemplate.unity @@ -0,0 +1,80 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &-6794523166426839361 +MonoBehaviour: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: e5d17a21594effb4e9591490b009e7aa, type: 3} + m_Name: BundledAssetGroupSchema + m_EditorClassIdentifier: + m_Group: {fileID: 0} + m_InternalBundleIdMode: 1 + m_Compression: 1 + m_IncludeAddressInCatalog: 1 + m_IncludeGUIDInCatalog: 1 + m_IncludeLabelsInCatalog: 1 + m_InternalIdNamingMode: 0 + m_CacheClearBehavior: 0 + m_IncludeInBuild: 1 + m_BundledAssetProviderType: + m_AssemblyName: Unity.ResourceManager, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + m_ClassName: UnityEngine.ResourceManagement.ResourceProviders.BundledAssetProvider + m_ForceUniqueProvider: 0 + m_UseAssetBundleCache: 1 + m_UseAssetBundleCrc: 1 + m_UseAssetBundleCrcForCachedBundles: 1 + m_UseUWRForLocalBundles: 0 + m_Timeout: 0 + m_ChunkedTransfer: 0 + m_RedirectLimit: -1 + m_RetryCount: 0 + m_BuildPath: + m_Id: + m_LoadPath: + m_Id: + m_BundleMode: 0 + m_AssetBundleProviderType: + m_AssemblyName: Unity.ResourceManager, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + m_ClassName: UnityEngine.ResourceManagement.ResourceProviders.AssetBundleProvider + m_UseDefaultSchemaSettings: 0 + m_SelectedPathPairIndex: 0 + m_BundleNaming: 0 + m_AssetLoadMode: 0 +--- !u!114 &-1107740541918034454 +MonoBehaviour: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5834b5087d578d24c926ce20cd31e6d6, type: 3} + m_Name: ContentUpdateGroupSchema + m_EditorClassIdentifier: + m_Group: {fileID: 0} + m_StaticContent: 0 +--- !u!114 &11400000 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 1a3c5d64ac83548c09dd1678b9f6f1cd, type: 3} + m_Name: myTemplate + m_EditorClassIdentifier: + m_SchemaObjects: + - {fileID: -6794523166426839361} + - {fileID: -1107740541918034454} + m_Description: 'my description + + with carriage return' + m_Settings: {fileID: 11400000, guid: 3bf47571d203fa84d8dd31832e7c9339, type: 2} diff --git a/Tests/Editor/Expected/~SerializationTests_GroupTemplate.unity.meta b/Tests/Editor/Expected/~SerializationTests_GroupTemplate.unity.meta new file mode 100644 index 00000000..c418be69 --- /dev/null +++ b/Tests/Editor/Expected/~SerializationTests_GroupTemplate.unity.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: f0f1b78a97fcd1e4aa45deb60db1d673 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Tests/Editor/Expected/~SerializationTests_ProfileDataSourceSettings.unity b/Tests/Editor/Expected/~SerializationTests_ProfileDataSourceSettings.unity new file mode 100644 index 00000000..9750f0ab --- /dev/null +++ b/Tests/Editor/Expected/~SerializationTests_ProfileDataSourceSettings.unity @@ -0,0 +1,51 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &11400000 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 7e3976da977cb49238499ea3b4c237ae, type: 3} + m_Name: ProfileDataSourceSettings + m_EditorClassIdentifier: + profileGroupTypes: + - m_GroupTypePrefix: Built-In + m_Variables: + - m_Suffix: BuildPath + m_Value: '[UnityEngine.AddressableAssets.Addressables.BuildPath]/[BuildTarget]' + - m_Suffix: LoadPath + m_Value: '{UnityEngine.AddressableAssets.Addressables.RuntimePath}/[BuildTarget]' + - m_GroupTypePrefix: testPrefix + m_Variables: + - m_Suffix: BuildPath + m_Value: Build/ + - m_Suffix: EnvironmentName + m_Value: production + - m_Suffix: LoadPath + m_Value: https://example.com/a/ + environments: + - id: 0214d8a4-af63-4534-814f-431d180926d6 + projectId: + projectGenesisId: + name: production + isDefault: 0 + - id: 7c9f9aeb-2b9d-4258-bf52-0d2b6118ac39 + projectId: + projectGenesisId: + name: staging + isDefault: 0 + - id: a9ec150c-9a63-46ab-9a21-a3434afc7ab3 + projectId: + projectGenesisId: + name: development + isDefault: 0 + currentEnvironment: + id: + projectId: + projectGenesisId: + name: + isDefault: 0 diff --git a/Tests/Editor/Expected/~SerializationTests_ProfileDataSourceSettings.unity.meta b/Tests/Editor/Expected/~SerializationTests_ProfileDataSourceSettings.unity.meta new file mode 100644 index 00000000..0ab30a16 --- /dev/null +++ b/Tests/Editor/Expected/~SerializationTests_ProfileDataSourceSettings.unity.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: e78f261bbbf61e54990aba7192f284fe +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Tests/Editor/Fixtures.meta b/Tests/Editor/Fixtures.meta new file mode 100644 index 00000000..b7bb25b4 --- /dev/null +++ b/Tests/Editor/Fixtures.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 7277b88aa10077d4e8b0b5d4c48adede +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Tests/Editor/Fixtures/InitFixture.cs b/Tests/Editor/Fixtures/InitFixture.cs new file mode 100644 index 00000000..ace900b6 --- /dev/null +++ b/Tests/Editor/Fixtures/InitFixture.cs @@ -0,0 +1,20 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.ResourceManagement.Util; + +namespace UnityEditor.AddressableAssets.Tests +{ + public class InitFixture : ScriptableObject, IObjectInitializationDataProvider + { + public string Name + { + get => "InitFixture1"; + } + + public ObjectInitializationData CreateObjectInitializationData() + { + return new ObjectInitializationData(); + } + } +} diff --git a/Tests/Editor/Fixtures/InitFixture.cs.meta b/Tests/Editor/Fixtures/InitFixture.cs.meta new file mode 100644 index 00000000..e243f4f1 --- /dev/null +++ b/Tests/Editor/Fixtures/InitFixture.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 01e363df9994fd447956660126e96f98 \ No newline at end of file diff --git a/Tests/Editor/Fixtures/InitFixture1.asset b/Tests/Editor/Fixtures/InitFixture1.asset new file mode 100644 index 00000000..a2d4cde7 --- /dev/null +++ b/Tests/Editor/Fixtures/InitFixture1.asset @@ -0,0 +1,14 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &11400000 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 01e363df9994fd447956660126e96f98, type: 3} + m_Name: InitFixture1 + m_EditorClassIdentifier: diff --git a/Tests/Editor/Fixtures/InitFixture1.asset.meta b/Tests/Editor/Fixtures/InitFixture1.asset.meta new file mode 100644 index 00000000..762de9dc --- /dev/null +++ b/Tests/Editor/Fixtures/InitFixture1.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 0f703ff181d66cb4b9124c32c9095b86 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Tests/Editor/Fixtures/InitFixture2.asset b/Tests/Editor/Fixtures/InitFixture2.asset new file mode 100644 index 00000000..955ef045 --- /dev/null +++ b/Tests/Editor/Fixtures/InitFixture2.asset @@ -0,0 +1,19 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &11400000 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: b44c2db511ef19949950f4b3b93a6e53, type: 3} + m_Name: InitFixture2 + m_EditorClassIdentifier: + m_Data: + m_CompressionEnabled: 1 + m_CacheDirectoryOverride: + m_LimitCacheSize: 1 + m_MaximumCacheSize: 2147483648 diff --git a/Tests/Editor/Fixtures/InitFixture2.asset.meta b/Tests/Editor/Fixtures/InitFixture2.asset.meta new file mode 100644 index 00000000..4ed794cf --- /dev/null +++ b/Tests/Editor/Fixtures/InitFixture2.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 71891bb601fa21a47ac0317a4fe38f45 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Tests/Editor/OptionalPackages/Ccd/CcdBuildMenuTests.cs b/Tests/Editor/OptionalPackages/Ccd/CcdBuildMenuTests.cs index dc3cc5fc..0cfef6c3 100644 --- a/Tests/Editor/OptionalPackages/Ccd/CcdBuildMenuTests.cs +++ b/Tests/Editor/OptionalPackages/Ccd/CcdBuildMenuTests.cs @@ -1,6 +1,8 @@ #if (ENABLE_CCD && ENABLE_MOQ) using System; +using System.Collections.Generic; using System.IO; +using System.Linq; using System.Reflection; using System.Text.RegularExpressions; using NUnit.Framework; @@ -29,6 +31,14 @@ namespace UnityEditor.AddressableAssets.Tests.OptionalPackages.Ccd { public class CcdBuildMenuTests { + public enum RemoteCatalogType + { + None, + Local, + Remote + }; + static RemoteCatalogType[] remoteCatalogTypes = new RemoteCatalogType[] { RemoteCatalogType.None, RemoteCatalogType.Local, RemoteCatalogType.Remote}; + private const int k_SleepTime = 30000; // this is all cribbed from AddressableAssetTestsBase @@ -58,6 +68,9 @@ public class CcdBuildMenuTests private static string m_ManagedBucketId = "05bb444b-5c7e-40ad-a123-fd7596f60784"; private static string m_StaticBucketId = "ce62fde2-1451-4e0c-adee-1924e95b48e7"; private static string m_SecondBucketId = "98476627-3c9d-49a2-ac79-b84e3e2b6913"; + // this is used to queue up subsequent calls to ListBuckets + private Queue> m_listBucketCalls = new Queue>(); + private AddressablesDataBuilderInput m_Input; @@ -247,6 +260,26 @@ public async Task SetUp() m_CcdManagementMock = new CcdManagementServiceSdkMock(); m_CcdManagementMock.Init(); + #if CCD_3_OR_NEWER + m_CcdManagementMock.Setup(client => client.ListBucketsAsync(It.IsAny(), It.IsAny())).Returns((v, bucketsOptions) => + #else + m_CcdManagementMock.Setup(client => client.ListBucketsAsync(It.IsAny())).Returns((v) => + #endif + { + if (v.Page == 1) + { + if (m_listBucketCalls.Count == 0) + { + throw new Exception("no list bucket data found for call"); + } + + return Task.FromResult(m_listBucketCalls.Dequeue()); + } + if (v.Page == 2) + throw new CcdManagementException(CcdManagementErrorCodes.OutOfRange, "out of range"); + return null; + }); + // Refresh data sources to populate our profiles Assert.True(await refreshDataSources()); @@ -263,6 +296,10 @@ public void Teardown() m_CcdManagementMock.VerifyAll(); AssetDatabase.DeleteAsset(k_ProfileSettingsPath); deleteContentStateBin(); + if (m_listBucketCalls.Count > 0) + { + throw new Exception("not all list bucket calls were made"); + } } [OneTimeTearDown] @@ -298,6 +335,29 @@ private void createFile(string fileName) Assert.True(File.Exists(fileName)); } + private void setupRemoteCatalog(RemoteCatalogType remoteCatalogType) + { + switch(remoteCatalogType) + { + case RemoteCatalogType.None: + m_Settings.BuildRemoteCatalog = false; + m_Settings.RemoteCatalogBuildPath = new ProfileValueReference(); + break; + case RemoteCatalogType.Local: + m_Settings.BuildRemoteCatalog = true; + m_Settings.RemoteCatalogBuildPath = new ProfileValueReference(); + m_Settings.RemoteCatalogBuildPath.SetVariableByName(m_Settings, "Local.BuildPath"); + m_Settings.RemoteCatalogLoadPath.SetVariableByName(m_Settings, "Local.LoadPath"); + break; + case RemoteCatalogType.Remote: + m_Settings.BuildRemoteCatalog = true; + m_Settings.RemoteCatalogBuildPath = new ProfileValueReference(); + m_Settings.RemoteCatalogBuildPath.SetVariableByName(m_Settings, "Remote.BuildPath"); + m_Settings.RemoteCatalogLoadPath.SetVariableByName(m_Settings, "Remote.LoadPath"); + break; + } + } + [Test] public async Task AutomaticAndStaticProfileUploadAndReleaseSuccess() { @@ -686,33 +746,26 @@ public async Task refreshDataSources() var managedBucket = new CcdBucket(id: Guid.Parse(m_ManagedBucketId), name: EditorUserBuildSettings.activeBuildTarget.ToString(), attributes: new CcdBucketAttributes(promoteOnly: false)); var staticBucket = new CcdBucket(id: Guid.Parse(m_StaticBucketId), name: "Static Bucket", attributes: new CcdBucketAttributes(promoteOnly: false)); var secondBucket = new CcdBucket(id: Guid.Parse(m_SecondBucketId), name: "Second Bucket", attributes: new CcdBucketAttributes(promoteOnly: false)); - var buckets = new System.Collections.Generic.List(); + var buckets = new List(); buckets.Add(managedBucket); buckets.Add(staticBucket); buckets.Add(secondBucket); - // this is refresh data -#if CCD_3_OR_NEWER - var listBucketsCalls = m_CcdManagementMock.Setup(client => client.ListBucketsAsync(It.IsAny(), It.IsAny())).Returns((v, bucketsOptions) => -#else - var listBucketsCalls = m_CcdManagementMock.Setup(client => client.ListBucketsAsync(It.IsAny())).Returns((v) => -#endif - { - if (v.Page == 1) - return Task.FromResult(buckets); - if (v.Page == 2) - throw new CcdManagementException(CcdManagementErrorCodes.OutOfRange, "out of range"); - return null; - }); - - // no badges m_CcdManagementMock.Setup(client => client.ListBadgesAsync(managedBucket.Id, It.IsAny())) .Throws(new CcdManagementException(CcdManagementErrorCodes.OutOfRange, "out of range")); m_CcdManagementMock.Setup(client => client.ListBadgesAsync(staticBucket.Id, It.IsAny())) .Throws(new CcdManagementException(CcdManagementErrorCodes.OutOfRange, "out of range")); m_CcdManagementMock.Setup(client => client.ListBadgesAsync(secondBucket.Id, It.IsAny())) .Throws(new CcdManagementException(CcdManagementErrorCodes.OutOfRange, "out of range")); - + return await refreshDataSources(buckets); + } + public async Task refreshDataSources(List buckets) + { + // this is refresh data + // production + m_listBucketCalls.Enqueue(buckets); + // development + m_listBucketCalls.Enqueue(new List()); // I can't add group types directly as it's internal. So adding my test group types by calling var result = await CcdBuildEvents.Instance.RefreshDataSources(m_Input); @@ -732,10 +785,10 @@ public async Task refreshDataSources() [Test] - public async Task VerifyTargetBucketAllLocal() + public async Task VerifyTargetBucketAllLocal([ValueSource(nameof(remoteCatalogTypes))] RemoteCatalogType remoteCatalogType) { m_Settings.activeProfileId = m_Settings.profileSettings.GetProfileId(k_ProfileDefault); - m_Settings.BuildRemoteCatalog = false; + setupRemoteCatalog(remoteCatalogType); var success = await CcdBuildEvents.Instance.VerifyTargetBucket(m_Input); Assert.True(success); #if !ADDRESSABLES_WITHOUT_GROUP_FIXES @@ -762,12 +815,26 @@ private CcdManagementException InternalErrorException() } [Test] - public async Task VerifyTargetBucketAutomaticAndStaticProfile() + public async Task VerifyTargetBucketAutomaticAndStaticProfile([ValueSource(nameof(remoteCatalogTypes))] RemoteCatalogType remoteCatalogType) { - m_CcdManagementMock.Setup(client => client.CreateBucketAsync(It.Is((v) => v.Name == EditorUserBuildSettings.activeBuildTarget.ToString()))) - .Throws(AlreadyExistsException); m_Settings.activeProfileId = m_Settings.profileSettings.GetProfileId(k_ProfileAutomaticAndStatic); - m_Settings.BuildRemoteCatalog = true; + setupRemoteCatalog(remoteCatalogType); + var success = await CcdBuildEvents.Instance.VerifyTargetBucket(m_Input); + Assert.True(success); + + var ccdManagedDataField = m_Input.AddressableSettings.GetType().GetField("m_CcdManagedData", BindingFlags.NonPublic | BindingFlags.Instance); + var ccdManagedDataInstance = ccdManagedDataField.GetValue(m_Input.AddressableSettings); + var bucketIdField = ccdManagedDataInstance.GetType().GetField("BucketId", BindingFlags.Public | BindingFlags.Instance); + var verifiedBucketId = bucketIdField.GetValue(ccdManagedDataInstance); + Assert.AreEqual(m_ManagedBucketId, verifiedBucketId); + } + + [Test] + public async Task VerifyTargetBucketAutomaticProfile([ValueSource(nameof(remoteCatalogTypes))] RemoteCatalogType remoteCatalogType) + { + m_Settings.activeProfileId = m_Settings.profileSettings.GetProfileId(k_ProfileAutomaticAndLocal); + setupRemoteCatalog(remoteCatalogType); + var success = await CcdBuildEvents.Instance.VerifyTargetBucket(m_Input); Assert.True(success); @@ -779,12 +846,19 @@ public async Task VerifyTargetBucketAutomaticAndStaticProfile() } [Test] - public async Task VerifyTargetBucketAutomaticProfile() + public async Task VerifyTargetBucketAutomaticProfileNoManagedData([ValueSource(nameof(remoteCatalogTypes))] RemoteCatalogType remoteCatalogType) { + var managedBucket = new CcdBucket(id: Guid.Parse(m_ManagedBucketId), name: EditorUserBuildSettings.activeBuildTarget.ToString(), attributes: new CcdBucketAttributes(promoteOnly: false)); m_CcdManagementMock.Setup(client => client.CreateBucketAsync(It.Is((v) => v.Name == EditorUserBuildSettings.activeBuildTarget.ToString()))) - .Throws(AlreadyExistsException); + .Returns(Task.FromResult(managedBucket)); m_Settings.activeProfileId = m_Settings.profileSettings.GetProfileId(k_ProfileAutomaticAndLocal); - m_Settings.BuildRemoteCatalog = true; + setupRemoteCatalog(remoteCatalogType); + + resetManagedBucket(); + + // this should be a completely new remote project + await refreshDataSources(new List()); + var success = await CcdBuildEvents.Instance.VerifyTargetBucket(m_Input); Assert.True(success); @@ -796,26 +870,56 @@ public async Task VerifyTargetBucketAutomaticProfile() } [Test] - public async Task VerifyTargetBucketStaticProfile() + public async Task VerifyTargetBucketAutomaticProfileNoManagedDataBucketExists([ValueSource(nameof(remoteCatalogTypes))] RemoteCatalogType remoteCatalogType) + { + var managedBucket = new CcdBucket(id: Guid.Parse(m_ManagedBucketId), name: EditorUserBuildSettings.activeBuildTarget.ToString(), attributes: new CcdBucketAttributes(promoteOnly: false)); + m_CcdManagementMock.Setup(client => client.CreateBucketAsync(It.Is((v) => v.Name == EditorUserBuildSettings.activeBuildTarget.ToString()))) + .Throws(AlreadyExistsException()); + m_Settings.activeProfileId = m_Settings.profileSettings.GetProfileId(k_ProfileAutomaticAndLocal); + setupRemoteCatalog(remoteCatalogType); + + resetManagedBucket(); + + // this should be a completely new remote project + await refreshDataSources(new List()); + // since the bucket already exists we should load the bucket + m_listBucketCalls.Enqueue(new List(){managedBucket}); + + var success = await CcdBuildEvents.Instance.VerifyTargetBucket(m_Input); + Assert.True(success); + + var ccdManagedDataField = m_Input.AddressableSettings.GetType().GetField("m_CcdManagedData", BindingFlags.NonPublic | BindingFlags.Instance); + var ccdManagedDataInstance = ccdManagedDataField.GetValue(m_Input.AddressableSettings); + var bucketIdField = ccdManagedDataInstance.GetType().GetField("BucketId", BindingFlags.Public | BindingFlags.Instance); + var verifiedBucketId = bucketIdField.GetValue(ccdManagedDataInstance); + Assert.AreEqual(m_ManagedBucketId, verifiedBucketId); + } + + + [Test] + public async Task VerifyTargetBucketStaticProfile([ValueSource(nameof(remoteCatalogTypes))] RemoteCatalogType remoteCatalogType) { m_Settings.activeProfileId = m_Settings.profileSettings.GetProfileId(k_ProfileStaticAndLocal); - m_Settings.BuildRemoteCatalog = true; + setupRemoteCatalog(remoteCatalogType); var success = await CcdBuildEvents.Instance.VerifyTargetBucket(m_Input); Assert.True(success); } [Test] - public async Task VerifyTargetBucketOverride() + public async Task VerifyTargetBucketOverride([ValueSource(nameof(remoteCatalogTypes))] RemoteCatalogType remoteCatalogType) { var ccdManagedDataField = m_Input.AddressableSettings.GetType().GetField("m_CcdManagedData", BindingFlags.NonPublic | BindingFlags.Instance); var ccdManagedDataInstance = ccdManagedDataField.GetValue(m_Input.AddressableSettings); var stateField = ccdManagedDataInstance.GetType().GetField("State", BindingFlags.Public | BindingFlags.Instance); var stateEnumValues = stateField.GetValue(ccdManagedDataInstance).GetType().GetEnumValues(); - - m_Settings.BuildRemoteCatalog = true; stateField.SetValue(ccdManagedDataInstance, stateEnumValues.GetValue(2)); + m_Settings.activeProfileId = m_Settings.profileSettings.GetProfileId(k_ProfileAutomaticAndLocal); + setupRemoteCatalog(remoteCatalogType); + m_Settings.RemoteCatalogBuildPath.SetVariableByName(m_Settings, "Remote.BuildPath"); + m_Settings.RemoteCatalogLoadPath.SetVariableByName(m_Settings, "Remote.LoadPath"); + var success = await CcdBuildEvents.Instance.VerifyTargetBucket(m_Input); Assert.True(success); } diff --git a/Tests/Editor/SerializationTests.cs b/Tests/Editor/SerializationTests.cs new file mode 100644 index 00000000..3fab40d3 --- /dev/null +++ b/Tests/Editor/SerializationTests.cs @@ -0,0 +1,427 @@ +using System; +using System.Collections.Generic; +using System.IO; +using NUnit.Framework; +using UnityEditor.AddressableAssets.Settings; +using UnityEditor.AddressableAssets.Settings.GroupSchemas; +using UnityEngine; +using UnityEngine.ResourceManagement.Util; +using Object = UnityEngine.Object; + +namespace UnityEditor.AddressableAssets.Tests +{ + /* + * This is a series of tests to verify that our serialization is deterministic and does not + * trigger version control changes. + * + * If you get instabilities or one-off test failures, it's probably because something has + * changed in serialization of the object in question and we're not sorting the output. + * + * This test is made to be unstable if there are changes that are not deterministic. So if + * you see intermittent failures that's the sign there's a bug and should NOT be ignored. + */ + public class SerializationTests : AddressableAssetTestBase + { + + public string groupGuid = "422b6705-092c-4699-b57b-abfe7a6245d0"; + private System.Random m_Rnd; + private int m_Seed = 0; + private List m_SchemaTypes; + private string m_PackagePath; + + private void Shuffle(List toShuffle) + { + toShuffle.Sort((x, y) => m_Rnd.Next() - m_Rnd.Next()); + } + + + [OneTimeSetUp] + public new void Init() + { + base.Init(); + m_Rnd = new System.Random(m_Seed); + + m_SchemaTypes = new () + { + typeof(ContentUpdateGroupSchema), typeof(BundledAssetGroupSchema) + }; + } + // + [SetUp] + public void Setup() + { + Settings.groups.Clear(); + // this lazy creates the default group + var defaultGroup = Settings.DefaultGroup; + // the way our package isolation tests work the tests are built into their own package + // which means we have to load expected files from a different location + m_PackagePath = "Packages/com.unity.addressables/Tests/Editor"; + if (Directory.Exists("Packages/com.unity.addressables.tests")) + { + m_PackagePath = "Packages/com.unity.addressables.tests/Tests/Editor"; + } + } + + internal string GetExpectedPath(string filename) + { + return $"{m_PackagePath}/Expected/{filename}"; + } + + internal string GetFixturePath(string filename) + { + return $"{m_PackagePath}/Fixtures/{filename}"; + } + + [TestCase] + public void TestAssetGroupSerialization() + { + var group = Settings.CreateGroup("testGroup", false, false, false, + new List(), m_SchemaTypes.ToArray()); + + var labels = CreateAndShuffleLabels(); + AddAssetEntries(group, labels); + EditorUtility.SetDirty(group); + AssetDatabase.SaveAssetIfDirty(group); + AssetDatabase.SaveAssetIfDirty(Settings); + + var groupPath = AssetDatabase.GetAssetPath(group); + RemapMetaGuids(group); + AssetDatabase.Refresh(); + group = AssetDatabase.LoadAssetAtPath(groupPath); + Assert.AreEqual("16cd2736586abc441a3ef8bffa03b61f", group.Guid); + Shuffle(group.m_SerializeEntries); + Shuffle(group.Schemas); + EditorUtility.SetDirty(group); + AssetDatabase.SaveAssetIfDirty(group); + + var expectedSerializedGroup = File.ReadAllText(GetExpectedPath("~SerializationTests_Group.unity")); + var serializedGroup = File.ReadAllText(groupPath); + AssertSerializedAreEqual(expectedSerializedGroup, serializedGroup); + } + + private List> CreateAndShuffleLabels() + { + var labels = new List> + { + new() {"c", "a", "b"}, + new() {"a5", "a2", "a"}, + new() {"5", "22", "2"} + }; + foreach (var label in labels) + { + Shuffle(label); + } + + return labels; + } + + private void AddAssetEntries(AddressableAssetGroup group, List> labels) + { + var entry1 = new AddressableAssetEntry("4df50598-ce2c-4265-a0f9-4e943a2991b0", "secondAsset", group, false); + foreach (var label in labels[0]) + { + entry1.SetLabel(label, true, false, false); + } + var entry2 = new AddressableAssetEntry("2269b1fb-67ee-4b32-a936-4647ff4c45b4", "firstAsset", group, false); + foreach (var label in labels[1]) + { + entry2.SetLabel(label, true, false, false); + } + var entry3 = new AddressableAssetEntry("9e86b64f-f58e-4d4f-aa9d-6e8be96505ec", "thirdAsset", group, false); + foreach (var label in labels[2]) + { + entry3.SetLabel(label, true, false, false); + } + group.AddAssetEntry(entry1); + group.AddAssetEntry(entry2); + group.AddAssetEntry(entry3); + } + + [TestCase] + public void TestAssetGroupTemplateSerialization() + { + var newAssetGroupTemplate = Settings.CreateAndAddGroupTemplateInternal("myTemplate", "my description\nwith carriage return", m_SchemaTypes.ToArray()); + AssetDatabase.TryGetGUIDAndLocalFileIdentifier(newAssetGroupTemplate, out string guid, out long templateFileId); + AssetDatabase.TryGetGUIDAndLocalFileIdentifier(newAssetGroupTemplate.GetSchemaByType(typeof(ContentUpdateGroupSchema)), out string cugsguid, out long cugsFileId); + AssetDatabase.TryGetGUIDAndLocalFileIdentifier(newAssetGroupTemplate.GetSchemaByType(typeof(BundledAssetGroupSchema)), out string bagsguid, out long bagsFileId); + + var monoBehaviorMap = new Dictionary() + { + {bagsFileId.ToString(), "-6794523166426839361"}, + {cugsFileId.ToString(), "-1107740541918034454"}, + {templateFileId.ToString(), "11400000"}, + }; + + RemapMetaGuids(null, monoBehaviorMap); + AssetDatabase.Refresh(); + Shuffle(newAssetGroupTemplate.SchemaObjects); + EditorUtility.SetDirty(newAssetGroupTemplate); + AssetDatabase.SaveAssetIfDirty(newAssetGroupTemplate); + + var expectedSerializedTemplate = File.ReadAllText(GetExpectedPath("~SerializationTests_GroupTemplate.unity")); + var serializedTemplate = File.ReadAllText(AssetDatabase.GetAssetPath(newAssetGroupTemplate)); + AssertSerializedAreEqual(serializedTemplate, expectedSerializedTemplate); + } + + [TestCase] + public void TestProfileDataSourceSettingsSerialization() + { + var profileDataSourceSettings = ProfileDataSourceSettings.Create(ConfigFolder, "ProfileDataSourceSettings"); + // another profile is added when CCD_ENABLED is defined. We remove that to keep the test consistent. + DeleteCcdProfile(profileDataSourceSettings); + AddProfileGroupTypes(profileDataSourceSettings); + AddEnvironments(profileDataSourceSettings); + AssetDatabase.SaveAssetIfDirty(profileDataSourceSettings); + + RemapMetaGuids(null); + AssetDatabase.Refresh(); + + // shuffle + Shuffle(profileDataSourceSettings.profileGroupTypes); + + foreach (var groupType in profileDataSourceSettings.profileGroupTypes) + { + Shuffle(groupType.Variables); + } + Shuffle(profileDataSourceSettings.environments); + AssetDatabase.SaveAssetIfDirty(profileDataSourceSettings); + + var expectedProfileDataSourceSettings = File.ReadAllText(GetExpectedPath("~SerializationTests_ProfileDataSourceSettings.unity")); + var serializedProfileDataSourceSettings = File.ReadAllText(AssetDatabase.GetAssetPath(profileDataSourceSettings)); + AssertSerializedAreEqual(expectedProfileDataSourceSettings, serializedProfileDataSourceSettings); + } + + private void AddProfileGroupTypes(ProfileDataSourceSettings profileDataSourceSettings) + { + ProfileGroupType profileGroupType = new ProfileGroupType("testPrefix"); + profileGroupType.AddVariable(new ProfileGroupType.GroupTypeVariable(AddressableAssetSettings.kBuildPath, "Build/")); + profileGroupType.AddVariable(new ProfileGroupType.GroupTypeVariable(AddressableAssetSettings.kLoadPath, "https://example.com/a/")); + profileGroupType.AddVariable(new ProfileGroupType.GroupTypeVariable(ProfileDataSourceSettings.ENVIRONMENT_NAME, "production")); + profileDataSourceSettings.profileGroupTypes.Add(profileGroupType); + } + + private void DeleteCcdProfile(ProfileDataSourceSettings profileDataSourceSettings) + { + var toDelete = profileDataSourceSettings.profileGroupTypes.Find((x) => x.GroupTypePrefix == "Automatic"); + if (toDelete!= null) + { + profileDataSourceSettings.profileGroupTypes.Remove(toDelete); + } + } + + private void AddEnvironments(ProfileDataSourceSettings profileDataSourceSettings) + { + profileDataSourceSettings.environments = new List + { + new() {name = "production", id = "0214d8a4-af63-4534-814f-431d180926d6"}, + new() {name = "staging", id = "7c9f9aeb-2b9d-4258-bf52-0d2b6118ac39"}, + new() {name = "development", id = "a9ec150c-9a63-46ab-9a21-a3434afc7ab3"} + }; + } + + [TestCase] + public void TestAddressableAssetSettingsSerialization() + { + var group = Settings.CreateGroup("testGroup", false, false, false, + new List(), m_SchemaTypes.ToArray()); + Settings.DefaultGroup = Settings.groups.Find((g) => g.Default); + + AddProfile(); + AddInitializationObjects(); + AssetDatabase.SaveAssetIfDirty(Settings); + RemapMetaGuids(group); + AssetDatabase.Refresh(); + + // shuffle + foreach (var profile in Settings.profileSettings.profiles) + { + Shuffle(profile.values); + } + Shuffle(Settings.profileSettings.profiles); + Shuffle(Settings.profileSettings.profileEntryNames); + Shuffle(Settings.groups); + + EditorUtility.SetDirty(Settings); + AssetDatabase.SaveAssetIfDirty(Settings); + +#if CCD_3_OR_NEWER + var expectedSerializedSettings = File.ReadAllText(GetExpectedPath("~SerializationTests_AddressableAssetSettings.ccd3.unity")); +#elif ENABLE_CCD + var expectedSerializedSettings = File.ReadAllText(GetExpectedPath("~SerializationTests_AddressableAssetSettings.ccd2.unity")); +#else + var expectedSerializedSettings = File.ReadAllText(GetExpectedPath("~SerializationTests_AddressableAssetSettings.unity")); +#endif + var serializedSettings = File.ReadAllText(AssetDatabase.GetAssetPath(Settings)); + AssertSerializedAreEqual(expectedSerializedSettings, serializedSettings); + } + + private void AddProfile() + { + // ok so we need to add a profile here + Settings.profileSettings.AddProfile("testProfile", null); + } + + private void AddInitializationObjects() + { + Settings.AddInitializationObject(AssetDatabase.LoadAssetAtPath(GetFixturePath("InitFixture1.asset")) as IObjectInitializationDataProvider); + Settings.AddInitializationObject(AssetDatabase.LoadAssetAtPath(GetFixturePath("InitFixture2.asset")) as IObjectInitializationDataProvider); + } + + private void AddProfileValueMappings(Dictionary mappings) + { + foreach (var profile in Settings.profileSettings.profiles) + { + foreach (var value in profile.values) + { + switch (value.value) + { + case "[UnityEditor.EditorUserBuildSettings.activeBuildTarget]": + mappings.TryAdd(value.id, "0507cc90998e0a04f94da7055d0cc638"); + break; + case "[UnityEngine.AddressableAssets.Addressables.BuildPath]/[BuildTarget]": + mappings.TryAdd(value.id, "8f726afcd2923be469c05fdfc9963d44"); + break; + case "{UnityEngine.AddressableAssets.Addressables.RuntimePath}/[BuildTarget]": + mappings.TryAdd(value.id, "44693b9e8b6e8ab4e8bada109cd9e70d"); + break; + case "ServerData/[BuildTarget]": + mappings.TryAdd(value.id, "0d5680944a6e4bb47a35cd423b95cb36"); + break; + case "": + mappings.TryAdd(value.id, "42ec52cd576b8b9439d83010f809d5b5"); + break; + default: + throw new Exception($"unknown value in profile settings {value.value}"); + } + } + } + + } + + private void AddProfileEntryMappings(Dictionary mappings) + { + foreach (var profileEntryName in Settings.profileSettings.profileEntryNames) + { + switch (profileEntryName.ProfileName) + { + case "BuildTarget": + mappings.TryAdd(profileEntryName.m_Id, "0507cc90998e0a04f94da7055d0cc638"); + break; + case "Local.BuildPath": + mappings.TryAdd(profileEntryName.m_Id, "8f726afcd2923be469c05fdfc9963d44"); + break; + case "Local.LoadPath": + mappings.TryAdd(profileEntryName.m_Id, "44693b9e8b6e8ab4e8bada109cd9e70d"); + break; + case "Remote.BuildPath": + mappings.TryAdd(profileEntryName.m_Id, "0d5680944a6e4bb47a35cd423b95cb36"); + break; + case "Remote.LoadPath": + mappings.TryAdd(profileEntryName.m_Id, "42ec52cd576b8b9439d83010f809d5b5"); + break; + default: + throw new Exception($"unknown value in profile entry names {profileEntryName.ProfileName}"); + } + } + } + + private void RemapMetaGuids(AddressableAssetGroup group) + { + var remapped = new Dictionary(); + RemapMetaGuids(group, remapped); + } + + private void RemapMetaGuids(AddressableAssetGroup group, Dictionary remapped) + { + // we should be order by GUID + var defaultGroup = Settings.groups.Find((v) => v.Default); + // var secondGroup = Settings.groups.Find((v) => !v.Default); + var buildScriptFast = Settings.DataBuilders.Find((b) => b.name == "BuildScriptFastMode"); + var buildScriptPackedPlay = Settings.DataBuilders.Find((b) => b.name == "BuildScriptPackedPlayMode"); + var buildScriptPacked = Settings.DataBuilders.Find((b) => b.name == "BuildScriptPackedMode"); + var defaultProfile = Settings.profileSettings.profiles.Find((p) => p.profileName == "Default"); + var secondProfile = Settings.profileSettings.profiles.Find((p) => p.profileName != "Default"); + + // this mapping is from the GUID in the file (ex. ~testAddressableAssetSettings.unity) to the currently in use guid + // we replace all the current guids with our static guids so that we can compare the sorting + remapped.Add(GetMetaGuidFromObject(Settings), "3bf47571d203fa84d8dd31832e7c9339"); + remapped.Add(GetMetaGuidFromObject(buildScriptFast), "271b00b9e756a6d448f4ae7a08b88509"); + remapped.Add(GetMetaGuidFromObject(buildScriptPacked), "1694decfa7f2ffd4983ca3978e171998"); + remapped.Add(GetMetaGuidFromObject(buildScriptPackedPlay), "533ad9bddde5e2540a2a76c0203d0acb"); + + if (Settings?.DefaultGroup?.Guid != null) { + remapped.Add(Settings.DefaultGroup.Guid, "73831a73d82c83d4183d7e0477f7e745"); + } + if (defaultGroup != null) { + remapped.Add(GetMetaGuidFromObject(defaultGroup), "2308cd47506141c4aae9737b7d567105"); + } + if (Settings.GroupTemplateObjects.Count > 0) { + remapped.Add(GetMetaGuidFromObject(Settings.GroupTemplateObjects[0]), "97a492a095c0434448524d71cc7f0b0d"); + } + if (group != null) { + remapped.TryAdd(group.Guid, "16cd2736586abc441a3ef8bffa03b61f"); + remapped.TryAdd(GetMetaGuidFromObject(group), "e3940d5982f85734ca7aec5a9b7a90ee"); + remapped.Add(GetMetaGuidFromObject(group.Schemas[0]), "798716054e8a18a479c179e6d6f5ad2d"); + remapped.Add(GetMetaGuidFromObject(group.Schemas[1]), "7991916e228786548a8c905c2235f71f"); + } + if (defaultProfile != null) { + remapped.Add(defaultProfile.id, "5550bbbe2a7ee8c4f9d4600df43218be"); + } + if (secondProfile != null) { + remapped.Add(secondProfile.id, "c901f922cc200454b815a5b33a8427c6"); + } + AddProfileValueMappings(remapped); + AddProfileEntryMappings(remapped); + RemapFiles(remapped, ConfigFolder); + + // clear any caches + Settings.groups.Clear(); // this should be repopulated on AssetDatabase.Refresh() + Settings.ClearFindAssetEntryCache(); + } + + private void RemapFiles(Dictionary mappings, string dirName) + { + foreach (string dir in Directory.EnumerateDirectories(dirName)) + { + RemapFiles(mappings, dir); + } + + foreach (string file in Directory.EnumerateFiles(dirName)) + { + string line; + var inFile = file; + var outFile = $"{file}.tmp"; + var reader = new StreamReader(inFile); + var writer = new StreamWriter(outFile); + while ((line = reader.ReadLine()) != null) + { + foreach (var pair in mappings) + { + if (line.Contains(pair.Key)) + { + line = line.Replace(pair.Key, pair.Value); + } + } + writer.WriteLine(line); + } + reader.Close(); + writer.Close(); + File.Delete(inFile); + File.Move(outFile, inFile); + } + } + + private string GetMetaGuidFromObject(Object obj) + { + AssetDatabase.TryGetGUIDAndLocalFileIdentifier(obj, out string guid, out long templateFileId); + return guid; + } + + private void AssertSerializedAreEqual(string expected, string actual) + { + expected = expected.Replace("\r\n", "\n"); + actual = actual.Replace("\r\n", "\n"); + Assert.AreEqual(expected, actual); + } + } +} diff --git a/Tests/Editor/SerializationTests.cs.meta b/Tests/Editor/SerializationTests.cs.meta new file mode 100644 index 00000000..78bb4b28 --- /dev/null +++ b/Tests/Editor/SerializationTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 01dcf6e523a54f31918f41e8eb244804 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Tests/Runtime/AddressablesIntegrationTests.cs b/Tests/Runtime/AddressablesIntegrationTests.cs index e7313fe0..81fbaf23 100644 --- a/Tests/Runtime/AddressablesIntegrationTests.cs +++ b/Tests/Runtime/AddressablesIntegrationTests.cs @@ -6,6 +6,7 @@ using NUnit.Framework; using System.Collections; using System; +using System.IO; using System.Linq; using UnityEngine.AddressableAssets.ResourceLocators; using UnityEngine.ResourceManagement.ResourceLocations; diff --git a/Tests/Runtime/AddressablesIntegrationTestsImpl.cs b/Tests/Runtime/AddressablesIntegrationTestsImpl.cs index 295b96fe..2cef9e03 100644 --- a/Tests/Runtime/AddressablesIntegrationTestsImpl.cs +++ b/Tests/Runtime/AddressablesIntegrationTestsImpl.cs @@ -2368,6 +2368,67 @@ public IEnumerator DownloadDependencies_DoesNotRetainLoadedBundles_WithAutoRelea Assert.AreEqual(bundleCountBefore, AssetBundleProvider.AssetBundleCount); } + [UnityTest] + public IEnumerator DownloadDependencies_CannotLoadAssetWhenHandleNotReleased() + { +#if ENABLE_CACHING + if (string.IsNullOrEmpty(TypeName) || TypeName == "BuildScriptFastMode") + { + Assert.Ignore($"Skipping test {nameof(DownloadDependencies_CannotLoadAssetWhenHandleNotReleased)} for {TypeName}, AssetBundle based test."); + } + Caching.ClearCache(); + + yield return Init(); + int bundleCountBefore = AssetBundle.GetAllLoadedAssetBundles().Count(); + Assert.AreEqual(0, bundleCountBefore); + string label = AddressablesTestUtility.GetPrefabLabel("BASE"); + AsyncOperationHandle op = m_Addressables.DownloadDependenciesAsync(label); + yield return op; + Assert.IsTrue(op.IsValid()); + + var handle = m_Addressables.LoadAssetAsync>("test0BASE"); + yield return handle; + Assert.IsNull(handle.Result); +#if UNITY_EDITOR + Assert.AreEqual("Unable to load dependent bundle from location Assets/BuildScriptPackedMode_AssetsToDelete_BASE/test0BASE.prefab.\nRelease the handle returned by DownloadDependenciesAsync or call it with autoReleaseHandle set to true.", handle.OperationException.Message); +#else + Assert.AreEqual("Unable to load dependent bundle from location Assets/BuildScriptPackedMode_AssetsToDelete_BASE/test0BASE.prefab", handle.OperationException.Message); +#endif + handle.Release(); + op.Release(); +#else + Assert.Ignore(); + yield break; +#endif + } + + [UnityTest] + public IEnumerator DownloadDependencies_CanLoadAssetWhenHandleIsReleased() + { +#if ENABLE_CACHING + Caching.ClearCache(); + + yield return Init(); + int bundleCountBefore = AssetBundle.GetAllLoadedAssetBundles().Count(); + Assert.AreEqual(0, bundleCountBefore); + string label = AddressablesTestUtility.GetPrefabLabel("BASE"); + AsyncOperationHandle op = m_Addressables.DownloadDependenciesAsync(label); + yield return op; + Assert.IsTrue(op.IsValid()); + op.Release(); + + var handle = m_Addressables.LoadAssetAsync>("test0BASE"); + yield return handle; + Assert.IsTrue(handle.IsValid()); + Assert.IsNotNull(handle.Result); + handle.Release(); +#else + Assert.Ignore(); + yield break; +#endif + } + + [Test] public void AssetBundleProvider_CanSet_UnloadingBundles() { diff --git a/Tests/Runtime/AssetBundleProviderTests.cs b/Tests/Runtime/AssetBundleProviderTests.cs index 99f7900c..b489dd3b 100644 --- a/Tests/Runtime/AssetBundleProviderTests.cs +++ b/Tests/Runtime/AssetBundleProviderTests.cs @@ -38,7 +38,7 @@ string GetForceUWRAddrName(int i) #if UNITY_EDITOR internal override void Setup(AddressableAssetSettings settings, string tempAssetFolder) { - AddressableAssetGroup regGroup = settings.CreateGroup("localNoUWRGroup", false, false, true, + AddressableAssetGroup regGroup = settings.CreateGroup("localNoUWRGroup", true, false, true, new List(), typeof(BundledAssetGroupSchema)); regGroup.GetSchema().BundleNaming = BundledAssetGroupSchema.BundleNamingStyle.OnlyHash; diff --git a/package.json b/package.json index f9889276..27484a62 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "com.unity.addressables", "displayName": "Addressables", - "version": "2.0.8", + "version": "2.1.0", "unity": "2023.1", "description": "The Addressable Asset System allows the developer to ask for an asset via its address. Once an asset (e.g. a prefab) is marked \"addressable\", it generates an address which can be called from anywhere. Wherever the asset resides (local or remote), the system will locate it and its dependencies, then return it.\n\nUse 'Window->Asset Management->Addressables' to begin working with the system.\n\nAddressables use asynchronous loading to support loading from any location with any collection of dependencies. Whether you have been using direct references, traditional asset bundles, or Resource folders, addressables provide a simpler way to make your game more dynamic. Addressables simultaneously opens up the world of asset bundles while managing all the complexity.\n\nFor usage samples, see github.com/Unity-Technologies/Addressables-Sample", "keywords": [ @@ -12,7 +12,7 @@ "assetbundles" ], "dependencies": { - "com.unity.scriptablebuildpipeline": "2.1.2", + "com.unity.scriptablebuildpipeline": "2.1.3", "com.unity.modules.assetbundle": "1.0.0", "com.unity.modules.imageconversion": "1.0.0", "com.unity.modules.jsonserialize": "1.0.0", @@ -21,16 +21,16 @@ "com.unity.profiling.core": "1.0.2" }, "_upm": { - "changelog": "- Documents the behavior of using WaitForCompletion while a bundle is being unloaded.\n- Prevent a KeyNotFoundException from being logged to the console.\n- Fixed issue where a NullReferenceException occurs when using WaitForCompletion and the max number of concurrent requests is set to 1.\n- Fix error message to report not found when loading non-Addressable asset by guid\n- Fixed issue where there is missing asset data in the Addressables Profiler for binary catalogs.\n- Fixed an issue the error \"Cannot read BuildLayout header, BuildLayout has not open for a file\" would occur after a build\n- Added note about the limitations of the Check for Content Update Restrictions tool.\n- Fixed issue where an ArgumentException can occur when viewing multiple assets in the Addressables Profiler.\n\nFixed an issue where a broken script on any Addressable Asset would make it impossible to select Addressable Assets in the AssetReference inspector\nAdd migration upgrade prompt for legacy path pairs (ex. RemoteLoadPath)\nAdd logging of catalog and asset bundle http operations.\nAdd UI to trigger CCD management API http call logging (requires newer CCD package)\nCCD Automatic Profiles can now be one per-profile, rather than one per AddressableSettings instance\nCCD Manager is built when using the Build to CCD and the standard Build content menu" + "changelog": "- Fix \"Unable to verify target bucket for Remote Catalog: Not Found. Object could not be found\" error\n- Fixed caching to prevent unnecessary refreshing of bucket data.\n- Sort collections on serialization to prevent unecessary merge conflicts\n- Add warnings and documentation to make it clear you need to release the handle from LoadDependenciesAsync" }, "upmCi": { - "footprint": "2ce9b7197f5b2f035dcd5e61c40d4c6b3307f91e" + "footprint": "b4114ce971e4ee913e0e90b5d8b8bb86cb39529d" }, - "documentationUrl": "https://docs.unity3d.com/Packages/com.unity.addressables@2.0/manual/index.html", + "documentationUrl": "https://docs.unity3d.com/Packages/com.unity.addressables@2.1/manual/index.html", "repository": { "url": "https://github.cds.internal.unity3d.com/unity/Addressables.git", "type": "git", - "revision": "5244e030c3a00be55850b85103b7a3954b1108ba" + "revision": "ce0566e3aa96bd3b1648c405187751d0306cc931" }, "samples": [ {