using System.Collections; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text.RegularExpressions; using NUnit.Framework; #if UNITY_EDITOR using UnityEditor.AddressableAssets.Settings; using UnityEditor.AddressableAssets.Settings.GroupSchemas; #endif using UnityEngine; using UnityEngine.AddressableAssets; using UnityEngine.Networking; using UnityEngine.ResourceManagement; using UnityEngine.ResourceManagement.AsyncOperations; using UnityEngine.ResourceManagement.ResourceLocations; using UnityEngine.ResourceManagement.ResourceProviders; using UnityEngine.ResourceManagement.Util; using UnityEngine.SceneManagement; using UnityEngine.TestTools; namespace AddressableTests.SyncAddressables { public abstract class AssetBundleProviderTests : AddressablesTestFixture { protected string m_PrefabKey = "syncprefabkey"; protected string m_InvalidKey = "notarealkey"; protected string m_SceneKey = "syncscenekey"; const int kForceUWRBundleCount = 10; const int kMaxConcurrentRequests = 3; string GetForceUWRAddrName(int i) { return $"forceuwrasset{i}"; } #if UNITY_EDITOR internal override void Setup(AddressableAssetSettings settings, string tempAssetFolder) { AddressableAssetGroup regGroup = settings.CreateGroup("localNoUWRGroup", true, false, true, new List(), typeof(BundledAssetGroupSchema)); regGroup.GetSchema().BundleNaming = BundledAssetGroupSchema.BundleNamingStyle.OnlyHash; AddressableAssetGroup forceUWRGroup = settings.CreateGroup("ForceUWRGroup", false, false, true, new List(), typeof(BundledAssetGroupSchema)); forceUWRGroup.GetSchema().UseUnityWebRequestForLocalBundles = true; forceUWRGroup.GetSchema().BundleMode = BundledAssetGroupSchema.BundlePackingMode.PackSeparately; forceUWRGroup.GetSchema().BundleNaming = BundledAssetGroupSchema.BundleNamingStyle.OnlyHash; settings.MaxConcurrentWebRequests = kMaxConcurrentRequests; for (int i = 0; i < kForceUWRBundleCount; i++) { string s = GetForceUWRAddrName(i); string guid = CreatePrefab(tempAssetFolder + $"/{s}.prefab"); AddressableAssetEntry entry = settings.CreateOrMoveEntry(guid, forceUWRGroup); entry.address = s; } { string guid = CreatePrefab(tempAssetFolder + $"/testprefab.prefab"); AddressableAssetEntry entry = settings.CreateOrMoveEntry(guid, regGroup); entry.address = "testprefab"; } } #endif [SetUp] public void Setup() { #if ENABLE_CACHING Caching.ClearCache(); #endif MonoBehaviourCallbackHooks.DestroySingleton(); AssetBundle.UnloadAllAssetBundles(true); if (m_Addressables != null) m_Addressables.WebRequestOverride = null; } #if !UNITY_PS5 [UnityTest] public IEnumerator WhenUWRExceedsMaxLimit_UWRAreQueued() { List> l = Enumerable.Range(0, kForceUWRBundleCount).Select(x => m_Addressables.LoadAssetAsync(GetForceUWRAddrName(x))).ToList(); Assert.AreEqual(kMaxConcurrentRequests, WebRequestQueue.s_ActiveRequests.Count); Assert.AreEqual((kForceUWRBundleCount - kMaxConcurrentRequests), WebRequestQueue.s_QueuedOperations.Count); foreach (AsyncOperationHandle h in l) { yield return h; h.Release(); } } #endif #if !UNITY_PS5 [UnityTest] public IEnumerator WhenUWRExceedsMaxLimit_CompletesSynchronously() { List> loadOps = Enumerable.Range(0, kForceUWRBundleCount).Select(x => m_Addressables.LoadAssetAsync(GetForceUWRAddrName(x))).ToList(); AsyncOperationHandle lastHandle = loadOps[kForceUWRBundleCount - 1]; lastHandle.WaitForCompletion(); lastHandle.Release(); for (int i = 0; i < kForceUWRBundleCount - 1; i++) { AsyncOperationHandle handle = loadOps[i]; yield return loadOps[i]; handle.Release(); } } #endif #if !UNITY_PS5 [Test] public void WhenUWRIsUsed_CompletesSynchronously() { AsyncOperationHandle h = m_Addressables.LoadAssetAsync(GetForceUWRAddrName(0)); h.WaitForCompletion(); h.Release(); } #endif #if !UNITY_PS5 [UnityTest] public IEnumerator WhenAssetBundleIsLocal_AndForceUWRIsEnabled_UWRIsUsed() { AsyncOperationHandle h = m_Addressables.LoadAssetAsync(GetForceUWRAddrName(0)); Assert.AreEqual(1, WebRequestQueue.s_ActiveRequests.Count); yield return h; Assert.NotNull(h.Result); h.Release(); } #endif #if !UNITY_PS5 /* * this is verifying code coverage for the first shortcircuit in AssetBundleProvider::BeginWebRequestOperation. If the bundle completes immediately we short-circuit. This sometimes * happens in a CI and sometimes does not. This test verifies that we always test the intial short-circuit route. * It does this by enqueuing the request. Waiting for the webrequest to finish, and THEN adding the web request handlers which immediately trigger the callback. */ [UnityTest] public IEnumerator WhenAssetBundleIsComplete_ReturnImmediately() { var bundleName = GetForceUWRAddrName(0); AssetBundleResource abr = new AssetBundleResource(); IList locations = new List(); m_Addressables.GetResourceLocations(GetForceUWRAddrName(0), typeof(GameObject), out locations); // the test will fail if the dependencies change so just validating here to make future troubleshooting easier Assert.AreEqual(1, locations.Count); Assert.AreEqual(2, locations[0].Dependencies.Count); var providerOp = new ProviderOperation(); providerOp.Init(m_Addressables.ResourceManager, new BundledAssetProvider(), locations[0].Dependencies[0], new AsyncOperationHandle>()); abr.m_ProvideHandle = new ProvideHandle(m_Addressables.ResourceManager, providerOp); abr.m_Options = new AssetBundleRequestOptions() { BundleName = bundleName, UseUnityWebRequestForLocalBundles = true, }; var op = new AsyncOperationHandle(providerOp, 1); var path = locations[0].Dependencies[0].InternalId; if (Application.platform != RuntimePlatform.Android) { path = Path.GetFullPath(locations[0].Dependencies[0].InternalId); path = "file:///" + path; } var queueOp = abr.EnqueueWebRequest(path); abr.m_WebRequestQueueOperation = queueOp; // we always want the web request to complete before we add the request handlers yield return queueOp.Result; abr.AddBeginWebRequestHandler(queueOp); yield return abr.m_ProvideHandle; op.Release(); } #endif #if !UNITY_PS5 [UnityTest] public IEnumerator WhenWebRequestOverrideIsSet_CallbackIsCalled_AssetBundleProvider() { bool webRequestOverrideCalled = false; m_Addressables.WebRequestOverride = request => webRequestOverrideCalled = true; var prev = LogAssert.ignoreFailingMessages; LogAssert.ignoreFailingMessages = true; var nonExistingPath = "http://127.0.0.1/non-existing-bundle"; var loc = new ResourceLocationBase(nonExistingPath, nonExistingPath, typeof(AssetBundleProvider).FullName, typeof(AssetBundleResource)); loc.Data = new AssetBundleRequestOptions(); var h = m_Addressables.ResourceManager.ProvideResource(loc); yield return h; if (h.IsValid()) h.Release(); LogAssert.ignoreFailingMessages = prev; Assert.IsTrue(webRequestOverrideCalled); } #endif #if !UNITY_PS5 [UnityTest] public IEnumerator WhenWebRequestFails_RetriesCorrectAmount_AssetBundleProvider() { var prev = LogAssert.ignoreFailingMessages; LogAssert.ignoreFailingMessages = true; int retries = 0; m_Addressables.WebRequestOverride = request => { retries++; }; var nonExistingPath = "https://127.0.0.1/non-existing-bundle"; var loc = new ResourceLocationBase(nonExistingPath, nonExistingPath, typeof(AssetBundleProvider).FullName, typeof(AssetBundleResource)); var d = new AssetBundleRequestOptions(); d.BundleName = "non-existing-bundle"; d.RetryCount = 3; d.BundleName = "fakebundlename"; loc.Data = d; LogAssert.Expect(LogType.Log, new Regex(@"^(Web request failed, retrying \(0/3)")); LogAssert.Expect(LogType.Log, new Regex(@"^(Web request failed, retrying \(1/3)")); LogAssert.Expect(LogType.Log, new Regex(@"^(Web request failed, retrying \(2/3)")); var h = m_Addressables.ResourceManager.ProvideResource(loc); yield return h; if (h.IsValid()) h.Release(); LogAssert.ignoreFailingMessages = prev; // apparently we do 1 attempt, then 3 retries Assert.AreEqual(4, retries); } #endif #if !UNITY_PS5 [Test] [TestCase("Relative/Local/Path","Relative/Local/Path", true, false, RuntimePlatform.LinuxEditor, RuntimePlatform.LinuxPlayer, RuntimePlatform.OSXEditor, RuntimePlatform.OSXPlayer)] [TestCase("Relative\\Local\\Path","Relative\\Local\\Path", true, false, RuntimePlatform.WindowsEditor, RuntimePlatform.WindowsPlayer, RuntimePlatform.GameCoreXboxSeries)] [TestCase("Relative\\Local\\Path","file:///<>/Relative/Local/Path", true, true, RuntimePlatform.WindowsEditor, RuntimePlatform.WindowsPlayer, RuntimePlatform.GameCoreXboxSeries)] [TestCase("Relative/Local/Path","file:///<>/Relative/Local/Path", true, true, RuntimePlatform.LinuxEditor, RuntimePlatform.LinuxPlayer, RuntimePlatform.OSXEditor, RuntimePlatform.OSXPlayer)] [TestCase("http://127.0.0.1/Web/Path","http://127.0.0.1/Web/Path", false, true)] [TestCase("jar:file:///Local/Path", "jar:file:///Local/Path", true, false, RuntimePlatform.Android)] [TestCase("jar:file:///Local/Path", "jar:file:///Local/Path",true, true, RuntimePlatform.Android)] public void AssetBundleLoadPathsCorrectForGetLoadInfo(string internalId, string expectedPath, bool isLocal, bool useUnityWebRequestForLocalBundles, params RuntimePlatform[] applicablePlatforms) { // if not platforms are specified all are used if (applicablePlatforms.Length > 0 && !applicablePlatforms.Contains(Application.platform)) Assert.Ignore($"Skipping test {TestContext.CurrentContext.Test.Name} due to platform not being in the list of applicable platforms."); var loc = new ResourceLocationBase("dummy", internalId, "dummy", typeof(Object)); loc.Data = new AssetBundleRequestOptions {UseUnityWebRequestForLocalBundles = useUnityWebRequestForLocalBundles}; ProviderOperation op = new ProviderOperation(); op.Init(m_Addressables.ResourceManager, null, loc, new AsyncOperationHandle>()); ProvideHandle h = new ProvideHandle(m_Addressables.ResourceManager, op); AssetBundleResource.GetLoadInfo(h, out AssetBundleResource.LoadType loadType, out string path); var expectedLoadType = isLocal ? useUnityWebRequestForLocalBundles ? AssetBundleResource.LoadType.Web : AssetBundleResource.LoadType.Local : AssetBundleResource.LoadType.Web; Assert.AreEqual(expectedLoadType, loadType, "Incorrect load type found for internalId " + internalId); // we can't do this in the definition so we have to do it here var relativeBase = Path.GetFullPath("."); if (applicablePlatforms.Contains(RuntimePlatform.WindowsEditor)) { relativeBase = relativeBase.Replace("\\", "/"); } expectedPath = expectedPath.Replace("<>", relativeBase); Assert.AreEqual(expectedPath, path); } #endif [Test] [TestCase("http://www.example.com/index with space.html", "http://www.example.com/index%20with%20space.html")] [TestCase("http://www.example.com/index%20with%20space.html", "http://www.example.com/index%20with%20space.html")] [TestCase("http://www.example.com/index with space :andcolons.html", "http://www.example.com/index%20with%20space%20:andcolons.html")] public void CreateWebRequestReturnsCorrectlySpacedUrlPerPlatform(string inputUrl, string expectedUrl) { var abr = new AssetBundleResource(); var webRequest = abr.CreateWebRequest(inputUrl); Assert.AreEqual(webRequest.url, expectedUrl); } #if !UNITY_PS5 [UnityTest] public IEnumerator LoadBundleAsync_WithUnfinishedUnload_WaitsForUnloadAndCompletes() { var h = m_Addressables.LoadAssetAsync("testprefab"); yield return h; Assert.IsNotNull(h.Result); h.Release(); h = m_Addressables.LoadAssetAsync("testprefab"); yield return h; Assert.IsNotNull(h.Result); h.Release(); } [UnityTest] public IEnumerator LoadBundleSync_WithUnfinishedUnload_WaitsForUnloadAndCompletes() { var h = m_Addressables.LoadAssetAsync("testprefab"); yield return h; Assert.IsNotNull(h.Result); h.Release(); h = m_Addressables.LoadAssetAsync("testprefab"); h.WaitForCompletion(); Assert.IsNotNull(h.Result); h.Release(); } #endif [Test] // Only testing against important errors instead of full list [TestCase("", true)] [TestCase("Unknown error", true)] [TestCase("Request aborted", false)] [TestCase("Unable to write data", false)] public void UnityWebRequestResult_ShouldRetryReturnsExpected(string error, bool expected) { UnityWebRequestResult rst = new UnityWebRequestResult(new UnityWebRequest()); rst.Error = error; bool result = rst.ShouldRetryDownloadError(); Assert.AreEqual(expected, result, "Unexpected retry value for the input error."); } } #if UNITY_EDITOR class AssetBundleProviderTests_PackedPlaymodeMode : AssetBundleProviderTests { protected override TestBuildScriptMode BuildScriptMode { get { return TestBuildScriptMode.PackedPlaymode; } } } #endif [UnityPlatform(exclude = new[] {RuntimePlatform.WindowsEditor, RuntimePlatform.OSXEditor, RuntimePlatform.LinuxEditor})] class AssetBundleProviderTests_PackedMode : AssetBundleProviderTests { protected override TestBuildScriptMode BuildScriptMode { get { return TestBuildScriptMode.Packed; } } } }