using System; using System.Collections.Generic; using System.IO; using System.Linq; using UnityEditor.TestTools.TestRunner.Api; using UnityEngine; using UnityEngine.TestRunner.NUnitExtensions.Runner; namespace UnityEditor.TestTools.TestRunner { /// /// Handles deserialization of TestSettings from a provided json file path. /// internal class TestSettingsDeserializer : ITestSettingsDeserializer { private static readonly SettingsMap[] s_SettingsMapping = { new SettingsMap("scriptingBackend", (settings, value) => settings.scriptingBackend = value), new SettingsMap("architecture", (settings, value) => settings.Architecture = value), new SettingsMap("apiProfile", (settings, value) => settings.apiProfile = value), new SettingsMap("appleEnableAutomaticSigning", (settings, value) => settings.appleEnableAutomaticSigning = value), new SettingsMap("appleDeveloperTeamID", (settings, value) => settings.appleDeveloperTeamID = value), new SettingsMap("iOSManualProvisioningProfileType", (settings, value) => settings.iOSManualProvisioningProfileType = value), new SettingsMap("iOSManualProvisioningProfileID", (settings, value) => settings.iOSManualProvisioningProfileID = value), new SettingsMap("iOSTargetSDK", (settings, value) => settings.iOSTargetSDK = value), new SettingsMap("tvOSManualProvisioningProfileType", (settings, value) => settings.tvOSManualProvisioningProfileType = value), new SettingsMap("tvOSManualProvisioningProfileID", (settings, value) => settings.tvOSManualProvisioningProfileID = value), new SettingsMap("tvOSTargetSDK", (settings, value) => settings.tvOSTargetSDK = value), new SettingsMap("playerGraphicsAPI", (settings, value) => { settings.autoGraphicsAPIs = false; settings.playerGraphicsAPIs = new[] {value}; }), new SettingsMap("androidBuildAppBundle", (settings, value) => { settings.androidBuildAppBundle = value; }), new SettingsMap>("ignoreTests", (settings, list) => { settings.ignoreTests = list.Select(item => { var dictionary = (Dictionary)item; if (dictionary.ContainsKey("test") && dictionary.ContainsKey("ignoreComment")) { return new IgnoreTest() { test = dictionary["test"] as string, ignoreComment = dictionary["ignoreComment"] as string }; } throw new Exception("Wrong format for ignore test. Expected \"test\" and \"ignoreComment\"."); }).ToArray(); }), new SettingsMap>("featureFlags", (settings, dictionary) => { var converted = dictionary.ToDictionary(pair => pair.Key, pair => (bool)pair.Value); var featureFlags = new FeatureFlags(); if (converted.ContainsKey("fileCleanUpCheck")) { featureFlags.fileCleanUpCheck = converted["fileCleanUpCheck"]; } if (converted.ContainsKey("strictDomainReload")) { featureFlags.strictDomainReload = converted["strictDomainReload"]; } if (converted.ContainsKey("requiresSplashScreen")) { featureFlags.requiresSplashScreen = converted["requiresSplashScreen"]; } if (converted.ContainsKey("disableNestedEnumeratorBugfix")) { featureFlags.disableNestedEnumeratorBugfix = converted["disableNestedEnumeratorBugfix"]; } settings.featureFlags = featureFlags; }), #if UNITY_2023_2_OR_NEWER new SettingsMap("webGLClientBrowserType", (settings, value) => settings.webGLClientBrowserType = value), new SettingsMap("webGLClientBrowserPath", (settings, value) => settings.webGLClientBrowserPath = value), #endif }; private readonly Func m_TestSettingsFactory; public TestSettingsDeserializer(Func testSettingsFactory) { m_TestSettingsFactory = testSettingsFactory; } public ITestSettings GetSettingsFromJsonFile(string jsonFilePath) { var text = File.ReadAllText(jsonFilePath); var settingsDictionary = Json.Deserialize(text) as Dictionary; var testSettings = m_TestSettingsFactory(); if (settingsDictionary == null) { return testSettings; } foreach (var settingsMap in s_SettingsMapping) { if (!settingsDictionary.ContainsKey(settingsMap.Key)) { continue; } if (settingsMap.Type.IsEnum) { SetEnumValue(settingsMap.Key, settingsDictionary[settingsMap.Key], settingsMap.Type, value => settingsMap.ApplyToSettings(testSettings, value)); } else { SetValue(settingsMap.Key, settingsDictionary[settingsMap.Key], settingsMap.Type, value => settingsMap.ApplyToSettings(testSettings, value)); } } return testSettings; } private abstract class SettingsMap { public string Key { get; } public Type Type { get; } protected SettingsMap(string key, Type type) { Key = key; Type = type; } public abstract void ApplyToSettings(ITestSettings settings, object value); } private class SettingsMap : SettingsMap { private Action m_Setter; public SettingsMap(string key, Action setter) : base(key, typeof(T)) { m_Setter = setter; } public override void ApplyToSettings(ITestSettings settings, object value) { m_Setter(settings, (T)value); } } private static void SetEnumValue(string key, object value, Type type, Action setter) { object enumValue; if (TryGetEnum(value as string, type, out enumValue)) { setter(enumValue); return; } var acceptedValues = string.Join(", ", Enum.GetValues(type).OfType().Select(val => val.ToString()).ToArray()); Debug.LogFormat("Could not convert '{0}' argument '{1}' to a valid {2}. Accepted values: {3}.", key, value, type.Name, acceptedValues); } private static bool TryGetEnum(string value, Type type, out object enumValue) { try { enumValue = Enum.Parse(type, value, true); return true; } catch (Exception) { enumValue = null; return false; } } private static void SetValue(string key, object value, Type type, Action setter) { if (type.IsInstanceOfType(value)) { setter(value); return; } Debug.LogFormat("Could not convert '{0}' argument '{1}' to a valid {2}.", key, value, type.Name); } } }