diff --git a/Assets/AltTester/Runtime/AltDriver/AltDriver.cs b/Assets/AltTester/Runtime/AltDriver/AltDriver.cs index f159a9213..628cc5c6a 100644 --- a/Assets/AltTester/Runtime/AltDriver/AltDriver.cs +++ b/Assets/AltTester/Runtime/AltDriver/AltDriver.cs @@ -25,605 +25,57 @@ You should have received a copy of the GNU General Public License namespace AltTester.AltTesterUnitySDK.Driver { - public enum By + public class AltDriver: AltDriverUnity { - TAG, LAYER, NAME, COMPONENT, PATH, ID, TEXT - } - - public class AltDriver - { - private static readonly NLog.Logger logger = DriverLogManager.Instance.GetCurrentClassLogger(); - private readonly IDriverCommunication communicationHandler; - private static object driverLock = new object(); - public static readonly string VERSION = "2.2.2"; - - public IDriverCommunication CommunicationHandler { get { return communicationHandler; } } - - /// - /// Initiates AltDriver and begins connection with the instrumented Unity application through to AltServer. - /// - /// The IP or hostname AltTester® Server is listening on. - /// The port AltTester® Server is listening on. - /// If true it enables driver commands logging to log file and Unity. - /// The connect timeout in seconds. - /// The name of the Unity application. + public AltDriver(string host = "127.0.0.1", int port = 13000, string appName = "__default__", bool enableLogging = false, int connectTimeout = 60, string platform = "unknown", string platformVersion = "unknown", string deviceInstanceId = "unknown", string appId = "unknown", string driverType = "SDK") - { - lock (driverLock) - { -#if UNITY_EDITOR || ALTTESTER - var defaultLevels = new Dictionary { { AltLogger.File, AltLogLevel.Debug }, { AltLogger.Unity, AltLogLevel.Debug } }; -#else - var defaultLevels = new Dictionary { { AltLogger.File, AltLogLevel.Debug }, { AltLogger.Console, AltLogLevel.Debug } }; -#endif - - - if (enableLogging) - { - DriverLogManager.SetupAltDriverLogging(defaultLevels); - } - else - { - DriverLogManager.StopLogging(); - } - - logger.Debug( - "Connecting to AltTester(R) on host: '{0}', port: '{1}', appName: '{2}', platform: '{3}', platformVersion: '{4}', deviceInstanceId: '{5}' and driverType: '{6}'.", - host, - port, - appName, - platform, - platformVersion, - deviceInstanceId, - driverType - ); - while (true) - { - communicationHandler = new DriverCommunicationHandler(host, port, connectTimeout, appName, platform, platformVersion, deviceInstanceId, appId, driverType); - communicationHandler.Connect(); - try - { - checkServerVersion(); - break; - } - catch (NullReferenceException)//There is a strange situation when sometimes checkServerVersion throws that command params is null. I investigated but didn't find the cause. - { - communicationHandler.Close(); - } - } - } - } - - private void splitVersion(string version, out string major, out string minor) - { - var parts = version.Split(new[] { "." }, StringSplitOptions.None); - major = parts[0]; - minor = parts.Length > 1 ? parts[1] : string.Empty; - } - - private void checkServerVersion() - { - string serverVersion = GetServerVersion(); - - string majorServer; - string majorDriver; - string minorDriver; - string minorServer; - - splitVersion(serverVersion, out majorServer, out minorServer); - splitVersion(VERSION, out majorDriver, out minorDriver); - - int serverMajor, serverMinor; - - serverMajor = int.Parse(majorServer); - serverMinor = int.Parse(minorServer); - - bool isSupported = - (serverMajor == 2 && serverMinor == 2) || // Server version 2.2.x - (serverMajor == 1 && serverMinor == 0); // Server version 1.0.0 - - if (!isSupported) - { - string message = $"Version mismatch. AltDriver version is {VERSION}. AltTester(R) version is {serverVersion}."; - logger.Warn(message); - } - } - - public void Stop() - { - communicationHandler.Close(); - } - - public void ResetInput() - { - new AltResetInput(communicationHandler).Execute(); - } - - public void SetCommandResponseTimeout(int commandTimeout) - { - communicationHandler.SetCommandTimeout(commandTimeout); - } - - public void SetDelayAfterCommand(float delay) - { - communicationHandler.SetDelayAfterCommand(delay); - } - - public float GetDelayAfterCommand() - { - return communicationHandler.GetDelayAfterCommand(); - } - - public string GetServerVersion() - { - string serverVersion = new AltGetServerVersion(communicationHandler).Execute(); - communicationHandler.SleepFor(communicationHandler.GetDelayAfterCommand()); - return serverVersion; - } - - public void SetLogging(bool enableLogging) - { - if (enableLogging) - DriverLogManager.ResumeLogging(); - else - DriverLogManager.StopLogging(); - } - - public void LoadScene(string scene, bool loadSingle = true) - { - new AltLoadScene(communicationHandler, scene, loadSingle).Execute(); - communicationHandler.SleepFor(communicationHandler.GetDelayAfterCommand()); - } - - public void UnloadScene(string scene) - { - new AltUnloadScene(communicationHandler, scene).Execute(); - communicationHandler.SleepFor(communicationHandler.GetDelayAfterCommand()); - } - - public List GetAllLoadedScenes() - { - var sceneList = new AltGetAllLoadedScenes(communicationHandler).Execute(); - communicationHandler.SleepFor(communicationHandler.GetDelayAfterCommand()); - return sceneList; + : base(host, port, appName, enableLogging, connectTimeout, platform, platformVersion, deviceInstanceId, appId, driverType) + { } public List FindObjects(By by, string value, By cameraBy = By.NAME, string cameraValue = "", bool enabled = true) { - var listOfObjects = new AltFindObjects(communicationHandler, by, value, cameraBy, cameraValue, enabled).Execute(); - communicationHandler.SleepFor(communicationHandler.GetDelayAfterCommand()); - return listOfObjects; + return base.FindObjects(new AltBy(by, value), new AltBy(cameraBy, cameraValue), enabled); } public List FindObjectsWhichContain(By by, string value, By cameraBy = By.NAME, string cameraValue = "", bool enabled = true) { - var listOfObjects = new AltFindObjectsWhichContain(communicationHandler, by, value, cameraBy, cameraValue, enabled).Execute(); - communicationHandler.SleepFor(communicationHandler.GetDelayAfterCommand()); - return listOfObjects; + return base.FindObjectsWhichContain(new AltBy(by, value), new AltBy(cameraBy, cameraValue), enabled); } public AltObject FindObject(By by, string value, By cameraBy = By.NAME, string cameraValue = "", bool enabled = true) { - var findObject = new AltFindObject(communicationHandler, by, value, cameraBy, cameraValue, enabled).Execute(); - communicationHandler.SleepFor(communicationHandler.GetDelayAfterCommand()); - return findObject; + return base.FindObject(new AltBy(by, value), new AltBy(cameraBy, cameraValue), enabled); } public AltObject FindObjectWhichContains(By by, string value, By cameraBy = By.NAME, string cameraValue = "", bool enabled = true) { - var findObject = new AltFindObjectWhichContains(communicationHandler, by, value, cameraBy, cameraValue, enabled).Execute(); - communicationHandler.SleepFor(communicationHandler.GetDelayAfterCommand()); - return findObject; - } - - public void SetTimeScale(float timeScale) - { - new AltSetTimeScale(communicationHandler, timeScale).Execute(); - communicationHandler.SleepFor(communicationHandler.GetDelayAfterCommand()); - } - - public float GetTimeScale() - { - var timeScale = new AltGetTimeScale(communicationHandler).Execute(); - communicationHandler.SleepFor(communicationHandler.GetDelayAfterCommand()); - return timeScale; - } - - public T CallStaticMethod(string typeName, string methodName, string assemblyName, - object[] parameters, string[] typeOfParameters = null) - { - var result = new AltCallStaticMethod(communicationHandler, typeName, methodName, parameters, typeOfParameters, assemblyName).Execute(); - communicationHandler.SleepFor(communicationHandler.GetDelayAfterCommand()); - return result; - } - - public T GetStaticProperty(string componentName, string propertyName, string assemblyName, int maxDepth = 2) - { - var propertyValue = new AltGetStaticProperty(communicationHandler, componentName, propertyName, assemblyName, maxDepth).Execute(); - communicationHandler.SleepFor(communicationHandler.GetDelayAfterCommand()); - return propertyValue; - } - - public void SetStaticProperty(string componentName, string propertyName, string assemblyName, object updatedProperty) - { - new AltSetStaticProperty(communicationHandler, componentName, propertyName, assemblyName, updatedProperty).Execute(); - communicationHandler.SleepFor(communicationHandler.GetDelayAfterCommand()); - } - - public void DeletePlayerPref() - { - new AltDeletePlayerPref(communicationHandler).Execute(); - communicationHandler.SleepFor(communicationHandler.GetDelayAfterCommand()); - } - - public void DeleteKeyPlayerPref(string keyName) - { - new AltDeleteKeyPlayerPref(communicationHandler, keyName).Execute(); - communicationHandler.SleepFor(communicationHandler.GetDelayAfterCommand()); - } - - public void SetKeyPlayerPref(string keyName, int valueName) - { - new AltSetKeyPLayerPref(communicationHandler, keyName, valueName).Execute(); - communicationHandler.SleepFor(communicationHandler.GetDelayAfterCommand()); - } - - public void SetKeyPlayerPref(string keyName, float valueName) - { - new AltSetKeyPLayerPref(communicationHandler, keyName, valueName).Execute(); - communicationHandler.SleepFor(communicationHandler.GetDelayAfterCommand()); - } - - public void SetKeyPlayerPref(string keyName, string valueName) - { - new AltSetKeyPLayerPref(communicationHandler, keyName, valueName).Execute(); - communicationHandler.SleepFor(communicationHandler.GetDelayAfterCommand()); - } - - public int GetIntKeyPlayerPref(string keyName) - { - var keyValue = new AltGetIntKeyPlayerPref(communicationHandler, keyName).Execute(); - communicationHandler.SleepFor(communicationHandler.GetDelayAfterCommand()); - return keyValue; - } - - public float GetFloatKeyPlayerPref(string keyName) - { - var keyValue = new AltGetFloatKeyPlayerPref(communicationHandler, keyName).Execute(); - communicationHandler.SleepFor(communicationHandler.GetDelayAfterCommand()); - return keyValue; - } - - public string GetStringKeyPlayerPref(string keyName) - { - var keyValue = new AltGetStringKeyPlayerPref(communicationHandler, keyName).Execute(); - communicationHandler.SleepFor(communicationHandler.GetDelayAfterCommand()); - return keyValue; - } - - public string GetCurrentScene() - { - var sceneName = new AltGetCurrentScene(communicationHandler).Execute(); - communicationHandler.SleepFor(communicationHandler.GetDelayAfterCommand()); - return sceneName; - } - - /// - /// Simulates a swipe action between two points. - /// - /// Coordinates of the screen where the swipe begins - /// Coordinates of the screen where the swipe ends - /// The time measured in seconds to move the mouse from start to end location. Defaults to 0.1. - /// If set wait for command to finish. Defaults to True. - public void Swipe(AltVector2 start, AltVector2 end, float duration = 0.1f, bool wait = true) - { - new AltSwipe(communicationHandler, start, end, duration, wait).Execute(); - communicationHandler.SleepFor(communicationHandler.GetDelayAfterCommand()); - } - - /// - /// Simulates a multipoint swipe action. - /// - /// A list of positions on the screen where the swipe be made. - /// The time measured in seconds to swipe from first position to the last position. Defaults to 0.1. - /// If set wait for command to finish. Defaults to True. - public void MultipointSwipe(AltVector2[] positions, float duration = 0.1f, bool wait = true) - { - new AltMultipointSwipe(communicationHandler, positions, duration, wait).Execute(); - communicationHandler.SleepFor(communicationHandler.GetDelayAfterCommand()); - } - - /// - /// Simulates holding left click button down for a specified amount of time at given coordinates. - /// - /// The coordinates where the button is held down. - /// The time measured in seconds to keep the button down. - /// If set wait for command to finish. Defaults to True. - public void HoldButton(AltVector2 coordinates, float duration, bool wait = true) - { - Swipe(coordinates, coordinates, duration, wait); - } - - /// - /// Simulates key press action in your app. - /// - /// The key code of the key simulated to be pressed. - /// A value between [-1,1] used for joysticks to indicate how hard the button was pressed. Defaults to 1. - /// The time measured in seconds from the key press to the key release. Defaults to 0.1 - /// If set wait for command to finish. Defaults to True. - public void PressKey(AltKeyCode keyCode, float power = 1, float duration = 0.1f, bool wait = true) - { - AltKeyCode[] keyCodes = { keyCode }; - PressKeys(keyCodes, power, duration, wait); - } - - /// - /// Simulates multiple keys pressed action in your app. - /// - /// The list of key codes of the keys simulated to be pressed. - /// A value between [-1,1] used for joysticks to indicate how hard the button was pressed. Defaults to 1. - /// The time measured in seconds from the key press to the key release. Defaults to 0.1 - /// If set wait for command to finish. Defaults to True. - public void PressKeys(AltKeyCode[] keyCodes, float power = 1, float duration = 0.1f, bool wait = true) - { - new AltPressKeys(communicationHandler, keyCodes, power, duration, wait).Execute(); - communicationHandler.SleepFor(communicationHandler.GetDelayAfterCommand()); - } - - public void KeyDown(AltKeyCode keyCode, float power = 1) - { - AltKeyCode[] keyCodes = { keyCode }; - KeysDown(keyCodes, power); - } - - /// - /// Simulates multiple keys down action in your app. - /// - /// The key codes of the keys simulated to be down. - /// A value between [-1,1] used for joysticks to indicate how hard the button was pressed. Defaults to 1. - public void KeysDown(AltKeyCode[] keyCodes, float power = 1) - { - new AltKeysDown(communicationHandler, keyCodes, power).Execute(); - communicationHandler.SleepFor(communicationHandler.GetDelayAfterCommand()); - } - - public void KeyUp(AltKeyCode keyCode) - { - AltKeyCode[] keyCodes = { keyCode }; - KeysUp(keyCodes); - } - - /// - /// Simulates multiple keys up action in your app. - /// - /// The key codes of the keys simulated to be up. - public void KeysUp(AltKeyCode[] keyCodes) - { - new AltKeysUp(communicationHandler, keyCodes).Execute(); - communicationHandler.SleepFor(communicationHandler.GetDelayAfterCommand()); - } - - /// - /// Simulate mouse movement in your app. - /// - /// The screen coordinates - /// The time measured in seconds to move the mouse from the current mouse position to the set coordinates. Defaults to 0.1f - /// If set wait for command to finish. Defaults to True. - public void MoveMouse(AltVector2 coordinates, float duration = 0.1f, bool wait = true) - { - new AltMoveMouse(communicationHandler, coordinates, duration, wait).Execute(); - communicationHandler.SleepFor(communicationHandler.GetDelayAfterCommand()); - } - - /// - /// Simulate scroll action in your app. - /// - /// Set how fast to scroll. Positive values will scroll up and negative values will scroll down. Defaults to 1 - /// The duration of the scroll in seconds. Defaults to 0.1 - /// If set wait for command to finish. Defaults to True. - public void Scroll(float speed = 1, float duration = 0.1f, bool wait = true) - { - new AltScroll(communicationHandler, speed, 0, duration, wait).Execute(); - communicationHandler.SleepFor(communicationHandler.GetDelayAfterCommand()); - } - - /// - /// Simulate scroll action in your app. - /// - /// Set how fast to scroll. X is horizontal and Y is vertical. Defaults to 1 - /// The duration of the scroll in seconds. Defaults to 0.1 - /// If set wait for command to finish. Defaults to True. - public void Scroll(AltVector2 scrollValue, float duration = 0.1f, bool wait = true) - { - new AltScroll(communicationHandler, scrollValue.y, scrollValue.x, duration, wait).Execute(); - communicationHandler.SleepFor(communicationHandler.GetDelayAfterCommand()); - } - - /// - /// Tap at screen coordinates - /// - /// The screen coordinates - /// Number of taps - /// Interval between taps in seconds - /// If set wait for command to finish. Defaults to True. - public void Tap(AltVector2 coordinates, int count = 1, float interval = 0.1f, bool wait = true) - { - new AltTapCoordinates(communicationHandler, coordinates, count, interval, wait).Execute(); - communicationHandler.SleepFor(communicationHandler.GetDelayAfterCommand()); - } - - /// - /// Click at screen coordinates - /// - /// The screen coordinates - /// Number of clicks. - /// Interval between clicks in seconds - /// If set wait for command to finish. Defaults to True. - public void Click(AltVector2 coordinates, int count = 1, float interval = 0.1f, bool wait = true) - { - new AltClickCoordinates(communicationHandler, coordinates, count, interval, wait).Execute(); - communicationHandler.SleepFor(communicationHandler.GetDelayAfterCommand()); - } - - /// - /// Simulates device rotation action in your app. - /// - /// The linear acceleration of a device. - /// How long the rotation will take in seconds. Defaults to 0.1. - /// If set wait for command to finish. Defaults to True. - public void Tilt(AltVector3 acceleration, float duration = 0.1f, bool wait = true) - { - new AltTilt(communicationHandler, acceleration, duration, wait).Execute(); - communicationHandler.SleepFor(communicationHandler.GetDelayAfterCommand()); + return base.FindObjectWhichContains(new AltBy(by, value), new AltBy(cameraBy, cameraValue), enabled); } public List GetAllElements(By cameraBy = By.NAME, string cameraValue = "", bool enabled = true) { - var listOfObjects = new AltGetAllElements(communicationHandler, cameraBy, cameraValue, enabled).Execute(); - communicationHandler.SleepFor(communicationHandler.GetDelayAfterCommand()); - return listOfObjects; + return base.GetAllElements(new AltBy(cameraBy, cameraValue), enabled); } public List GetAllElementsLight(By cameraBy = By.NAME, string cameraValue = "", bool enabled = true) { - var listOfObjects = new AltGetAllElementsLight(communicationHandler, cameraBy, cameraValue, enabled).Execute(); - communicationHandler.SleepFor(communicationHandler.GetDelayAfterCommand()); - return listOfObjects; - } - - public void WaitForCurrentSceneToBe(string sceneName, double timeout = 10, double interval = 1) - { - new AltWaitForCurrentSceneToBe(communicationHandler, sceneName, timeout, interval).Execute(); - communicationHandler.SleepFor(communicationHandler.GetDelayAfterCommand()); + return base.GetAllElementsLight(new AltBy(cameraBy, cameraValue), enabled); } public AltObject WaitForObject(By by, string value, By cameraBy = By.NAME, string cameraValue = "", bool enabled = true, double timeout = 20, double interval = 0.5) { - var objectFound = new AltWaitForObject(communicationHandler, by, value, cameraBy, cameraValue, enabled, timeout, interval).Execute(); - communicationHandler.SleepFor(communicationHandler.GetDelayAfterCommand()); - return objectFound; + return base.WaitForObject(new AltBy(by, value), new AltBy(cameraBy, cameraValue), enabled, timeout, interval); } public void WaitForObjectNotBePresent(By by, string value, By cameraBy = By.NAME, string cameraValue = "", bool enabled = true, double timeout = 20, double interval = 0.5) { - new AltWaitForObjectNotBePresent(communicationHandler, by, value, cameraBy, cameraValue, enabled, timeout, interval).Execute(); - communicationHandler.SleepFor(communicationHandler.GetDelayAfterCommand()); + base.WaitForObjectNotBePresent(new AltBy(by, value), new AltBy(cameraBy, cameraValue), enabled, timeout, interval); } public AltObject WaitForObjectWhichContains(By by, string value, By cameraBy = By.NAME, string cameraValue = "", bool enabled = true, double timeout = 20, double interval = 0.5) { - var objectFound = new AltWaitForObjectWhichContains(communicationHandler, by, value, cameraBy, cameraValue, enabled, timeout, interval).Execute(); - communicationHandler.SleepFor(communicationHandler.GetDelayAfterCommand()); - return objectFound; - } - - public List GetAllScenes() - { - var listOfScenes = new AltGetAllScenes(communicationHandler).Execute(); - communicationHandler.SleepFor(communicationHandler.GetDelayAfterCommand()); - return listOfScenes; - } - - public List GetAllCameras() - { - var listOfCameras = new AltGetAllCameras(communicationHandler).Execute(); - communicationHandler.SleepFor(communicationHandler.GetDelayAfterCommand()); - return listOfCameras; - } - - public List GetAllActiveCameras() - { - var listOfCameras = new AltGetAllActiveCameras(communicationHandler).Execute(); - communicationHandler.SleepFor(communicationHandler.GetDelayAfterCommand()); - return listOfCameras; - } - - public AltVector2 GetApplicationScreenSize() - { - var applicationScreenSize = new AltGetApplicationScreenSize(communicationHandler).Execute(); - return applicationScreenSize; - } - - public AltTextureInformation GetScreenshot(AltVector2 size = default(AltVector2), int screenShotQuality = 100) - { - var textureInformation = new AltGetScreenshot(communicationHandler, size, screenShotQuality).Execute(); - communicationHandler.SleepFor(communicationHandler.GetDelayAfterCommand()); - return textureInformation; - } - - public AltTextureInformation GetScreenshot(int id, AltColor color, float width, AltVector2 size = default(AltVector2), int screenShotQuality = 100) - { - var textureInformation = new AltGetHighlightObjectScreenshot(communicationHandler, id, color, width, size, screenShotQuality).Execute(); - communicationHandler.SleepFor(communicationHandler.GetDelayAfterCommand()); - return textureInformation; - } - - public AltTextureInformation GetScreenshot(AltVector2 coordinates, AltColor color, float width, out AltObject selectedObject, AltVector2 size = default(AltVector2), int screenShotQuality = 100) - { - var textureInformation = new AltGetHighlightObjectFromCoordinatesScreenshot(communicationHandler, coordinates, color, width, size, screenShotQuality).Execute(out selectedObject); - communicationHandler.SleepFor(communicationHandler.GetDelayAfterCommand()); - return textureInformation; - } - - public void GetPNGScreenshot(string path) - { - new AltGetPNGScreenshot(communicationHandler, path).Execute(); - communicationHandler.SleepFor(communicationHandler.GetDelayAfterCommand()); - } - - public List GetAllLoadedScenesAndObjects(bool enabled = true) - { - var listOfObjects = new AltGetAllLoadedScenesAndObjects(communicationHandler, enabled).Execute(); - communicationHandler.SleepFor(communicationHandler.GetDelayAfterCommand()); - return listOfObjects; - } - - public void SetServerLogging(AltLogger logger, AltLogLevel logLevel) - { - new AltSetServerLogging(communicationHandler, logger, logLevel).Execute(); - communicationHandler.SleepFor(communicationHandler.GetDelayAfterCommand()); - } - - public int BeginTouch(AltVector2 screenPosition) - { - var touchId = new AltBeginTouch(communicationHandler, screenPosition).Execute(); - communicationHandler.SleepFor(communicationHandler.GetDelayAfterCommand()); - return touchId; - } - - public void MoveTouch(int fingerId, AltVector2 screenPosition) - { - new AltMoveTouch(communicationHandler, fingerId, screenPosition).Execute(); - communicationHandler.SleepFor(communicationHandler.GetDelayAfterCommand()); - } - - public void EndTouch(int fingerId) - { - new AltEndTouch(communicationHandler, fingerId).Execute(); - communicationHandler.SleepFor(communicationHandler.GetDelayAfterCommand()); - } - - /// - /// Retrieves the Unity object at given coordinates. - /// Uses EventSystem.RaycastAll to find object. If no object is found then it uses UnityEngine.Physics.Raycast and UnityEngine.Physics2D.Raycast and returns the one closer to the camera. - /// - /// The screen coordinates - /// The UI object hit by event system Raycast, null otherwise - public AltObject FindObjectAtCoordinates(AltVector2 coordinates) - { - var objectFound = new AltFindObjectAtCoordinates(communicationHandler, coordinates).Execute(); - communicationHandler.SleepFor(communicationHandler.GetDelayAfterCommand()); - return objectFound; - } - - public void AddNotificationListener(NotificationType notificationType, Action callback, bool overwrite) - { - new AddNotificationListener(communicationHandler, notificationType, callback, overwrite).Execute(); - } - - public void RemoveNotificationListener(NotificationType notificationType) - { - new RemoveNotificationListener(communicationHandler, notificationType).Execute(); + return base.WaitForObjectWhichContains(new AltBy(by, value), new AltBy(cameraBy, cameraValue), enabled, timeout, interval); } } } diff --git a/Assets/AltTester/Runtime/AltDriver/AltDriverBase.cs b/Assets/AltTester/Runtime/AltDriver/AltDriverBase.cs new file mode 100644 index 000000000..5b384c23f --- /dev/null +++ b/Assets/AltTester/Runtime/AltDriver/AltDriverBase.cs @@ -0,0 +1,648 @@ +/* + Copyright(C) 2025 Altom Consulting + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +using System; +using System.Collections.Generic; +using System.Threading; +using AltTester.AltTesterUnitySDK.Driver.Commands; +using AltTester.AltTesterUnitySDK.Driver.Communication; +using AltTester.AltTesterUnitySDK.Driver.Logging; +using AltTester.AltTesterUnitySDK.Driver.Notifications; + +namespace AltTester.AltTesterUnitySDK.Driver +{ + public enum By + { + TAG, LAYER, NAME, COMPONENT, PATH, ID, TEXT + } + + public class AltDriverBase + { + private static readonly NLog.Logger logger = DriverLogManager.Instance.GetCurrentClassLogger(); + protected readonly IDriverCommunication communicationHandler; + private static object driverLock = new object(); + public static readonly string VERSION = "2.2.2"; + + public IDriverCommunication CommunicationHandler { get { return communicationHandler; } } + + /// + /// Initiates AltDriver and begins connection with the instrumented Unity application through to AltServer. + /// + /// The IP or hostname AltTester® Server is listening on. + /// The port AltTester® Server is listening on. + /// If true it enables driver commands logging to log file and Unity. + /// The connect timeout in seconds. + /// The name of the Unity application. + public AltDriverBase(string host = "127.0.0.1", int port = 13000, string appName = "__default__", bool enableLogging = false, int connectTimeout = 60, string platform = "unknown", string platformVersion = "unknown", string deviceInstanceId = "unknown", string appId = "unknown", string driverType = "SDK") + { + lock (driverLock) + { +#if UNITY_EDITOR || ALTTESTER + var defaultLevels = new Dictionary { { AltLogger.File, AltLogLevel.Debug }, { AltLogger.Unity, AltLogLevel.Debug } }; +#else + var defaultLevels = new Dictionary { { AltLogger.File, AltLogLevel.Debug }, { AltLogger.Console, AltLogLevel.Debug } }; +#endif + + + if (enableLogging) + { + DriverLogManager.SetupAltDriverLogging(defaultLevels); + } + else + { + DriverLogManager.StopLogging(); + } + + logger.Debug( + "Connecting to AltTester(R) on host: '{0}', port: '{1}', appName: '{2}', platform: '{3}', platformVersion: '{4}', deviceInstanceId: '{5}' and driverType: '{6}'.", + host, + port, + appName, + platform, + platformVersion, + deviceInstanceId, + driverType + ); + while (true) + { + communicationHandler = new DriverCommunicationHandler(host, port, connectTimeout, appName, platform, platformVersion, deviceInstanceId, appId, driverType); + communicationHandler.Connect(); + try + { + checkServerVersion(); + break; + } + catch (NullReferenceException)//There is a strange situation when sometimes checkServerVersion throws that command params is null. I investigated but didn't find the cause. + { + communicationHandler.Close(); + } + } + } + } + + private void splitVersion(string version, out string major, out string minor) + { + var parts = version.Split(new[] { "." }, StringSplitOptions.None); + major = parts[0]; + minor = parts.Length > 1 ? parts[1] : string.Empty; + } + + private void checkServerVersion() + { + string serverVersion = GetServerVersion(); + + string majorServer; + string majorDriver; + string minorDriver; + string minorServer; + + splitVersion(serverVersion, out majorServer, out minorServer); + splitVersion(VERSION, out majorDriver, out minorDriver); + + int serverMajor, serverMinor; + + serverMajor = int.Parse(majorServer); + serverMinor = int.Parse(minorServer); + + bool isSupported = + (serverMajor == 2 && serverMinor == 2) || // Server version 2.2.x + (serverMajor == 1 && serverMinor == 0); // Server version 1.0.0 + + if (!isSupported) + { + string message = $"Version mismatch. AltDriver version is {VERSION}. AltTester(R) version is {serverVersion}."; + logger.Warn(message); + } + } + + + //////////////////////////////////////////// + /// + /// Common methods for all drivers, they are public and can be used directly in the derived classes + /// + //////////////////////////////////////////// + + public void Stop() + { + communicationHandler.Close(); + } + + public void SetCommandResponseTimeout(int commandTimeout) + { + communicationHandler.SetCommandTimeout(commandTimeout); + } + + public void SetDelayAfterCommand(float delay) + { + communicationHandler.SetDelayAfterCommand(delay); + } + + public float GetDelayAfterCommand() + { + return communicationHandler.GetDelayAfterCommand(); + } + + public string GetServerVersion() + { + string serverVersion = new AltGetServerVersion(communicationHandler).Execute(); + communicationHandler.SleepFor(communicationHandler.GetDelayAfterCommand()); + return serverVersion; + } + + public void SetLogging(bool enableLogging) + { + if (enableLogging) + DriverLogManager.ResumeLogging(); + else + DriverLogManager.StopLogging(); + } + + public void KeyUp(AltKeyCode keyCode) + { + AltKeyCode[] keyCodes = { keyCode }; + KeysUp(keyCodes); + } + + /// + /// Simulates multiple keys up action in your app. + /// + /// The key codes of the keys simulated to be up. + public void KeysUp(AltKeyCode[] keyCodes) + { + new AltKeysUp(communicationHandler, keyCodes).Execute(); + communicationHandler.SleepFor(communicationHandler.GetDelayAfterCommand()); + } + + public AltTextureInformation GetScreenshot(AltVector2 size = default(AltVector2), int screenShotQuality = 100) + { + var textureInformation = new AltGetScreenshot(communicationHandler, size, screenShotQuality).Execute(); + communicationHandler.SleepFor(communicationHandler.GetDelayAfterCommand()); + return textureInformation; + } + + public AltTextureInformation GetScreenshot(int id, AltColor color, float width, AltVector2 size = default(AltVector2), int screenShotQuality = 100) + { + var textureInformation = new AltGetHighlightObjectScreenshot(communicationHandler, id, color, width, size, screenShotQuality).Execute(); + communicationHandler.SleepFor(communicationHandler.GetDelayAfterCommand()); + return textureInformation; + } + + public AltTextureInformation GetScreenshot(AltVector2 coordinates, AltColor color, float width, out AltObject selectedObject, AltVector2 size = default(AltVector2), int screenShotQuality = 100) + { + var textureInformation = new AltGetHighlightObjectFromCoordinatesScreenshot(communicationHandler, coordinates, color, width, size, screenShotQuality).Execute(out selectedObject); + communicationHandler.SleepFor(communicationHandler.GetDelayAfterCommand()); + return textureInformation; + } + + public void GetPNGScreenshot(string path) + { + new AltGetPNGScreenshot(communicationHandler, path).Execute(); + communicationHandler.SleepFor(communicationHandler.GetDelayAfterCommand()); + } + + public List GetAllLoadedScenesAndObjects(bool enabled = true) + { + var listOfObjects = new AltGetAllLoadedScenesAndObjects(communicationHandler, enabled).Execute(); + communicationHandler.SleepFor(communicationHandler.GetDelayAfterCommand()); + return listOfObjects; + } + + public void SetServerLogging(AltLogger logger, AltLogLevel logLevel) + { + new AltSetServerLogging(communicationHandler, logger, logLevel).Execute(); + communicationHandler.SleepFor(communicationHandler.GetDelayAfterCommand()); + } + + + public void AddNotificationListener(NotificationType notificationType, Action callback, bool overwrite) + { + new AddNotificationListener(communicationHandler, notificationType, callback, overwrite).Execute(); + } + + public void RemoveNotificationListener(NotificationType notificationType) + { + new RemoveNotificationListener(communicationHandler, notificationType).Execute(); + } + + //////////////////////////////////////////// + /// + /// Protected methods that need to be implemented in the derived classes + /// + //////////////////////////////////////////// + + + protected void ResetInput() + { + new AltResetInput(communicationHandler).Execute(); + } + + protected void LoadScene(string scene, bool loadSingle = true) + { + new AltLoadScene(communicationHandler, scene, loadSingle).Execute(); + communicationHandler.SleepFor(communicationHandler.GetDelayAfterCommand()); + } + + protected void UnloadScene(string scene) + { + new AltUnloadScene(communicationHandler, scene).Execute(); + communicationHandler.SleepFor(communicationHandler.GetDelayAfterCommand()); + } + + protected List GetAllLoadedScenes() + { + var sceneList = new AltGetAllLoadedScenes(communicationHandler).Execute(); + communicationHandler.SleepFor(communicationHandler.GetDelayAfterCommand()); + return sceneList; + } + + protected string GetCurrentScene() + { + var sceneName = new AltGetCurrentScene(communicationHandler).Execute(); + communicationHandler.SleepFor(communicationHandler.GetDelayAfterCommand()); + return sceneName; + } + + protected void WaitForCurrentSceneToBe(string sceneName, double timeout = 10, double interval = 1) + { + new AltWaitForCurrentSceneToBe(communicationHandler, sceneName, timeout, interval).Execute(); + communicationHandler.SleepFor(communicationHandler.GetDelayAfterCommand()); + } + + protected void SetTimeScale(float timeScale) + { + new AltSetTimeScale(communicationHandler, timeScale).Execute(); + communicationHandler.SleepFor(communicationHandler.GetDelayAfterCommand()); + } + + protected float GetTimeScale() + { + var timeScale = new AltGetTimeScale(communicationHandler).Execute(); + communicationHandler.SleepFor(communicationHandler.GetDelayAfterCommand()); + return timeScale; + } + + + protected void DeletePlayerPref() + { + new AltDeletePlayerPref(communicationHandler).Execute(); + communicationHandler.SleepFor(communicationHandler.GetDelayAfterCommand()); + } + + protected void DeleteKeyPlayerPref(string keyName) + { + new AltDeleteKeyPlayerPref(communicationHandler, keyName).Execute(); + communicationHandler.SleepFor(communicationHandler.GetDelayAfterCommand()); + } + + protected void SetKeyPlayerPref(string keyName, int valueName) + { + new AltSetKeyPLayerPref(communicationHandler, keyName, valueName).Execute(); + communicationHandler.SleepFor(communicationHandler.GetDelayAfterCommand()); + } + + protected void SetKeyPlayerPref(string keyName, float valueName) + { + new AltSetKeyPLayerPref(communicationHandler, keyName, valueName).Execute(); + communicationHandler.SleepFor(communicationHandler.GetDelayAfterCommand()); + } + + protected void SetKeyPlayerPref(string keyName, string valueName) + { + new AltSetKeyPLayerPref(communicationHandler, keyName, valueName).Execute(); + communicationHandler.SleepFor(communicationHandler.GetDelayAfterCommand()); + } + + protected int GetIntKeyPlayerPref(string keyName) + { + var keyValue = new AltGetIntKeyPlayerPref(communicationHandler, keyName).Execute(); + communicationHandler.SleepFor(communicationHandler.GetDelayAfterCommand()); + return keyValue; + } + + protected float GetFloatKeyPlayerPref(string keyName) + { + var keyValue = new AltGetFloatKeyPlayerPref(communicationHandler, keyName).Execute(); + communicationHandler.SleepFor(communicationHandler.GetDelayAfterCommand()); + return keyValue; + } + + protected string GetStringKeyPlayerPref(string keyName) + { + var keyValue = new AltGetStringKeyPlayerPref(communicationHandler, keyName).Execute(); + communicationHandler.SleepFor(communicationHandler.GetDelayAfterCommand()); + return keyValue; + } + + protected List FindObjects(AltBy altBy, AltBy cameraAltBy = null, bool enabled = true) + { + cameraAltBy ??= new AltBy(By.NAME, ""); + var listOfObjects = new AltFindObjects(communicationHandler, altBy.By, altBy.Value, cameraAltBy.By, cameraAltBy.Value, enabled).Execute(); + communicationHandler.SleepFor(communicationHandler.GetDelayAfterCommand()); + return listOfObjects; + } + + protected List FindObjectsWhichContain(AltBy altBy, AltBy cameraAltBy = null, bool enabled = true) + { + cameraAltBy ??= new AltBy(By.NAME, ""); + var listOfObjects = new AltFindObjectsWhichContain(communicationHandler, altBy.By, altBy.Value, cameraAltBy.By, cameraAltBy.Value, enabled).Execute(); + communicationHandler.SleepFor(communicationHandler.GetDelayAfterCommand()); + return listOfObjects; + } + + protected AltObject FindObject(AltBy altBy, AltBy cameraAltBy = null, bool enabled = true) + { + cameraAltBy ??= new AltBy(By.NAME, ""); + var findObject = new AltFindObject(communicationHandler, altBy.By, altBy.Value, cameraAltBy.By, cameraAltBy.Value, enabled).Execute(); + communicationHandler.SleepFor(communicationHandler.GetDelayAfterCommand()); + return findObject; + } + + protected AltObject FindObjectWhichContains(AltBy altBy, AltBy cameraAltBy = null, bool enabled = true) + { + cameraAltBy ??= new AltBy(By.NAME, ""); + var findObject = new AltFindObjectWhichContains(communicationHandler, altBy.By, altBy.Value, cameraAltBy.By, cameraAltBy.Value, enabled).Execute(); + communicationHandler.SleepFor(communicationHandler.GetDelayAfterCommand()); + return findObject; + } + + protected T CallStaticMethod(string typeName, string methodName, string assemblyName, + object[] parameters, string[] typeOfParameters = null) + { + var result = new AltCallStaticMethod(communicationHandler, typeName, methodName, parameters, typeOfParameters, assemblyName).Execute(); + communicationHandler.SleepFor(communicationHandler.GetDelayAfterCommand()); + return result; + } + + protected T GetStaticProperty(string componentName, string propertyName, string assemblyName, int maxDepth = 2) + { + var propertyValue = new AltGetStaticProperty(communicationHandler, componentName, propertyName, assemblyName, maxDepth).Execute(); + communicationHandler.SleepFor(communicationHandler.GetDelayAfterCommand()); + return propertyValue; + } + + protected void SetStaticProperty(string componentName, string propertyName, string assemblyName, object updatedProperty) + { + new AltSetStaticProperty(communicationHandler, componentName, propertyName, assemblyName, updatedProperty).Execute(); + communicationHandler.SleepFor(communicationHandler.GetDelayAfterCommand()); + } + + /// + /// Simulates a swipe action between two points. + /// + /// Coordinates of the screen where the swipe begins + /// Coordinates of the screen where the swipe ends + /// The time measured in seconds to move the mouse from start to end location. Defaults to 0.1. + /// If set wait for command to finish. Defaults to True. + protected void Swipe(AltVector2 start, AltVector2 end, float duration = 0.1f, bool wait = true) + { + new AltSwipe(communicationHandler, start, end, duration, wait).Execute(); + communicationHandler.SleepFor(communicationHandler.GetDelayAfterCommand()); + } + + /// + /// Simulates a multipoint swipe action. + /// + /// A list of positions on the screen where the swipe be made. + /// The time measured in seconds to swipe from first position to the last position. Defaults to 0.1. + /// If set wait for command to finish. Defaults to True. + protected void MultipointSwipe(AltVector2[] positions, float duration = 0.1f, bool wait = true) + { + new AltMultipointSwipe(communicationHandler, positions, duration, wait).Execute(); + communicationHandler.SleepFor(communicationHandler.GetDelayAfterCommand()); + } + + /// + /// Simulates holding left click button down for a specified amount of time at given coordinates. + /// + /// The coordinates where the button is held down. + /// The time measured in seconds to keep the button down. + /// If set wait for command to finish. Defaults to True. + protected void HoldButton(AltVector2 coordinates, float duration, bool wait = true) + { + Swipe(coordinates, coordinates, duration, wait); + } + + /// + /// Simulates key press action in your app. + /// + /// The key code of the key simulated to be pressed. + /// A value between [-1,1] used for joysticks to indicate how hard the button was pressed. Defaults to 1. + /// The time measured in seconds from the key press to the key release. Defaults to 0.1 + /// If set wait for command to finish. Defaults to True. + protected void PressKey(AltKeyCode keyCode, float power = 1, float duration = 0.1f, bool wait = true) + { + AltKeyCode[] keyCodes = { keyCode }; + PressKeys(keyCodes, power, duration, wait); + } + + /// + /// Simulates multiple keys pressed action in your app. + /// + /// The list of key codes of the keys simulated to be pressed. + /// A value between [-1,1] used for joysticks to indicate how hard the button was pressed. Defaults to 1. + /// The time measured in seconds from the key press to the key release. Defaults to 0.1 + /// If set wait for command to finish. Defaults to True. + protected void PressKeys(AltKeyCode[] keyCodes, float power = 1, float duration = 0.1f, bool wait = true) + { + new AltPressKeys(communicationHandler, keyCodes, power, duration, wait).Execute(); + communicationHandler.SleepFor(communicationHandler.GetDelayAfterCommand()); + } + + protected void KeyDown(AltKeyCode keyCode, float power = 1) + { + AltKeyCode[] keyCodes = { keyCode }; + KeysDown(keyCodes, power); + } + + /// + /// Simulates multiple keys down action in your app. + /// + /// The key codes of the keys simulated to be down. + /// A value between [-1,1] used for joysticks to indicate how hard the button was pressed. Defaults to 1. + protected void KeysDown(AltKeyCode[] keyCodes, float power = 1) + { + new AltKeysDown(communicationHandler, keyCodes, power).Execute(); + communicationHandler.SleepFor(communicationHandler.GetDelayAfterCommand()); + } + + /// + /// Simulate mouse movement in your app. + /// + /// The screen coordinates + /// The time measured in seconds to move the mouse from the current mouse position to the set coordinates. Defaults to 0.1f + /// If set wait for command to finish. Defaults to True. + protected void MoveMouse(AltVector2 coordinates, float duration = 0.1f, bool wait = true) + { + new AltMoveMouse(communicationHandler, coordinates, duration, wait).Execute(); + communicationHandler.SleepFor(communicationHandler.GetDelayAfterCommand()); + } + + /// + /// Simulate scroll action in your app. + /// + /// Set how fast to scroll. Positive values will scroll up and negative values will scroll down. Defaults to 1 + /// The duration of the scroll in seconds. Defaults to 0.1 + /// If set wait for command to finish. Defaults to True. + protected void Scroll(float speed = 1, float duration = 0.1f, bool wait = true) + { + new AltScroll(communicationHandler, speed, 0, duration, wait).Execute(); + communicationHandler.SleepFor(communicationHandler.GetDelayAfterCommand()); + } + + /// + /// Simulate scroll action in your app. + /// + /// Set how fast to scroll. X is horizontal and Y is vertical. Defaults to 1 + /// The duration of the scroll in seconds. Defaults to 0.1 + /// If set wait for command to finish. Defaults to True. + protected void Scroll(AltVector2 scrollValue, float duration = 0.1f, bool wait = true) + { + new AltScroll(communicationHandler, scrollValue.y, scrollValue.x, duration, wait).Execute(); + communicationHandler.SleepFor(communicationHandler.GetDelayAfterCommand()); + } + + /// + /// Tap at screen coordinates + /// + /// The screen coordinates + /// Number of taps + /// Interval between taps in seconds + /// If set wait for command to finish. Defaults to True. + protected void Tap(AltVector2 coordinates, int count = 1, float interval = 0.1f, bool wait = true) + { + new AltTapCoordinates(communicationHandler, coordinates, count, interval, wait).Execute(); + communicationHandler.SleepFor(communicationHandler.GetDelayAfterCommand()); + } + + /// + /// Click at screen coordinates + /// + /// The screen coordinates + /// Number of clicks. + /// Interval between clicks in seconds + /// If set wait for command to finish. Defaults to True. + public void Click(AltVector2 coordinates, int count = 1, float interval = 0.1f, bool wait = true) + { + new AltClickCoordinates(communicationHandler, coordinates, count, interval, wait).Execute(); + communicationHandler.SleepFor(communicationHandler.GetDelayAfterCommand()); + } + + /// + /// Simulates device rotation action in your app. + /// + /// The linear acceleration of a device. + /// How long the rotation will take in seconds. Defaults to 0.1. + /// If set wait for command to finish. Defaults to True. + protected void Tilt(AltVector3 acceleration, float duration = 0.1f, bool wait = true) + { + new AltTilt(communicationHandler, acceleration, duration, wait).Execute(); + communicationHandler.SleepFor(communicationHandler.GetDelayAfterCommand()); + } + + protected List GetAllElements(AltBy cameraAltBy = null, bool enabled = true) + { + cameraAltBy ??= new AltBy(By.NAME, ""); + var listOfObjects = new AltGetAllElements(communicationHandler, cameraAltBy.By, cameraAltBy.Value, enabled).Execute(); + communicationHandler.SleepFor(communicationHandler.GetDelayAfterCommand()); + return listOfObjects; + } + + protected List GetAllElementsLight(AltBy cameraAltBy = null, bool enabled = true) + { + cameraAltBy ??= new AltBy(By.NAME, ""); + var listOfObjects = new AltGetAllElementsLight(communicationHandler, cameraAltBy.By, cameraAltBy.Value, enabled).Execute(); + communicationHandler.SleepFor(communicationHandler.GetDelayAfterCommand()); + return listOfObjects; + } + + protected AltObject WaitForObject(AltBy altBy, AltBy cameraAltBy = null, bool enabled = true, double timeout = 20, double interval = 0.5) + { + cameraAltBy ??= new AltBy(By.NAME, ""); + var objectFound = new AltWaitForObject(communicationHandler, altBy.By, altBy.Value, cameraAltBy.By, cameraAltBy.Value, enabled, timeout, interval).Execute(); + communicationHandler.SleepFor(communicationHandler.GetDelayAfterCommand()); + return objectFound; + } + + protected void WaitForObjectNotBePresent(AltBy altBy, AltBy cameraAltBy = null, bool enabled = true, double timeout = 20, double interval = 0.5) + { + cameraAltBy ??= new AltBy(By.NAME, ""); + new AltWaitForObjectNotBePresent(communicationHandler, altBy.By, altBy.Value, cameraAltBy.By, cameraAltBy.Value, enabled, timeout, interval).Execute(); + communicationHandler.SleepFor(communicationHandler.GetDelayAfterCommand()); + } + + protected AltObject WaitForObjectWhichContains(AltBy altBy, AltBy cameraAltBy = null, bool enabled = true, double timeout = 20, double interval = 0.5) + { + cameraAltBy ??= new AltBy(By.NAME, ""); + var objectFound = new AltWaitForObjectWhichContains(communicationHandler, altBy.By, altBy.Value, cameraAltBy.By, cameraAltBy.Value, enabled, timeout, interval).Execute(); + communicationHandler.SleepFor(communicationHandler.GetDelayAfterCommand()); + return objectFound; + } + + protected List GetAllScenes() + { + var listOfScenes = new AltGetAllScenes(communicationHandler).Execute(); + communicationHandler.SleepFor(communicationHandler.GetDelayAfterCommand()); + return listOfScenes; + } + + protected List GetAllCameras() + { + var listOfCameras = new AltGetAllCameras(communicationHandler).Execute(); + communicationHandler.SleepFor(communicationHandler.GetDelayAfterCommand()); + return listOfCameras; + } + + protected List GetAllActiveCameras() + { + var listOfCameras = new AltGetAllActiveCameras(communicationHandler).Execute(); + communicationHandler.SleepFor(communicationHandler.GetDelayAfterCommand()); + return listOfCameras; + } + + protected AltVector2 GetApplicationScreenSize() + { + var applicationScreenSize = new AltGetApplicationScreenSize(communicationHandler).Execute(); + return applicationScreenSize; + } + + protected int BeginTouch(AltVector2 screenPosition) + { + var touchId = new AltBeginTouch(communicationHandler, screenPosition).Execute(); + communicationHandler.SleepFor(communicationHandler.GetDelayAfterCommand()); + return touchId; + } + + protected void MoveTouch(int fingerId, AltVector2 screenPosition) + { + new AltMoveTouch(communicationHandler, fingerId, screenPosition).Execute(); + communicationHandler.SleepFor(communicationHandler.GetDelayAfterCommand()); + } + + protected void EndTouch(int fingerId) + { + new AltEndTouch(communicationHandler, fingerId).Execute(); + communicationHandler.SleepFor(communicationHandler.GetDelayAfterCommand()); + } + + protected AltObject FindObjectAtCoordinates(AltVector2 coordinates) + { + var objectFound = new AltFindObjectAtCoordinates(communicationHandler, coordinates).Execute(); + communicationHandler.SleepFor(communicationHandler.GetDelayAfterCommand()); + return objectFound; + } + } +} diff --git a/Assets/AltTester/Runtime/AltDriver/AltDriverBase.cs.meta b/Assets/AltTester/Runtime/AltDriver/AltDriverBase.cs.meta new file mode 100644 index 000000000..216c2e9c2 --- /dev/null +++ b/Assets/AltTester/Runtime/AltDriver/AltDriverBase.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7e6845d299e664da28e03dc64053603d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/AltTester/Runtime/AltDriver/AltDriverUnity.cs b/Assets/AltTester/Runtime/AltDriver/AltDriverUnity.cs new file mode 100644 index 000000000..bd06b9494 --- /dev/null +++ b/Assets/AltTester/Runtime/AltDriver/AltDriverUnity.cs @@ -0,0 +1,342 @@ +/* + Copyright(C) 2025 Altom Consulting + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +using System; +using System.Collections.Generic; +using AltTester.AltTesterUnitySDK.Driver.Commands; + +namespace AltTester.AltTesterUnitySDK.Driver +{ + public class AltDriverUnity : AltDriverBase + { + public AltDriverUnity(string host = "127.0.0.1", int port = 13000, string appName = "__default__", bool enableLogging = false, int connectTimeout = 60, string platform = "unknown", string platformVersion = "unknown", string deviceInstanceId = "unknown", string appId = "unknown", string driverType = "SDK") + : base(host, port, appName, enableLogging, connectTimeout, platform, platformVersion, deviceInstanceId, appId, driverType) + { + } + + public new void ResetInput() + { + base.ResetInput(); + } + public new void LoadScene(string scene, bool loadSingle = true) + { + base.LoadScene(scene, loadSingle); + } + + public new void UnloadScene(string scene) + { + base.UnloadScene(scene); + } + + public new List GetAllLoadedScenes() + { + return base.GetAllLoadedScenes(); + } + + public new string GetCurrentScene() + { + return base.GetCurrentScene(); + } + + public new void WaitForCurrentSceneToBe(string sceneName, double timeout = 10, double interval = 1) + { + base.WaitForCurrentSceneToBe(sceneName, timeout, interval); + } + + public new void SetTimeScale(float timeScale) + { + base.SetTimeScale(timeScale); + } + + public new float GetTimeScale() + { + return base.GetTimeScale(); + } + + + public new void DeletePlayerPref() + { + base.DeletePlayerPref(); + } + + public new void DeleteKeyPlayerPref(string keyName) + { + base.DeleteKeyPlayerPref(keyName); + } + + public new void SetKeyPlayerPref(string keyName, int valueName) + { + base.SetKeyPlayerPref(keyName, valueName); + } + + public new void SetKeyPlayerPref(string keyName, float valueName) + { + base.SetKeyPlayerPref(keyName, valueName); + } + + public new void SetKeyPlayerPref(string keyName, string valueName) + { + base.SetKeyPlayerPref(keyName, valueName); + } + + public new int GetIntKeyPlayerPref(string keyName) + { + return base.GetIntKeyPlayerPref(keyName); + } + + public new float GetFloatKeyPlayerPref(string keyName) + { + return base.GetFloatKeyPlayerPref(keyName); + } + + public new string GetStringKeyPlayerPref(string keyName) + { + return base.GetStringKeyPlayerPref(keyName); + } + + public new List FindObjects(AltBy altBy, AltBy cameraAltBy = null, bool enabled = true) + { + return base.FindObjects(altBy, cameraAltBy, enabled); + } + + public new List FindObjectsWhichContain(AltBy altBy, AltBy cameraAltBy = null, bool enabled = true) + { + return base.FindObjectsWhichContain(altBy, cameraAltBy, enabled); + } + + public new AltObject FindObject(AltBy altBy, AltBy cameraAltBy = null, bool enabled = true) + { + return base.FindObject(altBy, cameraAltBy, enabled); + } + + public new AltObject FindObjectWhichContains(AltBy altBy, AltBy cameraAltBy = null, bool enabled = true) + { + return base.FindObjectWhichContains(altBy, cameraAltBy, enabled); + } + + public new T CallStaticMethod(string typeName, string methodName, string assemblyName, + object[] parameters, string[] typeOfParameters = null) + { + return base.CallStaticMethod(typeName, methodName, assemblyName, parameters, typeOfParameters); + } + + public new T GetStaticProperty(string componentName, string propertyName, string assemblyName, int maxDepth = 2) + { + return base.GetStaticProperty(componentName, propertyName, assemblyName, maxDepth); + } + + public new void SetStaticProperty(string componentName, string propertyName, string assemblyName, object updatedProperty) + { + base.SetStaticProperty(componentName, propertyName, assemblyName, updatedProperty); + } + + /// + /// Simulates a swipe action between two points. + /// + /// Coordinates of the screen where the swipe begins + /// Coordinates of the screen where the swipe ends + /// The time measured in seconds to move the mouse from start to end location. Defaults to 0.1. + /// If set wait for command to finish. Defaults to True. + public new void Swipe(AltVector2 start, AltVector2 end, float duration = 0.1f, bool wait = true) + { + base.Swipe(start, end, duration, wait); + } + + /// + /// Simulates a multipoint swipe action. + /// + /// A list of positions on the screen where the swipe be made. + /// The time measured in seconds to swipe from first position to the last position. Defaults to 0.1. + /// If set wait for command to finish. Defaults to True. + public new void MultipointSwipe(AltVector2[] positions, float duration = 0.1f, bool wait = true) + { + base.MultipointSwipe(positions, duration, wait); + } + + /// + /// Simulates holding left click button down for a specified amount of time at given coordinates. + /// + /// The coordinates where the button is held down. + /// The time measured in seconds to keep the button down. + /// If set wait for command to finish. Defaults to True. + public new void HoldButton(AltVector2 coordinates, float duration, bool wait = true) + { + base.HoldButton(coordinates, duration, wait); + } + + /// + /// Simulates key press action in your app. + /// + /// The key code of the key simulated to be pressed. + /// A value between [-1,1] used for joysticks to indicate how hard the button was pressed. Defaults to 1. + /// The time measured in seconds from the key press to the key release. Defaults to 0.1 + /// If set wait for command to finish. Defaults to True. + public new void PressKey(AltKeyCode keyCode, float power = 1, float duration = 0.1f, bool wait = true) + { + base.PressKey(keyCode, power, duration, wait); + } + + /// + /// Simulates multiple keys pressed action in your app. + /// + /// The list of key codes of the keys simulated to be pressed. + /// A value between [-1,1] used for joysticks to indicate how hard the button was pressed. Defaults to 1. + /// The time measured in seconds from the key press to the key release. Defaults to 0.1 + /// If set wait for command to finish. Defaults to True. + public new void PressKeys(AltKeyCode[] keyCodes, float power = 1, float duration = 0.1f, bool wait = true) + { + base.PressKeys(keyCodes, power, duration, wait); + } + + public new void KeyDown(AltKeyCode keyCode, float power = 1) + { + base.KeyDown(keyCode, power); + } + + /// + /// Simulates multiple keys down action in your app. + /// + /// The key codes of the keys simulated to be down. + /// A value between [-1,1] used for joysticks to indicate how hard the button was pressed. Defaults to 1. + public new void KeysDown(AltKeyCode[] keyCodes, float power = 1) + { + base.KeysDown(keyCodes, power); + } + + + /// + /// Simulate mouse movement in your app. + /// + /// The screen coordinates + /// The time measured in seconds to move the mouse from the current mouse position to the set coordinates. Defaults to 0.1f + /// If set wait for command to finish. Defaults to True. + public new void MoveMouse(AltVector2 coordinates, float duration = 0.1f, bool wait = true) + { + base.MoveMouse(coordinates, duration, wait); + } + + /// + /// Simulate scroll action in your app. + /// + /// Set how fast to scroll. Positive values will scroll up and negative values will scroll down. Defaults to 1 + /// The duration of the scroll in seconds. Defaults to 0.1 + /// If set wait for command to finish. Defaults to True. + public new void Scroll(float speed = 1, float duration = 0.1f, bool wait = true) + { + base.Scroll(speed, duration, wait); + } + + /// + /// Simulate scroll action in your app. + /// + /// Set how fast to scroll. X is horizontal and Y is vertical. Defaults to 1 + /// The duration of the scroll in seconds. Defaults to 0.1 + /// If set wait for command to finish. Defaults to True. + public new void Scroll(AltVector2 scrollValue, float duration = 0.1f, bool wait = true) + { + base.Scroll(scrollValue, duration, wait); + } + + /// + /// Tap at screen coordinates + /// + /// The screen coordinates + /// Number of taps + /// Interval between taps in seconds + /// If set wait for command to finish. Defaults to True. + public new void Tap(AltVector2 coordinates, int count = 1, float interval = 0.1f, bool wait = true) + { + base.Tap(coordinates, count, interval, wait); + } + + /// + /// Simulates device rotation action in your app. + /// + /// The linear acceleration of a device. + /// How long the rotation will take in seconds. Defaults to 0.1. + /// If set wait for command to finish. Defaults to True. + public new void Tilt(AltVector3 acceleration, float duration = 0.1f, bool wait = true) + { + base.Tilt(acceleration, duration, wait); + } + + public new List GetAllElements(AltBy cameraAltBy = null, bool enabled = true) + { + return base.GetAllElements(cameraAltBy, enabled); + } + + public new List GetAllElementsLight(AltBy cameraAltBy = null, bool enabled = true) + { + return base.GetAllElementsLight(cameraAltBy, enabled); + } + + public new AltObject WaitForObject(AltBy altBy, AltBy cameraAltBy = null, bool enabled = true, double timeout = 20, double interval = 0.5) + { + return base.WaitForObject(altBy, cameraAltBy, enabled, timeout, interval); + } + + public new void WaitForObjectNotBePresent(AltBy altBy, AltBy cameraAltBy = null, bool enabled = true, double timeout = 20, double interval = 0.5) + { + base.WaitForObjectNotBePresent(altBy, cameraAltBy, enabled, timeout, interval); + } + + public new AltObject WaitForObjectWhichContains(AltBy altBy, AltBy cameraAltBy = null, bool enabled = true, double timeout = 20, double interval = 0.5) + { + return base.WaitForObjectWhichContains(altBy, cameraAltBy, enabled, timeout, interval); + } + + public new List GetAllScenes() + { + return base.GetAllScenes(); + } + + public new List GetAllCameras() + { + return base.GetAllCameras(); + } + + public new List GetAllActiveCameras() + { + return base.GetAllActiveCameras(); + } + public new AltVector2 GetApplicationScreenSize() + { + return base.GetApplicationScreenSize(); + } + + + public new int BeginTouch(AltVector2 screenPosition) + { + return base.BeginTouch(screenPosition); + } + + public new void MoveTouch(int fingerId, AltVector2 screenPosition) + { + base.MoveTouch(fingerId, screenPosition); + } + + public new void EndTouch(int fingerId) + { + base.EndTouch(fingerId); + } + + public new AltObject FindObjectAtCoordinates(AltVector2 screenPosition) + { + return base.FindObjectAtCoordinates(screenPosition); + } + } +} \ No newline at end of file diff --git a/Assets/AltTester/Runtime/AltDriver/AltDriverUnity.cs.meta b/Assets/AltTester/Runtime/AltDriver/AltDriverUnity.cs.meta new file mode 100644 index 000000000..b30994e87 --- /dev/null +++ b/Assets/AltTester/Runtime/AltDriver/AltDriverUnity.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: abf10e516cd774b878f02f7d35a29c11 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/AltTester/Runtime/AltDriver/AltDriverUnreal.cs b/Assets/AltTester/Runtime/AltDriver/AltDriverUnreal.cs new file mode 100644 index 000000000..c1b22bf22 --- /dev/null +++ b/Assets/AltTester/Runtime/AltDriver/AltDriverUnreal.cs @@ -0,0 +1,138 @@ +/* + Copyright(C) 2025 Altom Consulting + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + + +using System; +using System.Collections.Generic; +using AltTester.AltTesterUnitySDK.Driver.Commands; + +namespace AltTester.AltTesterUnitySDK.Driver +{ + public class AltDriverUnreal : AltDriverBase + { + public AltDriverUnreal(string host = "127.0.0.1", int port = 13000, string appName = "__default__", bool enableLogging = false, int connectTimeout = 60, string platform = "unknown", string platformVersion = "unknown", string deviceInstanceId = "unknown", string appId = "unknown", string driverType = "SDK") + : base(host, port, appName, enableLogging, connectTimeout, platform, platformVersion, deviceInstanceId, appId, driverType) + { + } + + public AltObjectUnreal FindObject(AltBy altBy, bool enabled = true) + { + AltObject altObject = base.FindObject(altBy, enabled: enabled); + return AltObjectUnreal.Create(altObject); + } + + public AltObjectUnreal FindObjectWhichContains(AltBy altBy, bool enabled = true) + { + return AltObjectUnreal.Create(base.FindObjectWhichContains(altBy, enabled: enabled)); + } + + + public List FindObjects(AltBy altBy, bool enabled = true) + { + return base.FindObjects(altBy, enabled: enabled).ConvertAll(obj => AltObjectUnreal.Create(obj)); + } + + + public List FindObjectsWhichContain(AltBy altBy, bool enabled = true) + { + + return base.FindObjectsWhichContain(altBy, enabled: enabled).ConvertAll(obj => AltObjectUnreal.Create(obj)); + } + + + public List GetAllElements(bool enabled = true) + { + return base.GetAllElements(enabled: enabled).ConvertAll(obj => AltObjectUnreal.Create(obj)); + } + + public List GetAllElementsLight(bool enabled = true) + { + return base.GetAllElementsLight(enabled: enabled); + } + + + public AltObjectUnreal WaitForObject(AltBy altBy, bool enabled = true, int timeout = 20, double interval = 0.5) + { + return AltObjectUnreal.Create(base.WaitForObject(altBy, enabled: enabled, timeout: timeout, interval: interval)); + } + + public void WaitForObjectNotBePresent(AltBy altBy, bool enabled = true, int timeout = 20, double interval = 0.5) + { + base.WaitForObjectNotBePresent(altBy, enabled: enabled, timeout: timeout, interval: interval); + } + + public AltObjectUnreal WaitForObjectWhichContains(AltBy altBy, AltBy cameraAltBy = null, bool enabled = true, int timeout = 20, double interval = 0.5) + { + return AltObjectUnreal.Create(base.WaitForObjectWhichContains(altBy, enabled: enabled, timeout: timeout, interval: interval)); + } + + public void KeyDown(AltKeyCode key) + { + base.KeyDown(key, power: 1); + } + + public void KeysDown(AltKeyCode[] keyCodes) + { + base.KeysDown(keyCodes, power: 1); + } + + public void PressKey(AltKeyCode key) + { + base.PressKey(key, power: 1); + } + + public void PressKeys(AltKeyCode[] keyCodes) + { + base.PressKeys(keyCodes, power: 1); + } + + public void LoadLevel(string levelName) + { + base.LoadScene(levelName); + } + + public string GetCurrentLevel() + { + return base.GetCurrentScene(); + } + + public void WaitForCurrentLevelToBe(string levelName, double timeout = 10, double interval = 1) + { + base.WaitForCurrentSceneToBe(levelName, timeout, interval); + } + + public float GetGlobalTimeDilation() + { + return base.GetTimeScale(); + } + + public void SetGlobalTimeDilation(float timeDilation) + { + base.SetTimeScale(timeDilation); + } + + public T CallStaticMethod(string typeName, string methodName, object[] parameters, string[] typeOfParameters = null) + { + return base.CallStaticMethod(typeName, methodName, "", parameters, typeOfParameters); + } + + public new AltObjectUnreal FindObjectAtCoordinates(AltVector2 screenPosition) + { + return AltObjectUnreal.Create(base.FindObjectAtCoordinates(screenPosition)); + } + } +} \ No newline at end of file diff --git a/Assets/AltTester/Runtime/AltDriver/AltDriverUnreal.cs.meta b/Assets/AltTester/Runtime/AltDriver/AltDriverUnreal.cs.meta new file mode 100644 index 000000000..2f295942a --- /dev/null +++ b/Assets/AltTester/Runtime/AltDriver/AltDriverUnreal.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c1e09b9ef5da6404880cf54a3b115a36 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/AltTester/Runtime/AltDriver/Common/AltBy.cs b/Assets/AltTester/Runtime/AltDriver/Common/AltBy.cs new file mode 100644 index 000000000..6eba5f7a0 --- /dev/null +++ b/Assets/AltTester/Runtime/AltDriver/Common/AltBy.cs @@ -0,0 +1,40 @@ +/* + Copyright(C) 2024 Altom Consulting + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +namespace AltTester.AltTesterUnitySDK.Driver +{ + public class AltBy + { + public By By { get; set; } + public string Value { get; set; } + + public AltBy(By by, string value) + { + this.By = by; + this.Value = value; + } + + public static AltBy Text(string text) => new(By.TEXT, text); + public static AltBy Name(string name) => new(By.NAME, name); + public static AltBy Id(string id) => new(By.ID, id); + public static AltBy Tag(string tag) => new(By.TAG, tag); + public static AltBy Layer(string layer) => new(By.LAYER, layer); + public static AltBy Component(string component) => new(By.COMPONENT, component); + public static AltBy Path(string path) => new(By.PATH, path); + + } +} diff --git a/Assets/AltTester/Runtime/AltDriver/Common/AltBy.cs.meta b/Assets/AltTester/Runtime/AltDriver/Common/AltBy.cs.meta new file mode 100644 index 000000000..cdd910586 --- /dev/null +++ b/Assets/AltTester/Runtime/AltDriver/Common/AltBy.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d9375e355126846c79ffe3fcea3c8e01 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/AltTester/Runtime/AltDriver/Common/AltObject.cs b/Assets/AltTester/Runtime/AltDriver/Common/AltObject.cs index 0f611b80a..ca7c4b2fc 100644 --- a/Assets/AltTester/Runtime/AltDriver/Common/AltObject.cs +++ b/Assets/AltTester/Runtime/AltDriver/Common/AltObject.cs @@ -19,144 +19,70 @@ You should have received a copy of the GNU General Public License using System.Collections.Generic; using System.Threading; using AltTester.AltTesterUnitySDK.Driver.Commands; +using Newtonsoft.Json; namespace AltTester.AltTesterUnitySDK.Driver { - public class AltObject + public class AltObject: AltObjectBase { - public string name; - public int id; - public int x; - public int y; - public int z; - public int mobileY; - public string type; - public bool enabled; - public float worldX; - public float worldY; - public float worldZ; - public int idCamera; - public int transformParentId; - public int transformId; - [Newtonsoft.Json.JsonIgnore] - public IDriverCommunication CommHandler; - + + [JsonConstructor] public AltObject(string name, int id = 0, int x = 0, int y = 0, int z = 0, int mobileY = 0, string type = "", bool enabled = true, float worldX = 0, float worldY = 0, float worldZ = 0, int idCamera = 0, int transformParentId = 0, int transformId = 0) + : base(name, id, x, y, z, mobileY, type, enabled, worldX, worldY, worldZ, idCamera, transformParentId, transformId) { - this.name = name; - this.id = id; - this.x = x; - this.y = y; - this.z = z; - this.mobileY = mobileY; - this.type = type; - this.enabled = enabled; - this.worldX = worldX; - this.worldY = worldY; - this.worldZ = worldZ; - this.idCamera = idCamera; - this.transformParentId = transformParentId; - this.transformId = transformId; - } - - public AltObject UpdateObject() - { - var altObject = new AltFindObject(CommHandler, By.ID, this.id.ToString(), By.NAME, "", this.enabled).Execute(); - x = altObject.x; - y = altObject.y; - z = altObject.z; - id = altObject.id; - name = altObject.name; - mobileY = altObject.mobileY; - type = altObject.type; - enabled = altObject.enabled; - worldX = altObject.worldX; - worldY = altObject.worldY; - worldZ = altObject.worldZ; - idCamera = altObject.idCamera; - transformParentId = altObject.transformParentId; - transformId = altObject.transformId; - - CommHandler.SleepFor(CommHandler.GetDelayAfterCommand()); - return this; - } - public AltObject GetParent() - { - var altObject = new AltFindObject(CommHandler, By.PATH, "//*[@id=" + this.id + "]/..", By.NAME, "", true).Execute(); - CommHandler.SleepFor(CommHandler.GetDelayAfterCommand()); - return altObject; - } - - [Obsolete("getParent is deprecated, please use GetParent instead.")] - public AltObject getParent() - { - var altObject = new AltFindObject(CommHandler, By.PATH, "//*[@id=" + this.id + "]/..", By.NAME, "", true).Execute(); - CommHandler.SleepFor(CommHandler.GetDelayAfterCommand()); - return altObject; - } - public AltObject FindObjectFromObject(By by, string value, By cameraBy = By.NAME, string cameraValue = "", bool enabled = true) - { - var findObject = new AltFindObjectFromObject(CommHandler, by, value, cameraBy, cameraValue, enabled, this).Execute(); - CommHandler.SleepFor(CommHandler.GetDelayAfterCommand()); - return findObject; - } - public AltVector2 GetScreenPosition() - { - return new AltVector2(x, y); } - [Obsolete("getScreenPosition is deprecated, please use GetScreenPosition instead.")] - public AltVector2 getScreenPosition() + public AltObject(AltObjectBase altObjectBase) + : base(altObjectBase.name, altObjectBase.id, altObjectBase.x, altObjectBase.y, altObjectBase.z, altObjectBase.mobileY, altObjectBase.type, altObjectBase.enabled, altObjectBase.worldX, altObjectBase.worldY, altObjectBase.worldZ, altObjectBase.idCamera, altObjectBase.transformParentId, altObjectBase.transformId) { - return new AltVector2(x, y); + this.name = altObjectBase.name; + this.id = altObjectBase.id; + this.x = altObjectBase.x; + this.y = altObjectBase.y; + this.z = altObjectBase.z; + this.mobileY = altObjectBase.mobileY; + this.type = altObjectBase.type; + this.enabled = altObjectBase.enabled; + this.worldX = altObjectBase.worldX; + this.worldY = altObjectBase.worldY; + this.worldZ = altObjectBase.worldZ; + this.idCamera = altObjectBase.idCamera; + this.transformParentId = altObjectBase.transformParentId; + this.transformId = altObjectBase.transformId; + this.CommHandler = altObjectBase.CommHandler; } - public AltVector3 GetWorldPosition() + + public new AltObject UpdateObject() { - return new AltVector3(worldX, worldY, worldZ); + return base.UpdateObject(); } - [Obsolete("getWorldPosition is deprecated, please use GetWorldPosition instead.")] - public AltVector3 getWorldPosition() + public new AltObject FindObjectFromObject(By by, string value, By cameraBy = By.NAME, string cameraValue = "", bool enabled = true) { - return new AltVector3(worldX, worldY, worldZ); - } - public T GetComponentProperty(string componentName, string propertyName, string assemblyName, int maxDepth = 2) - { - var propertyValue = new AltGetComponentProperty(CommHandler, componentName, propertyName, assemblyName, maxDepth, this).Execute(); - CommHandler.SleepFor(CommHandler.GetDelayAfterCommand()); - return propertyValue; + return base.FindObjectFromObject(by, value, cameraBy, cameraValue, enabled); } - public T WaitForComponentProperty(string componentName, string propertyName, T propertyValue, string assemblyName, double timeout = 20, double interval = 0.5, bool getPropertyAsString = false, int maxDepth = 2) + + public new T GetComponentProperty(string componentName, string propertyName, string assemblyName, int maxDepth = 2) { - var propertyFound = new AltWaitForComponentProperty(CommHandler, componentName, propertyName, propertyValue, assemblyName, timeout, interval, getPropertyAsString, maxDepth, this).Execute(); - CommHandler.SleepFor(CommHandler.GetDelayAfterCommand()); - return propertyFound; + return base.GetComponentProperty(componentName, propertyName, assemblyName, maxDepth); } - public void SetComponentProperty(string componentName, string propertyName, object value, string assemblyName) + public new T WaitForComponentProperty(string componentName, string propertyName, T propertyValue, string assemblyName, double timeout = 20, double interval = 0.5, bool getPropertyAsString = false, int maxDepth = 2) { - new AltSetComponentProperty(CommHandler, componentName, propertyName, value, assemblyName, this).Execute(); - CommHandler.SleepFor(CommHandler.GetDelayAfterCommand()); + return base.WaitForComponentProperty(componentName, propertyName, propertyValue, assemblyName, timeout, interval, getPropertyAsString, maxDepth); } - - public T CallComponentMethod(string componentName, string methodName, string assemblyName, object[] parameters, string[] typeOfParameters = null) + public new void SetComponentProperty(string componentName, string propertyName, object value, string assemblyName) { - var result = new AltCallComponentMethod(CommHandler, componentName, methodName, parameters, typeOfParameters, assemblyName, this).Execute(); - CommHandler.SleepFor(CommHandler.GetDelayAfterCommand()); - return result; + base.SetComponentProperty(componentName, propertyName, value, assemblyName); } - public string GetText() + public new T CallComponentMethod(string componentName, string methodName, string assemblyName, object[] parameters, string[] typeOfParameters = null) { - var text = new AltGetText(CommHandler, this).Execute(); - CommHandler.SleepFor(CommHandler.GetDelayAfterCommand()); - return text; + return base.CallComponentMethod(componentName, methodName, assemblyName, parameters, typeOfParameters); } - public AltObject SetText(string text, bool submit = false) + public new AltObject SetText(string text, bool submit = false) { - var altObject = new AltSetText(CommHandler, this, text, submit).Execute(); - CommHandler.SleepFor(CommHandler.GetDelayAfterCommand()); - return altObject; + return base.SetText(text, submit); } /// @@ -166,63 +92,29 @@ public AltObject SetText(string text, bool submit = false) /// Interval between clicks in seconds /// Wait for command to finish /// The clicked object - public AltObject Click(int count = 1, float interval = 0.1f, bool wait = true) + public new AltObject Click(int count = 1, float interval = 0.1f, bool wait = true) { - var altObject = new AltClickElement(CommHandler, this, count, interval, wait).Execute(); - CommHandler.SleepFor(CommHandler.GetDelayAfterCommand()); - return altObject; - } - - [Obsolete("PointerUpFromObject is deprecated, please use PointerUp instead.")] - public AltObject PointerUpFromObject() - { - return PointerUp(); - } - - public AltObject PointerUp() - { - var altObject = new AltPointerUpFromObject(CommHandler, this).Execute(); - CommHandler.SleepFor(CommHandler.GetDelayAfterCommand()); - return altObject; - } - - [Obsolete("PointerDownFromObject is deprecated, please use PointerDown instead.")] - public AltObject PointerDownFromObject() - { - return PointerDown(); - } - - public AltObject PointerDown() - { - var altObject = new AltPointerDownFromObject(CommHandler, this).Execute(); - CommHandler.SleepFor(CommHandler.GetDelayAfterCommand()); - return altObject; + return base.Click(count, interval, wait); } - [Obsolete("PointerEnterObject is deprecated, please use PointerEnter instead.")] - public AltObject PointerEnterObject() + public new AltObject PointerUp() { - return PointerEnter(); + return base.PointerUp(); } - public AltObject PointerEnter() + public new AltObject PointerDown() { - var altObject = new AltPointerEnterObject(CommHandler, this).Execute(); - CommHandler.SleepFor(CommHandler.GetDelayAfterCommand()); - return altObject; + return base.PointerDown(); } - [Obsolete("PointerExitObject is deprecated, please use PointerExit instead.")] - public AltObject PointerExitObject() + public new AltObject PointerEnter() { - return PointerExit(); + return base.PointerEnter(); } - public AltObject PointerExit() + public new AltObject PointerExit() { - var altObject = new AltPointerExitObject(CommHandler, this).Execute(); - CommHandler.SleepFor(CommHandler.GetDelayAfterCommand()); - return altObject; + return base.PointerExit(); } /// @@ -232,49 +124,33 @@ public AltObject PointerExit() /// Interval in seconds /// Wait for command to finish /// The tapped object - public AltObject Tap(int count = 1, float interval = 0.1f, bool wait = true) + public new AltObject Tap(int count = 1, float interval = 0.1f, bool wait = true) { - var altObject = new AltTapElement(CommHandler, this, count, interval, wait).Execute(); - CommHandler.SleepFor(CommHandler.GetDelayAfterCommand()); - return altObject; + return base.Tap(count, interval, wait); } - public System.Collections.Generic.List GetAllComponents() + public new System.Collections.Generic.List GetAllComponents() { - var altObject = new AltGetAllComponents(CommHandler, this).Execute(); - CommHandler.SleepFor(CommHandler.GetDelayAfterCommand()); - return altObject; + return base.GetAllComponents(); } - public System.Collections.Generic.List GetAllProperties(AltComponent altComponent, AltPropertiesSelections altPropertiesSelections = AltPropertiesSelections.ALLPROPERTIES) + public new System.Collections.Generic.List GetAllProperties(AltComponent altComponent, AltPropertiesSelections altPropertiesSelections = AltPropertiesSelections.ALLPROPERTIES) { - var altObject = new AltGetAllProperties(CommHandler, altComponent, this, altPropertiesSelections).Execute(); - CommHandler.SleepFor(CommHandler.GetDelayAfterCommand()); - return altObject; + return base.GetAllProperties(altComponent, altPropertiesSelections); } - public System.Collections.Generic.List GetAllFields(AltComponent altComponent, AltFieldsSelections altFieldsSelections = AltFieldsSelections.ALLFIELDS) + public new System.Collections.Generic.List GetAllFields(AltComponent altComponent, AltFieldsSelections altFieldsSelections = AltFieldsSelections.ALLFIELDS) { - var altObject = new AltGetAllFields(CommHandler, altComponent, this, altFieldsSelections).Execute(); - CommHandler.SleepFor(CommHandler.GetDelayAfterCommand()); - return altObject; + return base.GetAllFields(altComponent, altFieldsSelections); } - public System.Collections.Generic.List GetAllMethods(AltComponent altComponent, AltMethodSelection methodSelection = AltMethodSelection.ALLMETHODS) + public new System.Collections.Generic.List GetAllMethods(AltComponent altComponent, AltMethodSelection methodSelection = AltMethodSelection.ALLMETHODS) { - var altObject = new AltGetAllMethods(CommHandler, altComponent, methodSelection).Execute(); - CommHandler.SleepFor(CommHandler.GetDelayAfterCommand()); - return altObject; + return base.GetAllMethods(altComponent, methodSelection); } - public T GetVisualElementProperty(string propertyName) + public new T GetVisualElementProperty(string propertyName) { - if (type != "UIToolkit") - { - throw new WrongAltObjectTypeException("This method is only available for VisualElement objects"); - } - var propertyValue = new AltGetVisualElementProperty(CommHandler, propertyName, this).Execute(); - CommHandler.SleepFor(CommHandler.GetDelayAfterCommand()); - return propertyValue; + return base.GetVisualElementProperty(propertyName); } public T WaitForVisualElementProperty(string propertyName, T propertyValue, double timeout = 20, double interval = 0.5, bool getPropertyAsString = false) { diff --git a/Assets/AltTester/Runtime/AltDriver/Common/AltObjectBase.cs b/Assets/AltTester/Runtime/AltDriver/Common/AltObjectBase.cs new file mode 100644 index 000000000..2b1556789 --- /dev/null +++ b/Assets/AltTester/Runtime/AltDriver/Common/AltObjectBase.cs @@ -0,0 +1,296 @@ +/* + Copyright(C) 2025 Altom Consulting + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +using System; +using System.Threading; +using AltTester.AltTesterUnitySDK.Driver.Commands; + +namespace AltTester.AltTesterUnitySDK.Driver +{ + public class AltObjectBase + { + public string name; + public int id; + public int x; + public int y; + public int z; + public int mobileY; + public string type; + public bool enabled; + public float worldX; + public float worldY; + public float worldZ; + public int idCamera; + public int transformParentId; + public int transformId; + [Newtonsoft.Json.JsonIgnore] + public IDriverCommunication CommHandler; + + public AltObjectBase(string name, int id = 0, int x = 0, int y = 0, int z = 0, int mobileY = 0, string type = "", bool enabled = true, float worldX = 0, float worldY = 0, float worldZ = 0, int idCamera = 0, int transformParentId = 0, int transformId = 0) + { + this.name = name; + this.id = id; + this.x = x; + this.y = y; + this.z = z; + this.mobileY = mobileY; + this.type = type; + this.enabled = enabled; + this.worldX = worldX; + this.worldY = worldY; + this.worldZ = worldZ; + this.idCamera = idCamera; + this.transformParentId = transformParentId; + this.transformId = transformId; + } + + protected AltObject UpdateObject() + { + var altObject = new AltFindObject(CommHandler, By.ID, this.id.ToString(), By.NAME, "", this.enabled).Execute(); + x = altObject.x; + y = altObject.y; + z = altObject.z; + id = altObject.id; + name = altObject.name; + mobileY = altObject.mobileY; + type = altObject.type; + enabled = altObject.enabled; + worldX = altObject.worldX; + worldY = altObject.worldY; + worldZ = altObject.worldZ; + idCamera = altObject.idCamera; + transformParentId = altObject.transformParentId; + transformId = altObject.transformId; + + CommHandler.SleepFor(CommHandler.GetDelayAfterCommand()); + return new AltObject(this); + } + + public override bool Equals(object obj) + { + if (obj == null || GetType() != obj.GetType()) + { + return false; + } + + AltObject altObject = (AltObject)obj; + return id == altObject.id; + } + + public override int GetHashCode() + { + return id; + } + + public AltObject GetParent() + { + var altObject = new AltFindObject(CommHandler, By.PATH, "//*[@id=" + this.id + "]/..", By.NAME, "", true).Execute(); + CommHandler.SleepFor(CommHandler.GetDelayAfterCommand()); + return altObject; + } + + [Obsolete("getParent is deprecated, please use GetParent instead.")] + protected AltObject getParent() + { + var altObject = new AltFindObject(CommHandler, By.PATH, "//*[@id=" + this.id + "]/..", By.NAME, "", true).Execute(); + CommHandler.SleepFor(CommHandler.GetDelayAfterCommand()); + return altObject; + } + protected AltObject FindObjectFromObject(By by, string value, By cameraBy = By.NAME, string cameraValue = "", bool enabled = true) + { + var findObject = new AltFindObjectFromObject(CommHandler, by, value, cameraBy, cameraValue, enabled, new AltObject(this)).Execute(); + CommHandler.SleepFor(CommHandler.GetDelayAfterCommand()); + return findObject; + } + public AltVector2 GetScreenPosition() + { + return new AltVector2(x, y); + } + + [Obsolete("getScreenPosition is deprecated, please use GetScreenPosition instead.")] + protected AltVector2 getScreenPosition() + { + return new AltVector2(x, y); + } + public AltVector3 GetWorldPosition() + { + return new AltVector3(worldX, worldY, worldZ); + } + + [Obsolete("getWorldPosition is deprecated, please use GetWorldPosition instead.")] + protected AltVector3 getWorldPosition() + { + return new AltVector3(worldX, worldY, worldZ); + } + protected T GetComponentProperty(string componentName, string propertyName, string assemblyName, int maxDepth = 2) + { + var propertyValue = new AltGetComponentProperty(CommHandler, componentName, propertyName, assemblyName, maxDepth, new AltObject(this)).Execute(); + CommHandler.SleepFor(CommHandler.GetDelayAfterCommand()); + return propertyValue; + } + protected T WaitForComponentProperty(string componentName, string propertyName, T propertyValue, string assemblyName, double timeout = 20, double interval = 0.5, bool getPropertyAsString = false, int maxDepth = 2) + { + var propertyFound = new AltWaitForComponentProperty(CommHandler, componentName, propertyName, propertyValue, assemblyName, timeout, interval, getPropertyAsString, maxDepth, new AltObject(this)).Execute(); + CommHandler.SleepFor(CommHandler.GetDelayAfterCommand()); + return propertyFound; + } + protected void SetComponentProperty(string componentName, string propertyName, object value, string assemblyName) + { + new AltSetComponentProperty(CommHandler, componentName, propertyName, value, assemblyName, new AltObject(this)).Execute(); + CommHandler.SleepFor(CommHandler.GetDelayAfterCommand()); + } + + protected T CallComponentMethod(string componentName, string methodName, string assemblyName, object[] parameters, string[] typeOfParameters = null) + { + var result = new AltCallComponentMethod(CommHandler, componentName, methodName, parameters, typeOfParameters, assemblyName, new AltObject(this)).Execute(); + CommHandler.SleepFor(CommHandler.GetDelayAfterCommand()); + return result; + } + + public string GetText() + { + var text = new AltGetText(CommHandler, new AltObject(this)).Execute(); + CommHandler.SleepFor(CommHandler.GetDelayAfterCommand()); + return text; + } + + protected AltObject SetText(string text, bool submit = false) + { + var altObject = new AltSetText(CommHandler, new AltObject(this), text, submit).Execute(); + CommHandler.SleepFor(CommHandler.GetDelayAfterCommand()); + return altObject; + } + + /// + /// Click current object + /// + /// Number of times to click + /// Interval between clicks in seconds + /// Wait for command to finish + /// The clicked object + protected AltObject Click(int count = 1, float interval = 0.1f, bool wait = true) + { + var altObject = new AltClickElement(CommHandler, new AltObject(this), count, interval, wait).Execute(); + CommHandler.SleepFor(CommHandler.GetDelayAfterCommand()); + return altObject; + } + + [Obsolete("PointerUpFromObject is deprecated, please use PointerUp instead.")] + protected AltObject PointerUpFromObject() + { + return PointerUp(); + } + + protected AltObject PointerUp() + { + var altObject = new AltPointerUpFromObject(CommHandler, new AltObject(this)).Execute(); + CommHandler.SleepFor(CommHandler.GetDelayAfterCommand()); + return altObject; + } + + [Obsolete("PointerDownFromObject is deprecated, please use PointerDown instead.")] + protected AltObject PointerDownFromObject() + { + return PointerDown(); + } + + protected AltObject PointerDown() + { + var altObject = new AltPointerDownFromObject(CommHandler, new AltObject(this)).Execute(); + CommHandler.SleepFor(CommHandler.GetDelayAfterCommand()); + return altObject; + } + + [Obsolete("PointerEnterObject is deprecated, please use PointerEnter instead.")] + protected AltObject PointerEnterObject() + { + return PointerEnter(); + } + + protected AltObject PointerEnter() + { + var altObject = new AltPointerEnterObject(CommHandler, new AltObject(this)).Execute(); + CommHandler.SleepFor(CommHandler.GetDelayAfterCommand()); + return altObject; + } + + [Obsolete("PointerExitObject is deprecated, please use PointerExit instead.")] + protected AltObject PointerExitObject() + { + return PointerExit(); + } + + protected AltObject PointerExit() + { + var altObject = new AltPointerExitObject(CommHandler, new AltObject(this)).Execute(); + CommHandler.SleepFor(CommHandler.GetDelayAfterCommand()); + return altObject; + } + + /// + /// Tap current object + /// + /// Number of taps + /// Interval in seconds + /// Wait for command to finish + /// The tapped object + protected AltObject Tap(int count = 1, float interval = 0.1f, bool wait = true) + { + var altObject = new AltTapElement(CommHandler, new AltObject(this), count, interval, wait).Execute(); + CommHandler.SleepFor(CommHandler.GetDelayAfterCommand()); + return altObject; + } + + public System.Collections.Generic.List GetAllComponents() + { + var altObject = new AltGetAllComponents(CommHandler, new AltObject(this)).Execute(); + CommHandler.SleepFor(CommHandler.GetDelayAfterCommand()); + return altObject; + } + + protected System.Collections.Generic.List GetAllProperties(AltComponent altComponent, AltPropertiesSelections altPropertiesSelections = AltPropertiesSelections.ALLPROPERTIES) + { + var altObject = new AltGetAllProperties(CommHandler, altComponent, new AltObject(this), altPropertiesSelections).Execute(); + CommHandler.SleepFor(CommHandler.GetDelayAfterCommand()); + return altObject; + } + + protected System.Collections.Generic.List GetAllFields(AltComponent altComponent, AltFieldsSelections altFieldsSelections = AltFieldsSelections.ALLFIELDS) + { + var altObject = new AltGetAllFields(CommHandler, altComponent, new AltObject(this), altFieldsSelections).Execute(); + CommHandler.SleepFor(CommHandler.GetDelayAfterCommand()); + return altObject; + } + + protected System.Collections.Generic.List GetAllMethods(AltComponent altComponent, AltMethodSelection methodSelection = AltMethodSelection.ALLMETHODS) + { + var altObject = new AltGetAllMethods(CommHandler, altComponent, methodSelection).Execute(); + CommHandler.SleepFor(CommHandler.GetDelayAfterCommand()); + return altObject; + } + protected T GetVisualElementProperty(string propertyName) + { + if (type != "UIToolkit") + { + throw new WrongAltObjectTypeException("This method is only available for VisualElement objects"); + } + var propertyValue = new AltGetVisualElementProperty(CommHandler, propertyName, new AltObject(this)).Execute(); + CommHandler.SleepFor(CommHandler.GetDelayAfterCommand()); + return propertyValue; + } + } +} diff --git a/Assets/AltTester/Runtime/AltDriver/Common/AltObjectBase.cs.meta b/Assets/AltTester/Runtime/AltDriver/Common/AltObjectBase.cs.meta new file mode 100644 index 000000000..2e1472753 --- /dev/null +++ b/Assets/AltTester/Runtime/AltDriver/Common/AltObjectBase.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 04b413ad3dd2b41d9b74ec810c49eb62 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/AltTester/Runtime/AltDriver/Common/AltObjectUnreal.cs b/Assets/AltTester/Runtime/AltDriver/Common/AltObjectUnreal.cs new file mode 100644 index 000000000..bb0a92c53 --- /dev/null +++ b/Assets/AltTester/Runtime/AltDriver/Common/AltObjectUnreal.cs @@ -0,0 +1,82 @@ +/* + Copyright(C) 2025 Altom Consulting + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + + +using Newtonsoft.Json; + +namespace AltTester.AltTesterUnitySDK.Driver +{ + public class AltObjectUnreal: AltObjectBase + { + [JsonConstructor] + public AltObjectUnreal(string name, int id = 0, int x = 0, int y = 0, int z = 0, int mobileY = 0, string type = "", bool enabled = true, float worldX = 0, float worldY = 0, float worldZ = 0, int idCamera = 0, int transformParentId = 0, int transformId = 0) + : base(name, id, x, y, z, mobileY, type, enabled, worldX, worldY, worldZ, idCamera, transformParentId, transformId) + { + } + + private AltObjectUnreal(AltObject altObject) + : base(altObject.name, altObject.id, altObject.x, altObject.y, altObject.z, altObject.mobileY, altObject.type, altObject.enabled, altObject.worldX, altObject.worldY, altObject.worldZ, altObject.idCamera, altObject.transformParentId, altObject.transformId) + { + this.CommHandler = altObject.CommHandler; + } + + public new AltObjectUnreal UpdateObject() + { + return AltObjectUnreal.Create(base.UpdateObject()); + } + + public static AltObjectUnreal Create(AltObject altObject) + { + if (altObject == null) + { + return null; + } + + return new AltObjectUnreal(altObject); + } + + public AltObjectUnreal Click() + { + return AltObjectUnreal.Create(base.Click()); + } + + public T GetComponentProperty(string componentName, string propertyName) + { + return base.GetComponentProperty(componentName, propertyName, "", 2); + } + + public T WaitForComponentProperty(string componentName, string propertyName, T propertyValue, double timeout = 20, double interval = 0.5, bool getPropertyAsString = false) + { + return base.WaitForComponentProperty(componentName, propertyName, propertyValue, "", timeout, interval, getPropertyAsString, 2); + } + + public void SetComponentProperty(string componentName, string propertyName, object value) + { + base.SetComponentProperty(componentName, propertyName, value, ""); + } + + public T CallComponentMethod(string componentName, string methodName, object[] parameters, string[] typeOfParameters = null) + { + return base.CallComponentMethod(componentName, methodName, "", parameters, typeOfParameters); + } + + public AltObjectUnreal SetText(string text) + { + return AltObjectUnreal.Create(base.SetText(text, false)); + } + } +} diff --git a/Assets/AltTester/Runtime/AltDriver/Common/AltObjectUnreal.cs.meta b/Assets/AltTester/Runtime/AltDriver/Common/AltObjectUnreal.cs.meta new file mode 100644 index 000000000..c8de864a7 --- /dev/null +++ b/Assets/AltTester/Runtime/AltDriver/Common/AltObjectUnreal.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 828743fae22944d10aa2f0c86ef00312 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/AltTester/Runtime/Commands/FindObject/OldFindObject/AltOldFindObjectCommand.cs b/Assets/AltTester/Runtime/Commands/FindObject/OldFindObject/AltOldFindObjectCommand.cs index 059638e6e..d03e75731 100644 --- a/Assets/AltTester/Runtime/Commands/FindObject/OldFindObject/AltOldFindObjectCommand.cs +++ b/Assets/AltTester/Runtime/Commands/FindObject/OldFindObject/AltOldFindObjectCommand.cs @@ -28,7 +28,6 @@ public AltOldFindObjectCommand(BaseFindObjectsParams cmdParam) : base(cmdParam) public override AltObject Execute() { - UnityEngine.Debug.Log("OlfFindObject " + CommandParams.path); var path = new OldPathSelector(CommandParams.path); var foundGameObject = FindObjects(null, path.FirstBound, true, CommandParams.enabled); UnityEngine.Camera camera = null; diff --git a/Assets/AltTester/Runtime/Commands/FindObject/OldFindObject/AltOldFindObjectsCommand.cs b/Assets/AltTester/Runtime/Commands/FindObject/OldFindObject/AltOldFindObjectsCommand.cs index 6188b0a0d..4868803fc 100644 --- a/Assets/AltTester/Runtime/Commands/FindObject/OldFindObject/AltOldFindObjectsCommand.cs +++ b/Assets/AltTester/Runtime/Commands/FindObject/OldFindObject/AltOldFindObjectsCommand.cs @@ -28,7 +28,6 @@ public AltOldFindObjectsCommand(BaseFindObjectsParams cmdParams) : base(cmdParam public override List Execute() { - UnityEngine.Debug.Log("OlfFindObject " + CommandParams.path); UnityEngine.Camera camera = null; if (!CommandParams.cameraPath.Equals("//")) { diff --git a/Assets/Examples/Test/Editor/Driver/TestBaseAltDriverUnity.cs b/Assets/Examples/Test/Editor/Driver/TestBaseAltDriverUnity.cs new file mode 100644 index 000000000..86ea640b7 --- /dev/null +++ b/Assets/Examples/Test/Editor/Driver/TestBaseAltDriverUnity.cs @@ -0,0 +1,51 @@ +/* + Copyright(C) 2024 Altom Consulting + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +using AltTester.AltTesterUnitySDK.Driver; +using AltTester.AltTesterUnitySDK.Driver.Logging; +using AltTester.AltTesterUnitySDK.Driver.Tests; +using NUnit.Framework; + +public class TestBaseAltDriverUnity +{ + protected AltDriverUnity altDriver; + protected string sceneName; + private int defaultCommandResponseTimeout = 5; + + [OneTimeSetUp] + public void SetUp() + { + altDriver = TestsHelper.GetAltDriverUnity(); + DriverLogManager.SetMinLogLevel(AltLogger.Console, AltLogLevel.Info); + DriverLogManager.SetMinLogLevel(AltLogger.Unity, AltLogLevel.Info); + } + + [OneTimeTearDown] + public void TearDown() + { + altDriver.Stop(); + } + + [SetUp] + protected void LoadLevel() + { + altDriver.ResetInput(); + + altDriver.SetCommandResponseTimeout(defaultCommandResponseTimeout); + altDriver.LoadScene(this.sceneName, true); + } +} diff --git a/Assets/Examples/Test/Editor/Driver/TestBaseAltDriverUnity.cs.meta b/Assets/Examples/Test/Editor/Driver/TestBaseAltDriverUnity.cs.meta new file mode 100644 index 000000000..41aa70534 --- /dev/null +++ b/Assets/Examples/Test/Editor/Driver/TestBaseAltDriverUnity.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a603d14553c9b46ecaf7b5fa3a512eaa +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Examples/Test/Editor/Driver/TestBaseAltDriverUnreal.cs b/Assets/Examples/Test/Editor/Driver/TestBaseAltDriverUnreal.cs new file mode 100644 index 000000000..2bb7940d5 --- /dev/null +++ b/Assets/Examples/Test/Editor/Driver/TestBaseAltDriverUnreal.cs @@ -0,0 +1,54 @@ +/* + Copyright(C) 2024 Altom Consulting + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +using System.Threading; +using AltTester.AltTesterUnitySDK.Driver; +using AltTester.AltTesterUnitySDK.Driver.Logging; +using AltTester.AltTesterUnitySDK.Driver.Tests; +using NUnit.Framework; + +public class TestBaseAltDriverUnreal +{ + protected AltDriverUnreal altDriver; + protected string sceneName; + private int defaultCommandResponseTimeout = 5; + + [OneTimeSetUp] + public void SetUp() + { + altDriver = TestsHelper.GetAltDriverUnreal(); + DriverLogManager.SetMinLogLevel(AltLogger.Console, AltLogLevel.Info); + DriverLogManager.SetMinLogLevel(AltLogger.Unity, AltLogLevel.Info); + } + + [OneTimeTearDown] + public void TearDown() + { + altDriver.Stop(); + } + + [SetUp] + protected void LoadLevel() + { + if (!string.IsNullOrEmpty(sceneName)) // Ensure sceneName is not null or empty + { + // altDriver.ResetInput(); + altDriver.SetCommandResponseTimeout(defaultCommandResponseTimeout); + altDriver.LoadLevel(sceneName); + } + } +} diff --git a/Assets/Examples/Test/Editor/Driver/TestBaseAltDriverUnreal.cs.meta b/Assets/Examples/Test/Editor/Driver/TestBaseAltDriverUnreal.cs.meta new file mode 100644 index 000000000..e7bd7dbd4 --- /dev/null +++ b/Assets/Examples/Test/Editor/Driver/TestBaseAltDriverUnreal.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: cb2254a5a52774b2ca66dfb4504756a9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Examples/Test/Editor/Driver/TestforScene1TestSampleWithAltDriverUnreal.cs b/Assets/Examples/Test/Editor/Driver/TestforScene1TestSampleWithAltDriverUnreal.cs new file mode 100644 index 000000000..34285abcf --- /dev/null +++ b/Assets/Examples/Test/Editor/Driver/TestforScene1TestSampleWithAltDriverUnreal.cs @@ -0,0 +1,716 @@ +using System.Text.RegularExpressions; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Security.Cryptography; +using System.Threading; +using Newtonsoft.Json.Linq; +using NUnit.Framework; + + + +namespace AltTester.AltTesterUnitySDK.Driver.Tests +{ + [Ignore("These tests will be run from the Unreal Engine SDK Test repo")] + [TestFixture] + [Parallelizable] + [Timeout(30000)] + public class TestForScene1TestSampleWithAltDriverUnreal : TestBaseAltDriverUnreal + { + public TestForScene1TestSampleWithAltDriverUnreal() + { + sceneName = "Scene1"; + } + + // working tests + // --------------------------------------------------------------------------------------------------------------- + + [Test] + public void TestGetCurrentLevel() + { + Assert.That(altDriver.GetCurrentLevel(), Is.EqualTo(sceneName)); + } + + [TestCase("//*[contains(@name,AltTesterConnectionOverlay)]/*[contains(@name,CanvasPanel_)]/ConnectionMenu", "ConnectionMenu")] + [TestCase("//*[contains(@name,AltTesterConnectionOverlay)]/*[contains(@name,CanvasPanel_)]/ConnectionMenu/AltTesterInfoText", "AltTesterInfoText")] + [TestCase("/BP_Cube", "BP_Cube")] + public void TestFindDisabledObject(string path, string name) + { + AltObjectUnreal altObject = altDriver.FindObject(AltBy.Path(path), enabled: false); + Assert.That(altObject, Is.Not.Null); + Assert.That(altObject.name, Does.Match($"^{Regex.Escape(name)}.*")); + } + + // [TestCase(By.ID, "13b211d0-eafa-452d-8708-cc70f5075e93", "//Capsule")] - AltID not implemented + [TestCase(By.LAYER, "Wat", "//BP_Capsule")] + [TestCase(By.COMPONENT, "Capsule", "//BP_Capsule")] + [TestCase(By.NAME, "Cap", "//BP_Capsule")] + [TestCase(By.TAG, "MainCamera", "//MainCamera")] + [TestCase(By.TEXT, "Change Camera", "//*[contains(@name,W_UserInterface_C_)]/*[contains(@name,CanvasPanel_)]/*[contains(@name,Button_)]/*[contains(@name,TextBlock_)]")] + public void TestFindObjectWhichContains(By by, string value, string path) + { + AltObjectUnreal expectedObject = altDriver.FindObject(AltBy.Path(path)); + AltObjectUnreal altObject = altDriver.FindObjectWhichContains(new AltBy(by, value)); + + Assert.That(altObject, Is.Not.Null); + Assert.That(altObject.id, Is.EqualTo(expectedObject.id)); + } + + [Test] + public void TestFindElementThatContainsText() + { + const string text = "Change Camera"; + AltObjectUnreal altElement = altDriver.FindObject(AltBy.Path("//*[contains(@text," + text + ")]")); + Assert.That(altElement, Is.Not.Null); + } + + [TestCase(By.COMPONENT, "NonExistent")] + [TestCase(By.ID, "NonExistent")] + [TestCase(By.LAYER, "NonExistent")] + [TestCase(By.NAME, "NonExistent")] + [TestCase(By.TAG, "NonExistent")] + [TestCase(By.TEXT, "NonExistent")] + public void TestFindNonExistentObjectWhichContains(By by, string value) + { + Assert.Throws(() => altDriver.FindObjectWhichContains(new AltBy(by, value))); + } + + [TestCase(By.NAME, "Cap", "//BP_Capsule", "//CapsuleInfo")] + [TestCase(By.TAG, "camera", "//MainCamera", "//SecondaryCamera")] + public void TestFindObjectsWhichContain2(By by, string value, string path1, string path2) + { + AltObjectUnreal expectedObject1 = altDriver.FindObject(AltBy.Path(path1)); + AltObjectUnreal expectedObject2 = altDriver.FindObject(AltBy.Path(path2)); + List altObjects = altDriver.FindObjectsWhichContain(new AltBy(by, value)); + + Assert.That(altObjects, Has.Count.EqualTo(2)); + Assert.That( + (altObjects[0].id == expectedObject1.id && altObjects[1].id == expectedObject2.id) || + (altObjects[0].id == expectedObject2.id && altObjects[1].id == expectedObject1.id), + Is.True, "The ids do not match the expected values in either order."); + } + [TestCase(By.COMPONENT, "NonExistent")] + [TestCase(By.ID, "NonExistent")] + [TestCase(By.LAYER, "NonExistent")] + [TestCase(By.NAME, "NonExistent")] + [TestCase(By.TAG, "NonExistent")] + [TestCase(By.TEXT, "NonExistent")] + public void TestFindNonExistentObjectsWhichContain(By by, string value) + { + List altObjects = altDriver.FindObjectsWhichContain(new AltBy(by, value)); + Assert.That(altObjects, Is.Empty); + } + + [Test] + public void TestWaitForCurrentLevelToBe() + { + DateTime timeStart = DateTime.Now; + altDriver.WaitForCurrentLevelToBe(sceneName); + DateTime timeEnd = DateTime.Now; + TimeSpan time = timeEnd - timeStart; + Assert.That(time.TotalSeconds, Is.LessThan(20)); + + string currentScene = altDriver.GetCurrentLevel(); + Assert.That(currentScene, Is.EqualTo(sceneName)); + } + + // [TestCase(By.ID, "13b211d0-eafa-452d-8708-cc70f5075e93", "//Capsule")] + [TestCase(By.LAYER, "Wat", "//BP_Capsule")] + [TestCase(By.COMPONENT, "Capsule", "//BP_Capsule")] + [TestCase(By.TEXT, "Change Camera", "//*[contains(@name,W_UserInterface_C_)]/*[contains(@name,CanvasPanel_)]/*[contains(@name,Button_)]/*[contains(@name,TextBlock_)]")] + public void TestFindObjectsWhichContain(By by, string value, string path) + { + AltObjectUnreal expectedObject = altDriver.FindObject(AltBy.Path(path)); + List altObjects = altDriver.FindObjectsWhichContain(new AltBy(by, value)); + Assert.That(altObjects, Has.Count.EqualTo(1)); + Assert.That(altObjects[0].id, Is.EqualTo(expectedObject.id)); + } + + // [TestCase(By.ID, "13b211d0-eafa-452d-8708-cc70f5075e93", "//Capsule")] + // [TestCase(By.PATH, "//*[@id=13b211d0-eafa-452d-8708-cc70f5075e93]", "//Capsule")] + // [TestCase(By.PATH, "//*[@tag=plane][@layer=Default][@name=Plane][@component=MeshCollider][@id=58af4167-0971-415f-901c-7c5226c3c170]", "//Plane")] + // [TestCase(By.PATH, "//*[@tag=Untagged][@layer=UI][@name=Text][@component=CanvasRenderer][@id=0ffed8a8-3d77-4b03-965b-5ae094ba9511][@text=Change Camera Mode]", "/Canvas/Button/Text")] + // [TestCase(By.PATH, "//*[contains(@id,13b211d0-eafa-452d-8708-cc70f5075e93)]", "//Capsule")] + // [TestCase(By.PATH, "//*[contains(@tag,pla)][contains(@layer,Def)][contains(@name,Pla)][contains(@component,MeshColl)][contains(@id,58af4167-0971-415f-901c-7c5226c3c170)]", "//Plane")] + // [TestCase(By.PATH, "//*[contains(@tag,Untag)][contains(@layer,U)][contains(@name,Tex)][contains(@component,CanvasRender)][contains(@id,0ffed8a8-3d77-4b03-965b-5ae094ba9511)][contains(@text,Change Camera)]", "/Canvas/Button/Text")] + [TestCase(By.LAYER, "Water", "//BP_Capsule")] + [TestCase(By.PATH, "//*[@layer=Water]", "//BP_Capsule")] + [TestCase(By.COMPONENT, "Capsule", "//BP_Capsule")] + [TestCase(By.COMPONENT, "SkyAtmosphereComponent", "//SkyAtmosphere")] + [TestCase(By.NAME, "BP_Capsule", "//BP_Capsule")] + [TestCase(By.PATH, "/Sphere", "//Sphere")] + [TestCase(By.PATH, "//PlaneS/..", "//Sphere")] + [TestCase(By.PATH, "//Sphere/*", "//PlaneS")] + [TestCase(By.PATH, "//*[@tag=MainCamera]", "//MainCamera")] + [TestCase(By.PATH, "//*[@name=BP_Capsule]", "//BP_Capsule")] + [TestCase(By.PATH, "//*[@component=Capsule]", "//BP_Capsule")] + [TestCase(By.PATH, "//*[@text=Change Camera Mode]", "//*[contains(@name,W_UserInterface_C_)]/*[contains(@name,CanvasPanel_)]/*[contains(@name,Button_)]/*[contains(@name,TextBlock_)]")] + [TestCase(By.PATH, "//*[contains(@tag,MainCam)]", "//MainCamera")] + [TestCase(By.PATH, "//*[contains(@name,Cap)]", "//BP_Capsule")] + [TestCase(By.PATH, "//*[contains(@component,Capsule)]", "//BP_Capsule")] + [TestCase(By.PATH, "//*[contains(@text,Change Camera)]", "//*[contains(@name,W_UserInterface_C_)]/*[contains(@name,CanvasPanel_)]/*[contains(@name,Button_)]/*[contains(@name,TextBlock_)]")] + [TestCase(By.PATH, "//*[contains(@layer,Wat)]", "//BP_Capsule")] + [TestCase(By.PATH, "//*[@tag=plane][@layer=Default]", "//Plane")] + [TestCase(By.PATH, "//*[@tag=plane][@layer=Default][@name=Plane]", "//Plane")] + [TestCase(By.PATH, "//*[@tag=plane][@layer=Default][@name=Plane][@component=StaticMeshComponent0]", "//Plane")] + [TestCase(By.PATH, "//*[contains(@tag,pla)][contains(@layer,Def)]", "//Plane")] + [TestCase(By.PATH, "//*[contains(@tag,pla)][contains(@layer,Def)][contains(@name,Pla)]", "//Plane")] + [TestCase(By.PATH, "//*[contains(@tag,pla)][contains(@layer,Def)][contains(@name,Pla)][contains(@component,StaticMeshComp)]", "//Plane")] + [TestCase(By.TAG, "MainCamera", "//MainCamera")] + [TestCase(By.TEXT, "Capsule Info", "//CapsuleInfo")] + [TestCase(By.TEXT, "Change Camera Mode", "//*[contains(@name,W_UserInterface_C_)]/*[contains(@name,CanvasPanel_)]/*[contains(@name,Button_)]/*[contains(@name,TextBlock_)]")] + public void TestFindObject(By by, string value, string path) + { + int referenceId = altDriver.FindObject(AltBy.Path(path)).id; + AltObjectUnreal altObject = altDriver.FindObject(new AltBy(by, value)); + Assert.That(altObject, Is.Not.Null); + Assert.That(altObject.id, Is.EqualTo(referenceId)); + } + + [TestCase(By.COMPONENT, "NonExistent")] + [TestCase(By.ID, "NonExistent")] + [TestCase(By.LAYER, "NonExistent")] + [TestCase(By.NAME, "NonExistent")] + [TestCase(By.PATH, "/NonExistent")] + [TestCase(By.PATH, "//NonExistent/..")] + [TestCase(By.PATH, "//NonExistent/*")] + [TestCase(By.PATH, "//*[@tag=NonExistent]")] + [TestCase(By.PATH, "//*[@layer=NonExistent]")] + [TestCase(By.PATH, "//*[@name=NonExistent]")] + [TestCase(By.PATH, "//*[@component=NonExistent]")] + [TestCase(By.PATH, "//*[@id=NonExistent]")] + [TestCase(By.PATH, "//*[@text=NonExistent]")] + [TestCase(By.PATH, "//*[@tag=plane][@layer=NonExistent]")] + [TestCase(By.PATH, "//*[@tag=plane][@layer=Default][@name=NonExistent]")] + [TestCase(By.PATH, "//*[@tag=plane][@layer=Default][@name=Plane][@component=NonExistent]")] + [TestCase(By.PATH, "//*[@tag=plane][@layer=Default][@name=Plane][@component=StaticMeshComponent0][@id=NonExistent]")] + // [TestCase(By.PATH, "//*[@tag=Untagged][@layer=UI][@name=Text][@component=CanvasRenderer][@id=f9dc3b3c-2791-42dc-9c0f-87ef7ff5e11d][@text=NonExistent]")] + [TestCase(By.PATH, "//*[contains(@tag,NonExistent)]")] + [TestCase(By.PATH, "//*[contains(@layer,NonExistent)]")] + [TestCase(By.PATH, "//*[contains(@name,NonExistent)]")] + [TestCase(By.PATH, "//*[contains(@component,NonExistent)]")] + [TestCase(By.PATH, "//*[contains(@id,NonExistent)]")] + [TestCase(By.PATH, "//*[contains(@text,NonExistent)]")] + [TestCase(By.PATH, "//*[contains(@tag,pla)][contains(@layer,NonExistent)]")] + [TestCase(By.PATH, "//*[contains(@tag,pla)][contains(@layer,Def)][contains(@name,NonExistent)]")] + [TestCase(By.PATH, "//*[contains(@tag,pla)][contains(@layer,Def)][contains(@name,Pla)][contains(@component,NonExistent)]")] + [TestCase(By.PATH, "//*[contains(@tag,pla)][contains(@layer,Def)][contains(@name,Pla)][contains(@component,StaticMeshComp)][contains(@id,NonExistent)]")] + // [TestCase(By.PATH, "//*[contains(@tag,Untag)][contains(@layer,U)][contains(@name,Tex)][contains(@component,CanvasRender)][contains(@id,f9dc3b3c-2791-42dc-9c0f-87ef7ff5e11d)][contains(@text,NonExistent)]")] + [TestCase(By.PATH, "//Canvas[100]")] + [TestCase(By.PATH, "//Canvas[-100]")] + [TestCase(By.TAG, "NonExistent")] + [TestCase(By.TEXT, "NonExistent")] + [TestCase(By.PATH, "//DisabledObject")] + [TestCase(By.PATH, "//DisabledObject/DisabledChild")] + public void TestFindNonExistentObject(By by, string value) + { + Assert.Throws(() => altDriver.FindObject(new AltBy(by, value))); + } + + [Test] + public void TestGetComponentPropertyPrivate() + { + const string componentName = "AltExampleScriptCapsule"; + const string propertyName = "privateVariable"; + + AltObjectUnreal altElement = altDriver.FindObject(AltBy.Name("BP_Capsule")); + Assert.That(altElement, Is.Not.Null); + + int propertyValue = altElement.GetComponentProperty(componentName, propertyName); + Assert.That(propertyValue, Is.EqualTo(0)); + } + + [Test] + public void TestGetComponentPropertyStatic() + { + const string componentName = "AltExampleScriptCapsule"; + const string propertyName = "privateStaticVariable"; + + AltObjectUnreal altElement = altDriver.FindObject(AltBy.Name("BP_Capsule")); + Assert.That(altElement, Is.Not.Null); + + // Can't access static variables from Unreal + // int propertyValue = altElement.GetComponentProperty(componentName, propertyName, ""); + // Assert.That(propertyValue, Is.EqualTo(0)); + + try + { + altElement.GetComponentProperty(componentName, propertyName); + Assert.Fail(); + } + catch (PropertyNotFoundException exception) + { + Assert.That(exception.Message, Is.EqualTo($"Property {propertyName} not found")); + } + } + + [Test] + public void TestGetComponentPropertyStaticPublic() + { + const string componentName = "AltExampleScriptCapsule"; + const string propertyName = "PublicStaticVariable"; + + AltObjectUnreal altElement = altDriver.FindObject(AltBy.Name("BP_Capsule")); + Assert.That(altElement, Is.Not.Null); + + // Can't access static variables from Unreal + // int propertyValue = altElement.GetComponentProperty(componentName, propertyName, ""); + // Assert.That(propertyValue, Is.EqualTo(0)); + + try + { + altElement.GetComponentProperty(componentName, propertyName); + Assert.Fail(); + } + catch (PropertyNotFoundException exception) + { + Assert.That(exception.Message, Is.EqualTo($"Property {propertyName} not found")); + } + } + + [Test] + public void TestSetComponentProperty() + { + const string componentName = "AltExampleScriptCapsule"; + const string propertyName = "stringToSetFromTests"; + + AltObjectUnreal altElement = altDriver.FindObject(AltBy.Name("BP_Capsule")); + Assert.That(altElement, Is.Not.Null); + altElement.SetComponentProperty(componentName, propertyName, "2"); + + string propertyValue = altElement.GetComponentProperty(componentName, propertyName); + Assert.That(propertyValue, Is.EqualTo("2")); + } + + [Test] + public void TestCallMethodWithOptionalParameters() + { + const string componentName = "AltExampleScriptCapsule"; + const string methodName = "TestMethodWithOptionalIntParameters"; + string[] parameters = new string[] {"1", "2"}; + + AltObjectUnreal altElement = altDriver.FindObject(AltBy.Name("BP_Capsule")); + string data = altElement.CallComponentMethod(componentName, methodName, parameters); + Assert.That(data, Is.EqualTo("3")); + } + + [Test] + public void TestCallMethodWithOptionalParametersString() + { + const string componentName = "AltExampleScriptCapsule"; + const string methodName = "TestMethodWithOptionalStringParameters"; + + object[] parameters = new object[] { "FirstParameter", "SecondParameter" }; + string[] typeOfParameters = new string[] { "System.String", "System.String" }; + + AltObjectUnreal altElement = altDriver.FindObject(AltBy.Name("BP_Capsule")); + string data = altElement.CallComponentMethod(componentName, methodName, parameters, typeOfParameters); + Assert.That(data, Is.EqualTo("FirstParameterSecondParameter")); + } + + [Test] + public void TestCallMethodWithOptionalParametersString2() + { + const string componentName = "AltExampleScriptCapsule"; + const string methodName = "TestMethodWithOptionalStringParameters"; + + object[] parameters = new object[] {"FirstParameter", ""}; + string[] typeOfParameters = new string[] {"System.String", "System.String"}; + + AltObjectUnreal altElement = altDriver.FindObject(AltBy.Name("BP_Capsule")); + string data = altElement.CallComponentMethod(componentName, methodName, parameters, typeOfParameters); + Assert.That(data, Is.EqualTo("FirstParameter")); + } + + [Test] + public void TestWaitForObjectToNotExist() + { + altDriver.WaitForObjectNotBePresent(AltBy.Name("ObjectDestroyedIn5Secs")); + altDriver.WaitForObjectNotBePresent(AltBy.Name("BP_Capsulee"), timeout: 1, interval: 0.5f); + } + + [Test] + public void TestWaitForObjectToNotExistFail() + { + try + { + altDriver.WaitForObjectNotBePresent(AltBy.Name("BP_Capsule"), timeout: 1, interval: 0.5f); + Assert.Fail(); + } + catch (WaitTimeOutException exception) + { + Assert.That(exception.Message, Is.EqualTo("Element //BP_Capsule still found after 1 seconds")); + } + } + + [Test] + public void TestWaitForCurrentLevelToBeANonExistingScene() + { + const string name = "AltDriverTestScene"; + try + { + altDriver.WaitForCurrentLevelToBe(name, 1); + Assert.Fail(); + } + catch (WaitTimeOutException exception) + { + Assert.That(exception.Message, Is.EqualTo("Scene AltDriverTestScene not loaded after 1 seconds")); + } + } + + [Test] + public void TestGetAllComponents() + { + List components = altDriver.FindObject(AltBy.Name("MainCamera")).GetAllComponents(); + Assert.That(components, Has.Count.EqualTo(3)); + Assert.That(components[0].componentName, Is.EqualTo("SceneComponent")); + Assert.That(components[0].assemblyName, Is.EqualTo("SceneComponent")); + } + + [TestCase("ChineseLetters", "哦伊娜哦")] + [TestCase("NonEnglishText", "BJÖRN'S PASS")] + public void TestGetChineseLetters(string name, string nonEnglishText) + { + string text = altDriver.FindObject(AltBy.Name(name)).GetText(); + Assert.That(text, Is.EqualTo(nonEnglishText)); + } + + [Test] + public void TestForSetText() + { + AltObjectUnreal text = altDriver.FindObject(AltBy.Name("NonEnglishText")); + string originalText = text.GetText(); + string afterText = text.SetText("ModifiedText").GetText(); + Assert.That(afterText, Is.Not.EqualTo(originalText)); + } + + [Test] + public void TestGetVersion() + { + Assert.That(altDriver.GetServerVersion(), Is.EqualTo("1.0.0")); + } + + [Test] + public void TestInvalidPaths() + { + Assert.Throws(() => altDriver.FindObject(AltBy.Path("//[1]"))); + Assert.Throws(() => altDriver.FindObject(AltBy.Path("CapsuleInfo[@tag=UI]"))); + Assert.Throws(() => altDriver.FindObject(AltBy.Path("//CapsuleInfo[@tag=UI/Text"))); + Assert.Throws(() => altDriver.FindObject(AltBy.Path("//CapsuleInfo[0/Text"))); + } + + [Test] + public void TestCallPrivateMethod() + { + AltObjectUnreal altObject = altDriver.FindObject(AltBy.Name("BP_Capsule")); + altObject.CallComponentMethod("AltExampleScriptCapsule", "CallJump", Array.Empty()); + AltObjectUnreal capsuleInfo = altDriver.FindObject(AltBy.Name("CapsuleInfo")); + string text = capsuleInfo.GetText(); + Assert.That(text, Is.EqualTo("Capsule jumps!")); + } + + [Test] + public void TestKeysDown() + { + AltObjectUnreal altObject = altDriver.FindObject(AltBy.Name("BP_Capsule")); + + // click in scene + altDriver.Click(new AltVector2()); + + // keys down + AltKeyCode[] keys = new AltKeyCode[] {AltKeyCode.K, AltKeyCode.L}; + altDriver.KeysDown(keys); + altDriver.KeysUp(keys); + + string finalPropertyValue = altObject.UpdateObject().GetComponentProperty("AltExampleScriptCapsule", "stringToSetFromTests"); + Assert.That(finalPropertyValue, Is.EqualTo("multiple keys pressed")); + } + + [Test] + public void TestPressKeys() + { + AltObjectUnreal altObject = altDriver.FindObject(AltBy.Name("BP_Capsule")); + + // click in scene + altDriver.Click(new AltVector2()); + + // press keys + AltKeyCode[] keys = new AltKeyCode[] {AltKeyCode.K, AltKeyCode.L}; + altDriver.PressKeys(keys); + + string finalPropertyValue = altObject.UpdateObject().GetComponentProperty("AltExampleScriptCapsule", "stringToSetFromTests"); + Assert.That(finalPropertyValue, Is.EqualTo("multiple keys pressed")); + } + + + [Test] + public void TestGetObjectWithNumberAsName() + { + AltObjectUnreal numberObject = altDriver.FindObject(AltBy.Name("1234"), enabled: false); + Assert.That(numberObject, Is.Not.Null); + + numberObject = altDriver.FindObject(AltBy.Path("//1234"), enabled: false); + Assert.That(numberObject, Is.Not.Null); + } + + [TestCase("//ConnectionMenu[0]", "ConnectionMenu", false)] + [TestCase("//TextBlock[-1]", "TextBlock", true)] + public void TestFindIndexer(string path, string expectedResult, bool enabled) + { + AltObjectUnreal altElement = altDriver.FindObject(AltBy.Path(path), enabled: enabled); + Assert.That(altElement.name, Is.EqualTo(expectedResult)); + } + + [TestCase("TextMeshInputField")] + [TestCase("UnrealUIInputField")] + public void TestSetTextForUnrealUIInputField(string fieldName) + { + string text = "exampleUnrealUIInputField"; + AltObjectUnreal inputField = altDriver.FindObject(AltBy.Name(fieldName)).SetText(text); + Assert.That(inputField.UpdateObject().GetText(), Is.EqualTo(text)); + } + + [Test] + public void TestSetTimeScale() + { + altDriver.SetGlobalTimeDilation(0.1f); + Thread.Sleep(1000); + + float timeScaleFromGame = altDriver.GetGlobalTimeDilation(); + Assert.That(timeScaleFromGame, Is.EqualTo(0.1f)); + altDriver.SetGlobalTimeDilation(1); + } + + [Test] + public void TestFindElementAtCoordinates() + { + AltObjectUnreal counterButtonText = altDriver.FindObject(AltBy.Name("ButtonCounter_Text")); + AltObjectUnreal element = altDriver.FindObjectAtCoordinates(new AltVector2(counterButtonText.x, counterButtonText.y)); + Assert.That(element.name, Is.EqualTo("ButtonCounter_Text")); + } + + [Test] + public void TestFindElementAtCoordinates_NoElement() + { + AltObjectUnreal element = altDriver.FindObjectAtCoordinates(new AltVector2(-1, -1)); + Assert.That(element, Is.Null); + } + + [Test] + // [TestCase("//BP_Capsule", @"^BP_Capsule_C_\d+$")] + [TestCase("//Plane", @"^StaticMeshActor_\d+$")] + public void TestFindElementAtCoordinates_3dElement(string objectPath, string expectedPatternName) + { + AltObjectUnreal altObject = altDriver.FindObject(AltBy.Path(objectPath)); + AltObjectUnreal element = altDriver.FindObjectAtCoordinates(altObject.GetScreenPosition()); + Assert.That(element, Is.Not.Null); + Assert.That(element.name, Does.Match(expectedPatternName)); + } + + [Test] + public void TestLoadNonExistentScene() + { + Assert.Throws(() => altDriver.LoadLevel("NonExistentScene")); + } + + [Test] + public void TestGetComponentPropertyArray() + { + const string componentName = "AltExampleScriptCapsule"; + const string propertyName = "arrayOfInts"; + + AltObjectUnreal altElement = altDriver.FindObject(AltBy.Name("BP_Capsule")); + Assert.That(altElement, Is.Not.Null); + + int[] propertyValue = altElement.GetComponentProperty(componentName, propertyName); + Assert.That(propertyValue, Has.Length.EqualTo(3)); + + Assert.That(propertyValue[0], Is.EqualTo(1)); + Assert.That(propertyValue[1], Is.EqualTo(2)); + Assert.That(propertyValue[2], Is.EqualTo(3)); + } + + [Test] + public void TestGetComponentPropertyNullValue() + { + const string componentName = "AltExampleScriptCapsule"; + const string propertyName = "fieldNullValue"; + + AltObjectUnreal altElement = altDriver.FindObject(AltBy.Name("BP_Capsule")); + Assert.That(altElement, Is.Not.Null); + + object propertyValue = altElement.GetComponentProperty(componentName, propertyName); + Assert.That(propertyValue, Is.EqualTo(null)); + } + + [Test] + public void TestSetNonExistingComponentProperty() + { + const string unexistingComponent = "unexisting"; + const string propertyName = "stringToSetFromTests"; + + AltObjectUnreal altElement = altDriver.FindObject(AltBy.Name("BP_Capsule")); + Assert.That(altElement, Is.Not.Null); + try + { + altElement.SetComponentProperty(unexistingComponent, propertyName, "2"); + Assert.Fail(); + } + catch (ComponentNotFoundException exception) + { + Assert.That(exception.Message, Does.StartWith("Component not found"), exception.Message); + } + } + + [Test] + public void TestSetNonExistingProperty() + { + const string componentName = "AltExampleScriptCapsule"; + const string unexistingPropertyName = "unexisting"; + + AltObjectUnreal altElement = altDriver.FindObject(AltBy.Name("BP_Capsule")); + Assert.That(altElement, Is.Not.Null); + try + { + altElement.SetComponentProperty(componentName, unexistingPropertyName, "2"); + Assert.Fail(); + } + catch (PropertyNotFoundException exception) + { + Assert.That(exception.Message, Does.StartWith($"Property {unexistingPropertyName} not found"), exception.Message); + } + } + + [Test] + public void TestCallMethodWithNoParameters() + { + const string componentName = "AltExampleScriptCapsule"; + const string methodName = "UIButtonClicked"; + + AltObjectUnreal altElement = altDriver.FindObject(AltBy.Name("BP_Capsule")); + string data = altElement.CallComponentMethod(componentName, methodName, Array.Empty()); + Assert.That(data, Is.Null); + } + + [Test] + public void TestCallMethodWithParameters() + { + const string componentName = "AltExampleScriptCapsule"; + const string methodName = "Jump"; + string[] parameters = new string[] {"New Text"}; + + AltObjectUnreal altElement = altDriver.FindObject(AltBy.Name("BP_Capsule")); + string data = altElement.CallComponentMethod(componentName, methodName, parameters); + Assert.That(data, Is.Null); + } + + [Test] + public void TestCallMethodWithManyParameters() + { + const string componentName = "AltExampleScriptCapsule"; + const string methodName = "TestMethodWithManyParameters"; + object[] parameters = new object[] { 1, "stringparam", 0.5, new int[] { 1, 2, 3 } }; + + AltObjectUnreal altElement = altDriver.FindObject(AltBy.Name("BP_Capsule")); + string data = altElement.CallComponentMethod(componentName, methodName, parameters); + Assert.That(data, Is.Null); + } + + [Test] + public void TestCallMethodWithIncorrectNumberOfParameters() + { + const string componentName = "AltExampleScriptCapsule"; + const string methodName = "TestMethodWithManyParameters"; + object[] parameters = new object[] { 1, "stringparam", new int[] { 1, 2, 3 } }; + + AltObjectUnreal altElement = altDriver.FindObject(AltBy.Name("BP_Capsule")); + try + { + altElement.CallComponentMethod(componentName, methodName, parameters); + Assert.Fail(); + } + catch (MethodWithGivenParametersNotFoundException exception) + { + Assert.That(exception.Message, Does.StartWith("No method found with 3 parameters matching signature: TestMethodWithManyParameters()"), exception.Message); + } + } + + [Test] + public void TestCallMethodWithIncorrectNumberOfParameters2() + { + const string componentName = "AltExampleScriptCapsule"; + const string methodName = "TestMethodWithManyParameters"; + object[] parameters = new object[] { "a", "stringparam", new int[] { 1, 2, 3 } }; + + AltObjectUnreal altElement = altDriver.FindObject(AltBy.Name("BP_Capsule")); + try + { + altElement.CallComponentMethod(componentName, methodName, parameters); + Assert.Fail(); + } + catch (MethodWithGivenParametersNotFoundException exception) + { + Assert.That(exception.Message, Does.StartWith("No method found with 3 parameters matching signature: TestMethodWithManyParameters()"), exception.Message); + } + } + + [Test] + public void TestCallMethodInvalidMethodArgumentTypes() + { + const string componentName = "AltExampleScriptCapsule"; + const string methodName = "TestMethodWithManyParameters"; + object[] parameters = new object[] { "stringnoint", "stringparam", 0.5, new int[] { 1, 2, 3 } }; + + AltObjectUnreal altElement = altDriver.FindObject(AltBy.Name("BP_Capsule")); + try + { + altElement.CallComponentMethod(componentName: componentName, methodName: methodName, parameters: parameters); + Assert.Fail(); + } + catch (FailedToParseArgumentsException exception) + { + Assert.That(exception.Message, Does.StartWith("Could not parse parameter"), exception.Message); + } + } + + [Test] + public void TestCallMethodInvalidParameterType() + { + const string componentName = "AltExampleScriptCapsule"; + const string methodName = "TestMethodWithManyParameters"; + object[] parameters = new object[] { 1, "stringparam", 0.5, new int[] { 1, 2, 3 } }; + + AltObjectUnreal altElement = altDriver.FindObject(AltBy.Name("BP_Capsule")); + try + { + altElement.CallComponentMethod(componentName, methodName, parameters, new string[] { "System.Stringggggg" }); + Assert.Fail(); + } + catch (InvalidParameterTypeException exception) + { + Assert.That(exception.Message, Does.StartWith("Number of parameters different than number of types of parameters"), exception.Message); + } + } + + [Test] + public void TestGetScreenshot() + { + string path = "test.png"; + altDriver.GetPNGScreenshot(path); + FileAssert.Exists(path); + } + + [Test] + public void TestCallStaticNonExistentMethod() + { + Assert.Throws(() => altDriver.CallStaticMethod("GameUserSettings", "UNEXISTING", new object[] { "Test", "2" })); + } + + [Test] + public void TestCallStaticMethodNonExistingTypeName() + { + Assert.Throws(() => altDriver.CallStaticMethod("UNEXISTING", "GetScreenResolution", new object[] { "Test", "2" })); + } + } +} diff --git a/Assets/Examples/Test/Editor/Driver/TestforScene1TestSampleWithAltDriverUnreal.cs.meta b/Assets/Examples/Test/Editor/Driver/TestforScene1TestSampleWithAltDriverUnreal.cs.meta new file mode 100644 index 000000000..7c37d2645 --- /dev/null +++ b/Assets/Examples/Test/Editor/Driver/TestforScene1TestSampleWithAltDriverUnreal.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 272ee3efbc9614ce09896b965ad6fa78 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Examples/Test/Editor/Driver/Testforscene1TestSampleWithAltDriverUnity.cs b/Assets/Examples/Test/Editor/Driver/Testforscene1TestSampleWithAltDriverUnity.cs new file mode 100644 index 000000000..1b05d1c88 --- /dev/null +++ b/Assets/Examples/Test/Editor/Driver/Testforscene1TestSampleWithAltDriverUnity.cs @@ -0,0 +1,406 @@ +/* + Copyright(C) 2024 Altom Consulting + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Security.Cryptography; +using System.Threading; +using Newtonsoft.Json.Linq; +using NUnit.Framework; + +namespace AltTester.AltTesterUnitySDK.Driver.Tests +{ + + [TestFixture] + [Parallelizable] + [Timeout(30000)] + public class TestForScene1TestSampleWithAltDriverUnity : TestBaseAltDriverUnity + { + + public TestForScene1TestSampleWithAltDriverUnity() + { + sceneName = "Scene 1 AltDriverTestScene"; + } + + // UI disable elements are needed for this because at the moment, the test uses the AltDialog objects + [TestCase("/AltTesterPrefab/AltDialog/Dialog", "Dialog")] + [TestCase("/AltTesterPrefab/AltDialog/Dialog/Title", "Title")] + [TestCase("/Cube", "Cube")] + public void TestFindDisabledObject(string path, string name) + { + var altObject = altDriver.FindObject(AltBy.Path(path), enabled: false); + Assert.NotNull(altObject); + Assert.AreEqual(name, altObject.name); + } + + [TestCase(By.COMPONENT, "CapsuleColl", "//Capsule")] + [TestCase(By.ID, "13b211d0-eafa-452d-8708-cc70f5075e93", "//Capsule")] + [TestCase(By.LAYER, "Wat", "//Capsule")] + [TestCase(By.NAME, "Cap", "//Capsule")] + [TestCase(By.TAG, "pla", "//Plane")] + [TestCase(By.TEXT, "Change Camera", "/Canvas/Button/Text")] + public void TestFindObjectWhichContains(By by, string value, string path) + { + var expectedObject = altDriver.FindObject(AltBy.Path(path)); + var altObject = altDriver.FindObjectWhichContains(new AltBy(by, value)); + Assert.NotNull(altObject); + Assert.AreEqual(expectedObject.id, altObject.id); + } + + [Test] + public void TestFindElementThatContainsText() + { + const string text = "Change Camera"; + var altElement = altDriver.FindObject(AltBy.Path("//*[contains(@text," + text + ")]")); + Assert.NotNull(altElement); + } + + [TestCase(By.COMPONENT, "NonExistent")] + [TestCase(By.ID, "NonExistent")] + [TestCase(By.LAYER, "NonExistent")] + [TestCase(By.NAME, "NonExistent")] + [TestCase(By.TAG, "NonExistent")] + [TestCase(By.TEXT, "NonExistent")] + public void TestFindNonExistentObjectWhichContains(By by, string value) + { + AltBy altBy = new AltBy(by, value); + Assert.Throws(() => altDriver.FindObjectWhichContains(altBy)); + } + + [TestCase(By.COMPONENT, "CapsuleColl", "//Capsule")] + [TestCase(By.ID, "13b211d0-eafa-452d-8708-cc70f5075e93", "//Capsule")] + [TestCase(By.LAYER, "Wat", "//Capsule")] + [TestCase(By.TEXT, "Change Camera", "/Canvas/Button/Text")] + public void TestFindObjectsWhichContain(By by, string value, string path) + { + var expectedObject = altDriver.FindObject(AltBy.Path(path)); + var altObjects = altDriver.FindObjectsWhichContain(new AltBy(by, value)); + Assert.AreEqual(1, altObjects.Count()); + Assert.AreEqual(expectedObject.id, altObjects[0].id); + } + + [TestCase(By.NAME, "Cap", "//Capsule", "//CapsuleInfo")] + [TestCase(By.TAG, "pla", "//Plane", "/UIWithWorldSpace/Plane")] + public void TestFindObjectsWhichContain2(By by, string value, string path1, string path2) + { + var expectedObject1 = altDriver.FindObject(AltBy.Path(path1)); + var expectedObject2 = altDriver.FindObject(AltBy.Path(path2)); + var altObjects = altDriver.FindObjectsWhichContain(new AltBy(by, value)); + Assert.AreEqual(2, altObjects.Count()); + Assert.AreEqual(expectedObject1.id, altObjects[0].id); + Assert.AreEqual(expectedObject2.id, altObjects[1].id); + } + + [TestCase(By.COMPONENT, "NonExistent")] + [TestCase(By.ID, "NonExistent")] + [TestCase(By.LAYER, "NonExistent")] + [TestCase(By.NAME, "NonExistent")] + [TestCase(By.TAG, "NonExistent")] + [TestCase(By.TEXT, "NonExistent")] + public void TestFindNonExistentObjectsWhichContain(By by, string value) + { + var altObjects = altDriver.FindObjectsWhichContain(new AltBy(by, value)); + Assert.IsEmpty(altObjects); + } + + [TestCase(By.COMPONENT, "AltRunner", "//AltTesterPrefab")] + [TestCase(By.COMPONENT, "CapsuleCollider", "//Capsule")] //Unity component + [TestCase(By.COMPONENT, "AltTester.AltDriver.AltRunner", "//AltTesterPrefab")] // namespace + [TestCase(By.ID, "13b211d0-eafa-452d-8708-cc70f5075e93", "//Capsule")] + [TestCase(By.LAYER, "Water", "//Capsule")] + [TestCase(By.NAME, "Capsule", "//Capsule")] + [TestCase(By.PATH, "/Sphere", "//Sphere")] + [TestCase(By.PATH, "//PlaneS/..", "//Sphere")] + [TestCase(By.PATH, "//Sphere/*", "//PlaneS")] + [TestCase(By.PATH, "//*[@tag=plane]", "//Plane")] + [TestCase(By.PATH, "//*[@layer=Water]", "//Capsule")] + [TestCase(By.PATH, "//*[@name=Capsule]", "//Capsule")] + [TestCase(By.PATH, "//*[@component=CapsuleCollider]", "//Capsule")] + [TestCase(By.PATH, "//*[@id=13b211d0-eafa-452d-8708-cc70f5075e93]", "//Capsule")] + [TestCase(By.PATH, "//*[@text=Change Camera Mode]", "/Canvas/Button/Text")] + [TestCase(By.PATH, "//*[@tag=plane][@layer=Default]", "//Plane")] + [TestCase(By.PATH, "//*[@tag=plane][@layer=Default][@name=Plane]", "//Plane")] + [TestCase(By.PATH, "//*[@tag=plane][@layer=Default][@name=Plane][@component=MeshCollider]", "//Plane")] + [TestCase(By.PATH, "//*[@tag=plane][@layer=Default][@name=Plane][@component=MeshCollider][@id=58af4167-0971-415f-901c-7c5226c3c170]", "//Plane")] + [TestCase(By.PATH, "//*[@tag=Untagged][@layer=UI][@name=Text][@component=CanvasRenderer][@id=0ffed8a8-3d77-4b03-965b-5ae094ba9511][@text=Change Camera Mode]", "/Canvas/Button/Text")] + [TestCase(By.PATH, "//*[contains(@tag,pla)]", "//Plane")] + [TestCase(By.PATH, "//*[contains(@layer,Wat)]", "//Capsule")] + [TestCase(By.PATH, "//*[contains(@name,Cap)]", "//Capsule")] + [TestCase(By.PATH, "//*[contains(@component,CapsuleColl)]", "//Capsule")] + [TestCase(By.PATH, "//*[contains(@id,13b211d0-eafa-452d-8708-cc70f5075e93)]", "//Capsule")] + [TestCase(By.PATH, "//*[contains(@text,Change Camera)]", "/Canvas/Button/Text")] + [TestCase(By.PATH, "//*[contains(@tag,pla)][contains(@layer,Def)]", "//Plane")] + [TestCase(By.PATH, "//*[contains(@tag,pla)][contains(@layer,Def)][contains(@name,Pla)]", "//Plane")] + [TestCase(By.PATH, "//*[contains(@tag,pla)][contains(@layer,Def)][contains(@name,Pla)][contains(@component,MeshColl)]", "//Plane")] + [TestCase(By.PATH, "//*[contains(@tag,pla)][contains(@layer,Def)][contains(@name,Pla)][contains(@component,MeshColl)][contains(@id,58af4167-0971-415f-901c-7c5226c3c170)]", "//Plane")] + [TestCase(By.PATH, "//*[contains(@tag,Untag)][contains(@layer,U)][contains(@name,Tex)][contains(@component,CanvasRender)][contains(@id,0ffed8a8-3d77-4b03-965b-5ae094ba9511)][contains(@text,Change Camera)]", "/Canvas/Button/Text")] + [TestCase(By.TAG, "plane", "//Plane")] + [TestCase(By.TEXT, "Capsule Info", "//CapsuleInfo")] // text area + [TestCase(By.TEXT, "Change Camera Mode", "//Canvas/Button/Text")] // button with text + public void TestFindObject(By by, string value, string path) + { + int referenceId = altDriver.FindObject(AltBy.Path(path)).id; + var altObject = altDriver.FindObject(new AltBy(by, value)); + Assert.NotNull(altObject); + Assert.AreEqual(referenceId, altObject.id); + } + + + [Test] + public void TestDifferentCamera() + { + var altButton = altDriver.FindObject(AltBy.Name("Button"), AltBy.Name("Main Camera")); + altButton.Click(); + altButton.Click(); + var altElement = altDriver.FindObject(AltBy.Name("Capsule"), AltBy.Name("Main Camera")); + var altElement2 = altDriver.FindObject(AltBy.Name("Capsule"), AltBy.Name("Camera"));; + AltVector2 pozOnScreenFromMainCamera = new AltVector2(altElement.x, altElement.y); + AltVector2 pozOnScreenFromSecondaryCamera = new AltVector2(altElement2.x, altElement2.y); + + Assert.AreNotEqual(pozOnScreenFromSecondaryCamera, pozOnScreenFromMainCamera); + + } + + + [TestCase(By.COMPONENT, "NonExistent")] + [TestCase(By.ID, "NonExistent")] + [TestCase(By.LAYER, "NonExistent")] + [TestCase(By.NAME, "NonExistent")] + [TestCase(By.PATH, "/NonExistent")] + [TestCase(By.PATH, "//NonExistent/..")] + [TestCase(By.PATH, "//NonExistent/*")] + [TestCase(By.PATH, "//*[@tag=NonExistent]")] + [TestCase(By.PATH, "//*[@layer=NonExistent]")] + [TestCase(By.PATH, "//*[@name=NonExistent]")] + [TestCase(By.PATH, "//*[@component=NonExistent]")] + [TestCase(By.PATH, "//*[@id=NonExistent]")] + [TestCase(By.PATH, "//*[@text=NonExistent]")] + [TestCase(By.PATH, "//*[@tag=plane][@layer=NonExistent]")] + [TestCase(By.PATH, "//*[@tag=plane][@layer=Default][@name=NonExistent]")] + [TestCase(By.PATH, "//*[@tag=plane][@layer=Default][@name=Plane][@component=NonExistent]")] + [TestCase(By.PATH, "//*[@tag=plane][@layer=Default][@name=Plane][@component=MeshCollider][@id=NonExistent]")] + [TestCase(By.PATH, "//*[@tag=Untagged][@layer=UI][@name=Text][@component=CanvasRenderer][@id=f9dc3b3c-2791-42dc-9c0f-87ef7ff5e11d][@text=NonExistent]")] + [TestCase(By.PATH, "//*[contains(@tag,NonExistent)]")] + [TestCase(By.PATH, "//*[contains(@layer,NonExistent)]")] + [TestCase(By.PATH, "//*[contains(@name,NonExistent)]")] + [TestCase(By.PATH, "//*[contains(@component,NonExistent)]")] + [TestCase(By.PATH, "//*[contains(@id,NonExistent)]")] + [TestCase(By.PATH, "//*[contains(@text,NonExistent)]")] + [TestCase(By.PATH, "//*[contains(@tag,pla)][contains(@layer,NonExistent)]")] + [TestCase(By.PATH, "//*[contains(@tag,pla)][contains(@layer,Def)][contains(@name,NonExistent)]")] + [TestCase(By.PATH, "//*[contains(@tag,pla)][contains(@layer,Def)][contains(@name,Pla)][contains(@component,NonExistent)]")] + [TestCase(By.PATH, "//*[contains(@tag,pla)][contains(@layer,Def)][contains(@name,Pla)][contains(@component,MeshColl)][contains(@id,NonExistent)]")] + [TestCase(By.PATH, "//*[contains(@tag,Untag)][contains(@layer,U)][contains(@name,Tex)][contains(@component,CanvasRender)][contains(@id,f9dc3b3c-2791-42dc-9c0f-87ef7ff5e11d)][contains(@text,NonExistent)]")] + [TestCase(By.PATH, "//Canvas[100]")] + [TestCase(By.PATH, "//Canvas[-100]")] + [TestCase(By.TAG, "NonExistent")] + [TestCase(By.TEXT, "NonExistent")] + [TestCase(By.PATH, "//DisabledObject")] + [TestCase(By.PATH, "//DisabledObject/DisabledChild")] + public void TestFindNonExistentObject(By by, string value) + { + Assert.Throws(() => altDriver.FindObject(new AltBy(by, value))); + } + + + [Test] + public void TestWaitForObjectToNotExist() + { + altDriver.WaitForObjectNotBePresent(AltBy.Name("ObjectDestroyedIn5Secs")); + altDriver.WaitForObjectNotBePresent(AltBy.Name("Capsulee"), timeout: 1, interval: 0.5f); + } + + [Test] + public void TestWaitForObjectToNotExistFail() + { + try + { + altDriver.WaitForObjectNotBePresent(AltBy.Name("Capsule"), timeout: 1, interval: 0.5f); + Assert.Fail(); + } + catch (WaitTimeOutException exception) + { + Assert.AreEqual("Element //Capsule still found after 1 seconds", exception.Message); + } + } + + [Test] + public void TestFindObjectWithCameraId() + { + var altButton = altDriver.FindObject(AltBy.Path("//Button")); + altButton.Click(); + altButton.Click(); + var camera = altDriver.FindObject(AltBy.Path("//Camera")); + var altElement = altDriver.FindObject(AltBy.Component("CapsuleCollider"), AltBy.Id(camera.id.ToString())); + Assert.True(altElement.name.Equals("Capsule")); + var camera2 = altDriver.FindObject(AltBy.Path("//Main Camera")); + var altElement2 = altDriver.FindObject(AltBy.Component("CapsuleCollider"), AltBy.Id(camera2.id.ToString())); + Assert.AreNotEqual(altElement.GetScreenPosition(), altElement2.GetScreenPosition()); + } + + [Test] + public void TestWaitForObjectWithCameraId() + { + var altButton = altDriver.FindObject(AltBy.Path("//Button")); + altButton.Click(); + altButton.Click(); + var camera = altDriver.FindObject(AltBy.Path("//Camera")); + var altElement = altDriver.WaitForObject(AltBy.Component("CapsuleCollider"), AltBy.Id(camera.id.ToString())); + Assert.True(altElement.name.Equals("Capsule")); + var camera2 = altDriver.FindObject(AltBy.Path("//Main Camera")); + var altElement2 = altDriver.WaitForObject(AltBy.Component("CapsuleCollider"), AltBy.Id(camera2.id.ToString())); + Assert.AreNotEqual(altElement.GetScreenPosition(), altElement2.GetScreenPosition()); + } + + [Test] + public void TestFindObjectsWithCameraId() + { + var altButton = altDriver.FindObject(AltBy.Path("//Button")); + altButton.Click(); + altButton.Click(); + var camera = altDriver.FindObject(AltBy.Path("//Camera")); + var altElement = altDriver.FindObjects(AltBy.Name("Plane"), AltBy.Id(camera.id.ToString())); + Assert.True(altElement[0].name.Equals("Plane")); + var camera2 = altDriver.FindObject(AltBy.Path("//Main Camera")); + var altElement2 = altDriver.FindObjects(AltBy.Name("Plane"), AltBy.Id(camera2.id.ToString())); + Assert.AreNotEqual(altElement[0].GetScreenPosition(), altElement2[0].GetScreenPosition()); + } + + [Test] + public void TestWaitForObjectNotBePresentWithCameraId() + { + var camera2 = altDriver.FindObject(AltBy.Path("//Main Camera")); + altDriver.WaitForObjectNotBePresent(AltBy.Name("ObjectDestroyedIn5Secs"), AltBy.Id(camera2.id.ToString())); + + var allObjectsInTheScene = altDriver.GetAllElements(cameraAltBy: null); + Assert.IsTrue(!allObjectsInTheScene.Any(obj => obj.name.Equals("ObjectDestroyedIn5Secs"))); + } + + [Test] + public void TestWaitForObjectWhichContainsWithCameraId() + { + var camera2 = altDriver.FindObject(AltBy.Path("//Main Camera")); + var altElement = altDriver.WaitForObjectWhichContains(AltBy.Name("Canva"), AltBy.Id(camera2.id.ToString())); + Assert.AreEqual("Canvas", altElement.name); + + } + + [Test] + public void TestWaitForObjectWhichContainsWithTag() + { + var altElement = altDriver.WaitForObjectWhichContains(AltBy.Name("Canva"), AltBy.Tag("MainCamera")); + Assert.AreEqual("Canvas", altElement.name); + + } + + [Test] + public void TestWaitForObjectWhichContainsNonExistingCriteria() + { + Assert.Throws(() => altDriver.WaitForObjectWhichContains(AltBy.Name("Unexisting"), AltBy.Tag("MainCamera"), timeout: 2)); + } + + [Test] + public void TestWaitForObjectWhichContainsExistingCriteriaButNonExistingCamera() + { + Assert.Throws(() => altDriver.WaitForObjectWhichContains(AltBy.Name("Canva"), AltBy.Tag("Unexisting"), timeout: 2)); + } + + [TestCase(By.NAME, "Main Camera")] + [TestCase(By.COMPONENT, "Camera")] + [TestCase(By.TAG, "MainCamera")] + [TestCase(By.PATH, "/Main Camera")] + [TestCase(By.LAYER, "Default")] + [TestCase(By.ID, "4eb39f50-3403-473c-b684-915f7a40c393")] + public void TestFindObjectByCamera(By cameraBy, string cameraValue) + { + int referenceId = altDriver.FindObject(AltBy.Path("//Capsule")).id; + var altObject = altDriver.FindObject(AltBy.Path("//Capsule"), new AltBy(cameraBy, cameraValue)); + Assert.NotNull(altObject); + Assert.AreEqual(referenceId, altObject.id); + } + + [Test] + public void TestWaitForObjectByCamera() + { + var altButton = altDriver.FindObject(AltBy.Path("//Button")); + altButton.Click(); + altButton.Click(); + var altElement = altDriver.WaitForObject(AltBy.Component("CapsuleCollider"), AltBy.Name("Camera")); + Assert.True(altElement.name.Equals("Capsule")); + var altElement2 = altDriver.WaitForObject(AltBy.Component("CapsuleCollider"), AltBy.Name("Main Camera")); + Assert.AreNotEqual(altElement.GetScreenPosition(), altElement2.GetScreenPosition()); + } + + [Test] + public void TestFindObjectsByCamera() + { + var altButton = altDriver.FindObject(AltBy.Path("//Button")); + altButton.Click(); + altButton.Click(); + var altElement = altDriver.FindObjects(AltBy.Name("Plane"), AltBy.Name("Camera")); + Assert.True(altElement[0].name.Equals("Plane")); + var altElement2 = altDriver.FindObjects(AltBy.Name("Plane"), AltBy.Name("Main Camera")); + Assert.AreNotEqual(altElement[0].GetScreenPosition(), altElement2[0].GetScreenPosition()); + } + + [Test] + public void TestWaitForObjectNotBePresentByCamera() + { + altDriver.WaitForObjectNotBePresent(AltBy.Name("ObjectDestroyedIn5Secs"), AltBy.Name("Main Camera")); + + var allObjectsInTheScene = altDriver.GetAllElements(cameraAltBy: null); + Assert.IsTrue(!allObjectsInTheScene.Any(obj => obj.name.Equals("ObjectDestroyedIn5Secs"))); + } + + + [Test] + public void TestGetAltObjectWithCanvasParentButOnlyTransform() + { + var altObject = altDriver.FindObject(AltBy.Name("UIWithWorldSpace/Plane")); + Assert.NotNull(altObject); + } + + + [TestCase("//Dialog[0]", "Dialog", false)] + [TestCase("//Text[-1]", "Text", true)] + public void TestFindIndexer(string path, string expectedResult, bool enabled) + { + var altElement = altDriver.FindObject(AltBy.Path(path), enabled: enabled); + Assert.AreEqual(expectedResult, altElement.name); + } + + [Test] + public void TestGetObjectWithNumberAsName() + { + var numberObject = altDriver.FindObject(AltBy.Name("1234"), enabled: false); + Assert.NotNull(numberObject); + numberObject = altDriver.FindObject(AltBy.Path("//1234"), enabled: false); + Assert.NotNull(numberObject); + } + + [Test] + public void TestInvalidPaths() + { + Assert.Throws(() => altDriver.FindObject(AltBy.Path("//[1]"))); + Assert.Throws(() => altDriver.FindObject(AltBy.Path("CapsuleInfo[@tag=UI]"))); + Assert.Throws(() => altDriver.FindObject(AltBy.Path("//CapsuleInfo[@tag=UI/Text"))); + Assert.Throws(() => altDriver.FindObject(AltBy.Path("//CapsuleInfo[0/Text"))); + } + } +} diff --git a/Assets/Examples/Test/Editor/Driver/Testforscene1TestSampleWithAltDriverUnity.cs.meta b/Assets/Examples/Test/Editor/Driver/Testforscene1TestSampleWithAltDriverUnity.cs.meta new file mode 100644 index 000000000..d744be250 --- /dev/null +++ b/Assets/Examples/Test/Editor/Driver/Testforscene1TestSampleWithAltDriverUnity.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2e39e8951087241b3acc36817d2c1a95 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Examples/Test/Editor/Driver/TestsHelper.cs b/Assets/Examples/Test/Editor/Driver/TestsHelper.cs index 05160bb4c..9e4412b2c 100644 --- a/Assets/Examples/Test/Editor/Driver/TestsHelper.cs +++ b/Assets/Examples/Test/Editor/Driver/TestsHelper.cs @@ -46,5 +46,15 @@ public static AltDriver GetAltDriver() return new AltDriver(host: GetAltDriverHost(), port: GetAltDriverPort(), enableLogging: true); } + public static AltDriverUnity GetAltDriverUnity() + { + return new AltDriverUnity(host: GetAltDriverHost(), port: GetAltDriverPort(), enableLogging: true); + } + + public static AltDriverUnreal GetAltDriverUnreal() + { + return new AltDriverUnreal(host: GetAltDriverHost(), port: GetAltDriverPort(), enableLogging: true); + } + } } diff --git a/Bindings~/java/src/main/java/com/alttester/AltBy.java b/Bindings~/java/src/main/java/com/alttester/AltBy.java new file mode 100644 index 000000000..0c94a7d9d --- /dev/null +++ b/Bindings~/java/src/main/java/com/alttester/AltBy.java @@ -0,0 +1,62 @@ +/* + Copyright(C) 2024 Altom Consulting + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.alttester; + +import com.alttester.AltDriver.By; +import lombok.Getter; + + +@Getter +public class AltBy { + + public By by; + public String value; + + public AltBy(By by, String value) { + this.by = by; + this.value = value; + } + + public static AltBy id(String value) { + return new AltBy(By.ID, value); + } + + public static AltBy name(String value) { + return new AltBy(By.NAME, value); + } + + public static AltBy path(String value) { + return new AltBy(By.PATH, value); + } + + public static AltBy tag(String value) { + return new AltBy(By.TAG, value); + } + + public static AltBy text(String value) { + return new AltBy(By.TEXT, value); + } + + public static AltBy component(String value) { + return new AltBy(By.COMPONENT, value); + } + + public static AltBy layer(String value) { + return new AltBy(By.LAYER, value); + } +} diff --git a/Bindings~/java/src/main/java/com/alttester/Commands/FindObject/AltFindObjectsParams.java b/Bindings~/java/src/main/java/com/alttester/Commands/FindObject/AltFindObjectsParams.java index 8a352308f..8a03329ce 100644 --- a/Bindings~/java/src/main/java/com/alttester/Commands/FindObject/AltFindObjectsParams.java +++ b/Bindings~/java/src/main/java/com/alttester/Commands/FindObject/AltFindObjectsParams.java @@ -18,6 +18,7 @@ package com.alttester.Commands.FindObject; import com.alttester.AltMessage; +import com.alttester.AltBy; import com.alttester.AltDriver; import com.alttester.AltDriver.By; @@ -35,6 +36,11 @@ public Builder(AltDriver.By by, String value) { this.value = value; } + public Builder(AltBy altBy) { + this.by = altBy.getBy(); + this.value = altBy.getValue(); + } + public AltFindObjectsParams.Builder isEnabled(boolean enabled) { this.enabled = enabled; return this; @@ -46,6 +52,12 @@ public AltFindObjectsParams.Builder withCamera(By cameraBy, String cameraValue) return this; } + public AltFindObjectsParams.Builder withCamera(AltBy altBy) { + this.cameraBy = altBy.getBy(); + this.cameraValue = altBy.getValue(); + return this; + } + public AltFindObjectsParams build() { AltFindObjectsParams altFindObjectsParameters = new AltFindObjectsParams(); altFindObjectsParameters.by = this.by; diff --git a/Bindings~/python/alttester/__init__.py b/Bindings~/python/alttester/__init__.py index bda18de49..463198b07 100644 --- a/Bindings~/python/alttester/__init__.py +++ b/Bindings~/python/alttester/__init__.py @@ -16,7 +16,11 @@ """ from alttester.altdriver import AltDriver +from alttester.altby import AltBy +from alttester.altdriver_unity import AltDriverUnity +from alttester.altdriver_unreal import AltDriverUnreal from alttester.altobject import AltObject +from alttester.altobject_unreal import AltObjectUnreal from alttester.by import By from alttester.playerpref import PlayerPrefKeyType from alttester.keycode import AltKeyCode diff --git a/Bindings~/python/alttester/altby.py b/Bindings~/python/alttester/altby.py new file mode 100644 index 000000000..b247245b7 --- /dev/null +++ b/Bindings~/python/alttester/altby.py @@ -0,0 +1,63 @@ +""" + Copyright(C) 2024 Altom Consulting + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +""" + +from alttester.by import By + + +class AltBy: + """The AltBy class is used to find elements in the scene using By locators strategy and value.""" + + def __init__(self, by=None, value=None): + self.by = by + self.value = value + + def __str__(self): + return f"By.{self.by}({self.value})" + + def __repr__(self): + return f"By.{self.by}({self.value})" + + def __eq__(self, other): + return self.by == other.by and self.value == other.value + + @classmethod + def name(cls, name): + return cls(by=By.NAME, value=name) + + @classmethod + def id(cls, id): + return cls(by=By.ID, value=id) + + @classmethod + def path(cls, path): + return cls(by=By.PATH, value=path) + + @classmethod + def tag(cls, tag): + return cls(by=By.TAG, value=tag) + + @classmethod + def layer(cls, layer): + return cls(by=By.LAYER, value=layer) + + @classmethod + def text(cls, text): + return cls(by=By.TEXT, value=text) + + @classmethod + def component(cls, component): + return cls(by=By.COMPONENT, value=component) diff --git a/Bindings~/python/alttester/altdriver.py b/Bindings~/python/alttester/altdriver.py index cbcb6e1e2..8b0956bd3 100644 --- a/Bindings~/python/alttester/altdriver.py +++ b/Bindings~/python/alttester/altdriver.py @@ -16,38 +16,23 @@ """ import warnings -import sys - -from loguru import logger - import alttester.commands as commands from alttester.commands.base_command import Command from alttester.__version__ import VERSION -from alttester._websocket import WebsocketConnection, CommandHandler, NotificationHandler +from alttester._websocket import ( + CommandHandler, + NotificationHandler, +) from alttester.altobject import AltObject from alttester.by import By +from alttester.altby import AltBy +from alttester.altdriver_base import AltDriverBase -warnings.filterwarnings( - "default", category=DeprecationWarning, module=__name__) - - -class AltDriver: - """The driver object will help interacting with all the application objects, their properties and methods. - - When you instantiate an ``AltDriver`` object in your tests, you can use it to “drive” your application like one of - your users would, by interacting with all the application objects, their properties and methods. An ``AltDriver`` - instance will connect to the AltTester® Server. +warnings.filterwarnings("default", category=DeprecationWarning, module=__name__) - Args: - host (:obj:`str`, optional): The host to connect to. - port (:obj:`int`, optional): The port to connect to. - app_name (:obj:`str`, optional): The application name. Defaults to ``__default__``. - enable_logging (:obj:`bool`, optional): If set to ``True`` will turn on logging, by default logging is disabled. - timeout (:obj:`int`, :obj:`float`, optional): The timeout duration for establishing a connection, in seconds. - If set to ``None``, the connection attempt will wait indefinitely. The default value is ``60`` seconds. - """ +class AltDriver(AltDriverBase): def __init__( self, @@ -59,877 +44,178 @@ def __init__( platform="unknown", platform_version="unknown", device_instance_id="unknown", - app_id="unknown" + app_id="unknown", ): - self.host = host - self.port = port - self.app_name = app_name - self.enable_logging = enable_logging - self.timeout = timeout - self.platform = platform - self.platform_version = platform_version - self.device_instance_id = device_instance_id - self.app_id = app_id - - self._config_logging(self.enable_logging) - - logger.debug( - "Connecting to AltTester(R) on host: '{}', port: '{}' and app name: '{}'.", - self.host, - self.port, - self.app_name - ) - - self._command_handler = CommandHandler() - self._notification_handler = NotificationHandler() - self._connection = WebsocketConnection( - host=self.host, - port=self.port, - timeout=self.timeout, - path="altws", - params={ - "appName": self.app_name, - "platform": self.platform, - "platformVersion": self.platform_version, - "deviceInstanceId": self.device_instance_id, - "appId": self.app_id, - "driverType": "SDK" - }, - command_handler=self._command_handler, - notification_handler=self._notification_handler - ) - self._connection.connect() - self._check_server_version() - - def __repr__(self): - return "{}({!r}, {!r}, {!r}, {!r}, {!r})".format( - self.__class__.__name__, - self.host, - self.port, - self.app_name, - self.enable_logging, - self.timeout - ) - - @staticmethod - def _config_logging(enable_logging): - if enable_logging: - logger.configure( - handlers=[ - dict(sink=sys.stdout, diagnose=False), - dict(sink="./AltTester.log", enqueue=False, - serialize=True, mode="w", diagnose=False), - ], - levels=[dict(name="DEBUG")], - activation=[("alttester", True)], - ) - else: - logger.disable("alttester") - - @staticmethod - def _split_version(version): - parts = version.split(".") - return (parts[0], parts[1]) if len(parts) > 1 else ("", "") - - def _check_server_version(self): - server_version = commands.GetServerVersion.run(self._connection) - logger.info("Connection established with instrumented Unity app. AltTester(R) Version: {}".format( - server_version)) - - major_server, minor_server = self._split_version(server_version) - - server_major = int(major_server) - server_minor = int(minor_server) - - is_supported = (server_major == 2 and server_minor == 2) or ( - server_major == 1 and server_minor == 0) - - if not is_supported: - message = "Version mismatch. AltDriver version is {}. AltTester(R) version is {}.".format( - VERSION, server_version) - logger.warning(message) - - def _get_alt_object(self, data): - if data is None: - return None - - alt_object = AltObject(self, data) - - logger.debug( - "Element {} found at x: {} y: {} mobileY: {}", - alt_object.name, - alt_object.x, - alt_object.y, - alt_object.mobileY - ) - - return alt_object - - def _get_alt_objects(self, data): - if data is None: - return None - - alt_objects = [] - - for element in data: - alt_object = AltObject(self, element) - alt_objects.append(alt_object) - - logger.debug( - "Element {} found at x: {} y: {} mobileY: {}", - alt_object.name, - alt_object.x, - alt_object.y, - alt_object.mobileY - ) - - return alt_objects - - def stop(self): - """Close the connection to AltTester.""" - - self._connection.close() - - def get_command_response_timeout(self): - """Gets the current command response timeout for the AltTester® connection. - - Return: - int or float: The current command response time. - - """ - - return self._connection.set_command_timeout() - - def set_command_response_timeout(self, timeout): - """Sets the command response timeout for the AltTester® connection. - - Args: - timeout (:obj:`int` or :obj:`float`): The new command response timeout in seconds. - - """ - - self._connection.set_command_timeout(timeout) - - def get_delay_after_command(self): - """Gets the current delay after a command.""" - - return Command.get_delay_after_command() - - def set_delay_after_command(self, delay): - """Set the delay after a command. - - Args: - delay (:obj:`int` or :obj:`float`): The new delay a after a command. - """ - - Command.set_delay_after_command(delay) - - def set_server_logging(self, logger, log_level): - """Sets the level of logging on AltTester. - - Args: - logger (:obj:`AltLogger`): The type of logger. - log_lever (:obj:`AltLogLevel`): The logging level. - - """ - - commands.SetServerLogging.run(self._connection, logger, log_level) - - def call_static_method(self, type_name, method_name, assembly, parameters=None, type_of_parameters=None): - """Invoke a static method from your application. - - Args: - type_name (:obj:`str`): The name of the script. If the script has a namespace the format should look like - this: ``"namespace.typeName"``. - method_name (:obj:`str`): The name of the public method that we want to call. If the method is inside a - static property/field to be able to call that method, methodName need to be the following format - ``"propertyName.MethodName"``. - assembly (:obj:`str`): The name of the assembly containing the script. - parameters (:obj:`list`, :obj:`tuple`, optional): Defaults to ``None``. - type_of_parameters (:obj:`list`, :obj:`tuple`, optional): Defaults to ``None``. - - Return: - str: The value returned by the method is serialized to a JSON string. - - """ - - return commands.CallMethod.run( - self._connection, - type_name, method_name, parameters=parameters, type_of_parameters=type_of_parameters, assembly=assembly - ) - - def get_current_scene(self): - """Returns the name of the current scene. - - Returns: - str: The name of the current scene. - - """ - - return commands.GetCurrentScene.run(self._connection) - - def load_scene(self, scene_name, load_single=True): - """Loads a scene. - - Args: - scene_name (:obj:`str`): The name of the scene to be loaded. - load_single (:obj`bool`, optional): Sets the loading mode. If set to ``False`` the scene will be loaded - additive, together with the current loaded scenes. Defaults to ``True``. - - """ - - commands.LoadScene.run( - self._connection, - scene_name, load_single - ) - - def wait_for_current_scene_to_be(self, scene_name, timeout=30, interval=1): - """Waits for the scene to be loaded for a specified amount of time. - - Args: - scene_name (:obj:`str`): The name of the scene to wait for. - timeout (obj:`int`, :obj:`float`, optional): The time measured in seconds to wait for the specified scene. - Defaults to ``30``. - interval (obj:`int`, :obj:`float`, optional): How often to check that the scene was loaded in the given - timeout. Defaults to ``1``. - - Returns: - str: The name of the loaded scene. - - """ - - return commands.WaitForCurrentSceneToBe.run( - self._connection, - scene_name, timeout, interval - ) - - def unload_scene(self, scene_name): - """Unloads a scene. - - Args: - scene_name (:obj:`str`): The name of the scene to be unloaded. - - """ - - commands.UnloadScene.run(self._connection, scene_name) - - def get_all_loaded_scenes(self): - """Returns all the scenes that have been loaded. - - Returns: - :obj:`list` of :obj:`str`: A list containing the names of all the loaded scenes. - - """ - - return commands.GetAllLoadedScenes.run(self._connection) - - def get_time_scale(self): - """Returns the value of the time scale. - - Returns: - float: The value of the time scale. - - """ - - return commands.GetTimeScale.run(self._connection) - - def set_time_scale(self, time_scale): - """Sets the value of the time scale. - - Args: - time_scale (:obj:`float`, :obj:`int`): The value of the time scale. - - """ - - commands.SetTimeScale.run(self._connection, time_scale) - - def get_player_pref_key(self, key_name, key_type): - """Returns the value for a given key from PlayerPrefs. - - Args: - key_name (:obj:`str`): The name of the key to be retrived. - key_type (:obj:`PlayerPrefKeyType`): The type of the key. - - """ - - return commands.GetPlayerPrefKey.run( - self._connection, - key_name, key_type - ) - - def set_player_pref_key(self, key_name, value, key_type): - """Sets the value for a given key in PlayerPrefs. - - Args: - key_name (:obj:`str`): The name of the key to be set. - value (:obj:`str`): The new value of be set. - key_type (:obj:`PlayerPrefKeyType`): The type of the key. - - """ - - commands.SetPlayerPrefKey.run( - self._connection, - key_name, value, key_type - ) - - def delete_player_pref_key(self, key_name): - """Removes a key and its corresponding value from PlayerPrefs. - - Args: - key_name (:obj:`str`): The name of the key to be deleted. - - """ - - commands.DeletePlayerPrefKey.run(self._connection, key_name) - - def delete_player_pref(self): - """Removes all keys and values from PlayerPref.""" - - commands.DeletePlayerPref.run(self._connection) + super().__init__( + host, + port, + app_name, + enable_logging, + timeout, + platform, + platform_version, + device_instance_id, + app_id, + ) + self.__command_handler = CommandHandler() + self.__notification_handler = NotificationHandler() def find_object(self, by, value, camera_by=By.NAME, camera_value="", enabled=True): - """Finds the first object in the scene that respects the given criteria. - - Args: - by (:obj:`By`): Sets what criteria to use in order to find the object. - value (:obj:`str`): The value to which an object will be compared to see if they respect the criteria or - not. - camera_by (:obj:`By`, optional): Set what criteria to use in order to find the camera. - camera_value (:obj:`str`, optional): The value to which all the cameras in the scene will be compared to - see if they respect the criteria or not. If no camera is given it will search through all camera that - are in the scene until some camera sees the object or return the screen coordinate of the object - calculated to the last camera in the scene. - enabled (:obj:`bool`, optional): If ``True`` will match only objects that are active in hierarchy. If - ``False`` will match all objects. Defaults to ``True``. - - Returns: - AltObject: The object. - - """ - - data = commands.FindObject.run( - self._connection, - by, value, camera_by, camera_value, enabled - ) - - return self._get_alt_object(data) - - def find_objects(self, by, value, camera_by=By.NAME, camera_value="", enabled=True): - """Finds all objects in the scene that respects the given criteria. - - Args: - by (:obj:`By`): Sets what criteria to use in order to find the objects. - value (:obj:`str`): The value to which an object will be compared to see if they respect the criteria or - not. - camera_by (:obj:`By`, optional): Set what criteria to use in order to find the camera. - camera_value (:obj:`str`, optional): The value to which all the cameras in the scene will be compared to - see if they respect the criteria or not. If no camera is given it will search through all camera that - are in the scene until some camera sees the object or return the screen coordinate of the object - calculated to the last camera in the scene. - enabled (:obj:`bool`, optional): If ``True`` will match only objects that are active in hierarchy. If - ``False`` will match all objects. Defaults to ``True``. - - Returns: - list of AltObject: The list of objects. - - """ - - data = commands.FindObjects.run( - self._connection, - by, value, camera_by, camera_value, enabled - ) - - return self._get_alt_objects(data) - - def find_object_which_contains(self, by, value, camera_by=By.NAME, camera_value="", enabled=True): - """Finds the first object in the scene that respects the given criteria. - - Args: - by (:obj:`By`): Sets what criteria to use in order to find the object. - value (:obj:`str`): The value to which an object will be compared to see if they respect the criteria or - not. - camera_by (:obj:`By`, optional): Set what criteria to use in order to find the camera. - camera_value (:obj:`str`, optional): The value to which all the cameras in the scene will be compared to - see if they respect the criteria or not. If no camera is given it will search through all camera that - are in the scene until some camera sees the object or return the screen coordinate of the object - calculated to the last camera in the scene. - enabled (:obj:`bool`, optional): If ``True`` will match only objects that are active in hierarchy. If - ``False`` will match all objects. Defaults to ``True``. - - Returns: - AltObject: The object. - - """ - - data = commands.FindObjectWhichContains.run( - self._connection, - by, value, camera_by, camera_value, enabled - ) - - return self._get_alt_object(data) - - def find_objects_which_contain(self, by, value, camera_by=By.NAME, camera_value="", enabled=True): - """Finds all objects in the scene that respects the given criteria. - - Args: - by (:obj:`By`): Sets what criteria to use in order to find the objects. - value (:obj:`str`): The value to which an object will be compared to see if they respect the criteria or - not. - camera_by (:obj:`By`): Set what criteria to use in order to find the camera. - camera_value (:obj:`str`, optional): The value to which all the cameras in the scene will be compared to - see if they respect the criteria or not. If no camera is given it will search through all camera that - are in the scene until some camera sees the object or return the screen coordinate of the object - calculated to the last camera in the scene. - enabled (:obj:`bool`): If ``True`` will match only objects that are active in hierarchy. If ``False`` will - match all objects. Defaults to ``True``. - - Returns: - list of AltObjects: The list of objects. - """ + Find an object in the scene using the specified locator strategy and value. - data = commands.FindObjectsWhichContain.run( - self._connection, - by, value, camera_by, camera_value, enabled - ) - - return self._get_alt_objects(data) - - def wait_for_object(self, by, value, camera_by=By.NAME, camera_value="", timeout=20, interval=0.5, enabled=True): - """Waits until it finds an object that respects the given criteria or until timeout limit is reached. - - Args: - by (:obj:`By`): Sets what criteria to use in order to find the object. - value (:obj:`str`): The value to which an object will be compared to see if they respect the criteria or - not. - camera_by (:obj:`By`, optional): Set what criteria to use in order to find the camera. - camera_value (:obj:`str`, optional): The value to which all the cameras in the scene will be compared to - see if they respect the criteria or not. If no camera is given it will search through all camera that - are in the scene until some camera sees the object or return the screen coordinate of the object - calculated to the last camera in the scene. - timeout (:obj:`int`, :obj:`float`, optional): The number of seconds that it will wait for object. - interval (:obj:`int`, :obj:`float`, optional): The number of seconds after which it will try to find the - object again. The interval should be smaller than the timeout. - enabled (:obj:`bool`, optional): If ``True`` will match only objects that are active in hierarchy. If - ``False`` will match all objects. Defaults to ``True``. - - Returns: - AltObject: The object. - + :param by: The locator strategy to use. + :param value: The value to search for. + :param camera_by: The locator strategy to use for the camera. + :param camera_value: The value to search for the camera. + :param enabled: The object enabled state. + :return: The AltObject instance. """ + altby = AltBy(by, value) + camera_altby = AltBy(camera_by, camera_value) + return self._AltDriverBase__find_object(altby, camera_altby, enabled) - data = commands.WaitForObject.run( - self._connection, - by, value, camera_by, camera_value, timeout, interval, enabled - ) - - return self._get_alt_object(data) - - def wait_for_object_which_contains(self, by, value, camera_by=By.NAME, camera_value="", timeout=20, interval=0.5, - enabled=True): - """Waits until it finds an object that respects the given criteria or time runs out and will throw an error. - - Args: - by (:obj:`By`): Sets what criteria to use in order to find the object. - value (:obj:`str`): The value to which an object will be compared to see if they respect the criteria or - not. - camera_by (:obj:`By`): Set what criteria to use in order to find the camera. - camera_value (:obj:`str`, optional): The value to which all the cameras in the scene will be compared to - see if they respect the criteria or not. If no camera is given it will search through all camera that - are in the scene until some camera sees the object or return the screen coordinate of the object - calculated to the last camera in the scene. - timeout (:obj:`int`, :obj:`float`, optional): The number of seconds that it will wait for object. - interval (:obj:`int`, :obj:`float`, optional): The number of seconds after which it will try to find the - object again. The interval should be smaller than the timeout. - enabled (:obj:`bool`, optional): If ``True`` will match only objects that are active in hierarchy. If - ``False`` will match all objects. Defaults to ``True``. - - Returns: - AltObject: The object. - - """ - - data = commands.WaitForObjectWhichContains.run( - self._connection, - by, value, camera_by, camera_value, timeout, interval, enabled - ) - - return self._get_alt_object(data) - - def wait_for_object_to_not_be_present(self, by, value, camera_by=By.NAME, camera_value="", timeout=20, interval=0.5, - enabled=True): - """Waits until the object in the scene that respects the given criteria is no longer in the scene or until - timeout limit is reached. - - Args: - by (:obj:`By`): Sets what criteria to use in order to find the object. - value (:obj:`str`): The value to which an object will be compared to see if they respect the criteria or - not. - camera_by (:obj:`By`): Set what criteria to use in order to find the camera. - camera_value (:obj:`str`, optional): The value to which all the cameras in the scene will be compared to - see if they respect the criteria or not. If no camera is given it will search through all camera that - are in the scene until some camera sees the object or return the screen coordinate of the object - calculated to the last camera in the scene. - timeout (:obj:`int`, :obj:`float`, optional): The number of seconds that it will wait for object. - interval (:obj:`int`, :obj:`float`, optional): The number of seconds after which it will try to find the - object again. The interval should be smaller than the timeout. - enabled (:obj:`bool`, optional): If ``True`` will match only objects that are active in hierarchy. If - ``False`` will match all objects. Defaults to ``True``. - + def find_objects(self, by, value, camera_by=By.NAME, camera_value="", enabled=True): """ + Find objects in the scene using the specified locator strategy and value. - commands.WaitForObjectToNotBePresent.run( - self._connection, - by, value, camera_by, camera_value, timeout, interval, enabled - ) - - def get_all_elements(self, camera_by=By.NAME, camera_value="", enabled=True): - """Returns information about every objects loaded in the currently loaded scenes. This also means objects that - are set as DontDestroyOnLoad. - - Args: - camera_by (:obj:`By`): Set what criteria to use in order to find the camera. - camera_value (:obj:`str`, optional): The value to which all the cameras in the scene will be compared to - see if they respect the criteria or not. If no camera is given it will search through all camera that - are in the scene until some camera sees the object or return the screen coordinate of the object - calculated to the last camera in the scene. - enabled (:obj:`bool`, optional): If ``True`` will match only objects that are active in hierarchy. If - ``False`` will match all objects. Defaults to ``True``. - - Returns: - list of AltObjects: The list of objects. - + :param by: The locator strategy to use. + :param value: The value to search for. + :param camera_by: The locator strategy to use for the camera. + :param camera_value: The value to search for the camera. + :param enabled: The object enabled state. + :return: The list of AltObject instances. """ + altby = AltBy(by, value) + camera_altby = AltBy(camera_by, camera_value) + return self._AltDriverBase__find_objects(altby, camera_altby, enabled) - return self.find_objects(By.PATH, "//*", camera_by=camera_by, camera_value=camera_value, enabled=enabled) - - def move_mouse(self, coordinates, duration=0.1, wait=True): - """Simulates mouse movement in your application. - - Args: - coordinates (:obj:`dict`): The screen coordinates - duration (:obj:`int`, optional): The time measured in seconds to move the mouse from current position to - the set location. Defaults to ``0.1`` - wait (:obj:`bool`, optional): If set wait for command to finish. Defaults to ``True``. - + def find_object_which_contains( + self, by, value, camera_by=By.NAME, camera_value="", enabled=True + ): """ + Find an object in the scene which contains the specified value. - commands.MoveMouse.run(self._connection, coordinates, duration, wait) - - def scroll(self, speed_vertical=1, duration=0.1, wait=True, speed_horizontal=1): - """Simulate scroll mouse action in your application. - - Args: - speed_vertical (:obj:`int`, :obj:`float`): Set how fast to scroll. Positive values will scroll up and - negative values will scroll down. Defaults to ``1`` - duration (:obj:`int`, :obj:`float`, optional): The duration of the scroll in seconds. Defaults to ``0.1``. - wait (:obj:`bool`, optional): If set wait for command to finish. Defaults to ``True``. - speed_horizontal (:obj:`int`, :obj:`float`): Set how fast to scroll right or left. Defaults to ``1`` - + :param by: The locator strategy to use. + :param value: The value to search for. + :param camera_by: The locator strategy to use for the camera. + :param camera_value: The value to search for the camera. + :param enabled: The object enabled state. + :return: The AltObject instance. """ - - commands.Scroll.run( - self._connection, - speed_vertical, duration, - wait, speed_horizontal + altby = AltBy(by, value) + camera_altby = AltBy(camera_by, camera_value) + return self._AltDriverBase__find_object_which_contains( + altby, camera_altby, enabled ) - def click(self, coordinates, count=1, interval=0.1, wait=True): - """Click at screen coordinates. - - Args: - coordinates (:obj:`dict`): The screen coordinates. - count (:obj:`int`, optional): Number of taps. Defaults to ``1``. - interval (:obj:`int`, :obj:`float`, optional): The interval between taps in seconds. Defaults to ``0.1``. - wait (:obj:`bool`, optional): If set to ``True`` Wait for command to finish. - - """ - - commands.ClickCoordinates.run( - self._connection, coordinates, count, interval, wait) - - def key_down(self, key_code, power=1): - """Simulates that a specific key was pressed without taking into consideration the duration of the press. - - Args: - key_code (:obj:`AltKeyCode`): The key code of the key simulated to be pressed. - power (:obj:`float`, optional): A value between [-1,1] used for joysticks to indicate how hard the button - was pressed. Defaults to ``1``. - - """ - - self.keys_down([key_code], power=power) - - def keys_down(self, key_codes, power=1): - """Simulates that multiple keys were pressed without taking into consideration the duration of the press. - - Args: - key_codes (:obj:`list` of :obj:`AltKeyCode`): The key codes of the keys simulated to be pressed. - power (:obj:`float`): A value between [-1,1] used for joysticks to indicate how hard the button was - pressed. Defaults to ``1``. - - """ - - commands.KeysDown.run(self._connection, key_codes, power) - - def key_up(self, key_code): - """Simulates that a specific key was released. - - Args: - key_code (:obj:`AltKeyCode`): The key code of the key simulated to be released. - - """ - - self.keys_up([key_code]) - - def keys_up(self, key_codes): - """Simulates that multiple keys were released. - - Args: - key_codes (:obj:`list` of :obj:`AltKeyCode`): The key codes of the keys simulated to be released. - - """ - - commands.KeysUp.run(self._connection, key_codes) - - def press_key(self, key_code, power=1, duration=0.1, wait=True): - """Simulates key press action in your application. - - Args: - key_code (:obj:`AltKeyCode`): The key code of the key simulated to be pressed. - power (:obj:`int`, :obj:`float`, optional): A value between [-1,1] used for joysticks to indicate how hard - the button was pressed. Defaults to ``1``. - duration (:obj:`float`, optional): The time measured in seconds from the key press to the key release. - wait (:obj:`bool`, optional): If set wait for command to finish. Defaults to ``True``. - - """ - - self.press_keys([key_code], power=power, duration=duration, wait=wait) - - def press_keys(self, key_codes, power=1, duration=0.1, wait=True): - """Simulates multiple keypress action in your application. - - Args: - key_codes (:obj:`list` of :obj:`AltKeyCode`): The key codes of the keys simulated to be pressed. - power (:obj:`float`): A value between [-1,1] used for joysticks to indicate how hard the buttons were - pressed. Defaults to ``1``. - duration (:obj:`float`): The time measured in seconds from the key press to the key release. - wait (:obj:`bool`): If set wait for command to finish. Defaults to ``True``. - - """ - - commands.PressKeys.run( - self._connection, key_codes, power, duration, wait) - - def begin_touch(self, coordinates): - """Simulates starting of a touch on the screen. - - Args: - coordinates (:obj:`dict`): The screen coordinates. - - Returns: - str: A ``finger_id`` to use with ``move_touch`` and ``end_touch``. - - """ - - return commands.BeginTouch.run(self._connection, coordinates) - - def move_touch(self, finger_id, coordinates): - """Simulates a touch movement on the screen. Move the touch created with ``begin_touch`` from the previous - position to the position given as parameters. - - Args: - finger_id (:obj:`str`): The value returned by ``begin_touch``. - coordinates (:obj:`dict`): Screen coordinates where the touch will be moved. - + def find_objects_which_contain( + self, by, value, camera_by=By.NAME, camera_value="", enabled=True + ): """ + Find objects in the scene which contain the specified value. - commands.MoveTouch.run(self._connection, finger_id, coordinates) - - def end_touch(self, finger_id): - """Simulates ending of a touch on the screen. This command will destroy the touch making it no longer usable to - other movements. - - Args: - finger_id (:obj:`str`): The value returned by ``begin_touch``. - + :param by: The locator strategy to use. + :param value: The value to search for. + :param camera_by: The locator strategy to use for the camera. + :param camera_value: The value to search for the camera. + :param enabled: The object enabled state. + :return: The list of AltObject instances. """ - - commands.EndTouch.run(self._connection, finger_id) - - def swipe(self, start, end, duration=0.1, wait=True): - """Simulates a swipe action between two points. - - Args: - start (:obj:`dict`): Coordinates of the screen where the swipe begins. - end (:obj:`dict`): Coordinates of the screen where the swipe ends. - duration (:obj:`int`, :obj:`float`, optional): The time measured in seconds to move the mouse from start to - end location. Defaults to ``0.1``. - wait (:obj:`bool`, optional): If set wait for command to finish. Defaults to ``True``. - - """ - - commands.Swipe.run( - self._connection, - start, end, duration, wait + altby = AltBy(by, value) + camera_altby = AltBy(camera_by, camera_value) + return self._AltDriverBase__find_objects_which_contain( + altby, camera_altby, enabled ) - def multipoint_swipe(self, positions, duration=0.1, wait=True): - """Simulates a multipoint swipe action. - - Args: - positions (:obj:`List[dict]`): A list of positions on the screen where the swipe be made. - duration (:obj:`float`): The time measured in seconds to swipe from first position to the last position. - Defaults to ``0.1``. - wait (:obj:`bool`): If set wait for command to finish. Defaults to ``True``. - - """ - - commands.MultipointSwipe.run( - self._connection, positions, duration, wait) - - def tap(self, coordinates, count=1, interval=0.1, wait=True): - """Tap at screen coordinates. - - Args: - coordinates (:obj:`dict`): The screen coordinates. - count (:obj:`int`, optional): Number of taps. Defaults to ``1``. - interval (:obj:`int`, :obj:`float`, optional): The interval between taps in seconds. Defaults to ``0.1``. - wait (:obj:`bool`, optional): If set wait for command to finish. Defaults to ``True``. - + def wait_for_object( + self, + by, + value, + camera_by=By.NAME, + camera_value="", + timeout=20, + interval=0.5, + enabled=True, + ): """ + Wait for an object in the scene using the specified locator strategy and value. - commands.TapCoordinates.run( - self._connection, coordinates, count, interval, wait) - - def tilt(self, acceleration, duration=0.1, wait=True): - """Simulates device rotation action in your application. - - Args: - acceleration (:obj:`dict`): The linear acceleration of a device. - duration (:obj:`int`, :obj:`float`, optional): How long the rotation will take in seconds. - Defaults to ``0.1``. - wait (:obj:`bool`, optional): If set wait for command to finish. Defaults to ``True``. - + :param by: The locator strategy to use. + :param value: The value to search for. + :param timeout: The timeout in seconds. + :param camera_by: The locator strategy to use for the camera. + :param camera_value: The value to search for the camera. + :param enabled: The object enabled state. + :return: The AltObject instance. """ - - commands.Tilt.run(self._connection, acceleration, duration, wait) - - def get_application_screensize(self): - screen_width = self.call_static_method( - "UnityEngine.Screen", "get_width", - "UnityEngine.CoreModule" - ) - screen_height = self.call_static_method( - "UnityEngine.Screen", "get_height", - "UnityEngine.CoreModule" + altby = AltBy(by, value) + camera_altby = AltBy(camera_by, camera_value) + return self._AltDriverBase__wait_for_object( + altby, camera_altby, timeout, interval, enabled ) - return (screen_width, screen_height) - - def get_png_screenshot(self, path): - """Creates a screenshot of the current scene in png format at the given path. - - Args: - path (:obj:`str`): The path where the image will be created. + def wait_for_object_which_contains( + self, + by, + value, + camera_by=By.NAME, + camera_value="", + timeout=20, + interval=0.5, + enabled=True, + ): """ + Wait for an object in the scene which contains the specified value. - commands.GetPNGScreenshot.run(self._connection, path) - - def hold_button(self, coordinates, duration=0.1, wait=True): - """Simulates holding left click button down for a specified amount of time at given coordinates. - - Args: - coordinates (:obj:`dict`): The coordinates where the button is held down - duration ((:obj:`int`, :obj:`float`, optional): The time measured in seconds to keep the button down. - Defaults to ``0.1``. - wait (:obj:`bool`, optional): If set wait for command to finish. Defaults to ``True``. - + :param by: The locator strategy to use. + :param value: The value to search for. + :param timeout: The timeout in seconds. + :param camera_by: The locator strategy to use for the camera. + :param camera_value: The value to search for the camera. + :param enabled: The object enabled state. + :return: The AltObject instance. """ - - return commands.Swipe.run( - self._connection, - coordinates, coordinates, duration, wait + altby = AltBy(by, value) + camera_altby = AltBy(camera_by, camera_value) + return self._AltDriverBase__wait_for_object_which_contains( + altby, camera_altby, timeout, interval, enabled ) - def get_static_property(self, component_name, property_name, assembly, max_depth=2): - """Returns the value of the static field or property given as parameter. - - Args: - component_name (:obj:`str`): The name of the component containing the field or property - to be retrieved. - property_name (:obj:`str`): The name of the field or property to be retrieved. - assembly (:obj:`str`): The name of the assembly containing the component mentioned above. - max_depth (:obj:`int`, optional): The value determining how deep to go in the hierarchy of objects - to find the field or property. - + def wait_for_object_to_not_be_present( + self, + by, + value, + camera_by=By.NAME, + camera_value="", + timeout=20, + interval=0.5, + enabled=True, + ): """ + Wait for an object to not be present in the scene using the specified locator strategy and value. - return commands.GetStaticProperty.run( - self._connection, - component_name, property_name, assembly, max_depth - ) - - def set_static_property(self, component_name, property_name, assembly, updated_value): - """Set the value of the static field or property given as parameter. - - Args: - component_name (:obj:`str`): The name of the component containing the field or property - to be retrieved. - property_name (:obj:`str`): The name of the field or property to be retrieved. - assembly (:obj:`str`): The name of the assembly containing the component mentioned above. - updated_value (:obj:`str`): The value of the field or property to be updated. + :param by: The locator strategy to use. + :param value: The value to search for. + :param timeout: The timeout in seconds. + :param camera_by: The locator strategy to use for the camera. + :param camera_value: The value to search for the camera. + :param enabled: The object enabled state. + :return: The AltObject instance. """ - - return commands.SetStaticProperty.run( - self._connection, - component_name, property_name, assembly, updated_value + altby = AltBy(by, value) + camera_altby = AltBy(camera_by, camera_value) + return self._AltDriverBase__wait_for_object_to_not_be_present( + altby, camera_altby, timeout, interval, enabled ) - def find_object_at_coordinates(self, coordinates): - """Retrieves the Unity object at given coordinates - - Uses EventSystem.RaycastAll to find object. If no object is found then it uses UnityEngine.Physics.Raycast - and UnityEngine.Physics2D.Raycast and returns the one closer to the camera. - - Args: - coordinates (:obj:`dict`): The screen coordinates. - - Returns: - AltObject: The UI object hit by event system Raycast, ``None`` otherwise. - - """ - - data = commands.FindObjectAtCoordinates.run( - self._connection, coordinates) - return self._get_alt_object(data) - - def add_notification_listener(self, notification_type, notification_callback, overwrite=True): - """Activates a notification that the tester will send. - - Args: - notification_type (:obj:`int`): Flag that indicates which notification to be turned on. - notification_callback (:obj:`method`): callback used when a notification is received. - overwrite (:obj:'bool', optional): Flag to set if the new callback will overwrite the other - callbacks or just append. - + def get_all_elements(self, camera_by=By.NAME, camera_value="", enabled=True): """ + Get all elements in the scene. - commands.AddNotificationListener.run( - self._connection, notification_type, notification_callback, overwrite) - - def remove_notification_listener(self, notification_type): - """Clear list of callback for the notification type and turn off the notification in tester. - - Args: - notification_type (:obj:`int`): Flag that indicates which notification to be turned off. - + :param camera_by: The locator strategy to use for the camera. + :param camera_value: The value to search for the camera. + :return: The list of AltObject instances. """ - - commands.RemoveNotificationListener.run( - self._connection, notification_type) - - def reset_input(self): - """Clear all active input actions simulated by AltTester.""" - - commands.ResetInput.run(self._connection) + camera_altby = AltBy(camera_by, camera_value) + return self._AltDriverBase__get_all_elements(camera_altby, enabled) diff --git a/Bindings~/python/alttester/altdriver_base.py b/Bindings~/python/alttester/altdriver_base.py new file mode 100644 index 000000000..19b72732d --- /dev/null +++ b/Bindings~/python/alttester/altdriver_base.py @@ -0,0 +1,945 @@ +""" + Copyright(C) 2025 Altom Consulting + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +""" + +import warnings +import sys + +from loguru import logger + +import alttester.commands as commands +from alttester.commands.base_command import Command +from alttester.__version__ import VERSION +from alttester._websocket import ( + WebsocketConnection, + CommandHandler, + NotificationHandler, +) +from alttester.altobject import AltObject +from alttester.altby import AltBy +from alttester.by import By + + +warnings.filterwarnings("default", category=DeprecationWarning, module=__name__) + + +class AltDriverBase: + """The driver object will help interacting with all the application objects, their properties and methods. + + When you instantiate an ``AltDriver`` object in your tests, you can use it to “drive” your application like one of + your users would, by interacting with all the application objects, their properties and methods. An ``AltDriver`` + instance will connect to the AltTester® Server. + + Args: + host (:obj:`str`, optional): The host to connect to. + port (:obj:`int`, optional): The port to connect to. + app_name (:obj:`str`, optional): The application name. Defaults to ``__default__``. + enable_logging (:obj:`bool`, optional): If set to ``True`` will turn on logging, by default logging is disabled. + timeout (:obj:`int`, :obj:`float`, optional): The timeout duration for establishing a connection, in seconds. + If set to ``None``, the connection attempt will wait indefinitely. The default value is ``60`` seconds. + + """ + + def __init__( + self, + host="127.0.0.1", + port=13000, + app_name="__default__", + enable_logging=False, + timeout=60, + platform="unknown", + platform_version="unknown", + device_instance_id="unknown", + app_id="unknown", + ): + self.host = host + self.port = port + self.app_name = app_name + self.enable_logging = enable_logging + self.timeout = timeout + self.platform = platform + self.platform_version = platform_version + self.device_instance_id = device_instance_id + self.app_id = app_id + + self._config_logging(self.enable_logging) + + logger.debug( + "Connecting to AltTester(R) on host: '{}', port: '{}' and app name: '{}'.", + self.host, + self.port, + self.app_name, + ) + + self._command_handler = CommandHandler() + self._notification_handler = NotificationHandler() + self._connection = WebsocketConnection( + host=self.host, + port=self.port, + timeout=self.timeout, + path="altws", + params={ + "appName": self.app_name, + "platform": self.platform, + "platformVersion": self.platform_version, + "deviceInstanceId": self.device_instance_id, + "appId": self.app_id, + "driverType": "SDK", + }, + command_handler=self._command_handler, + notification_handler=self._notification_handler, + ) + self._connection.connect() + self._check_server_version() + + def __repr__(self): + return "{}({!r}, {!r}, {!r}, {!r}, {!r})".format( + self.__class__.__name__, + self.host, + self.port, + self.app_name, + self.enable_logging, + self.timeout, + ) + + @staticmethod + def _config_logging(enable_logging): + if enable_logging: + logger.configure( + handlers=[ + dict(sink=sys.stdout, diagnose=False), + dict( + sink="./AltTester.log", + enqueue=False, + serialize=True, + mode="w", + diagnose=False, + ), + ], + levels=[dict(name="DEBUG")], + activation=[("alttester", True)], + ) + else: + logger.disable("alttester") + + @staticmethod + def _split_version(version): + parts = version.split(".") + return (parts[0], parts[1]) if len(parts) > 1 else ("", "") + + def _check_server_version(self): + server_version = commands.GetServerVersion.run(self._connection) + logger.info( + "Connection established with instrumented Unity app. AltTester(R) Version: {}".format( + server_version + ) + ) + + major_server, minor_server = self._split_version(server_version) + + server_major = int(major_server) + server_minor = int(minor_server) + + is_supported = (server_major == 2 and server_minor == 2) or ( + server_major == 1 and server_minor == 0 + ) + + if not is_supported: + message = "Version mismatch. AltDriver version is {}. AltTester(R) version is {}.".format( + VERSION, server_version + ) + logger.warning(message) + + def _get_alt_object(self, data): + if data is None: + return None + + alt_object = AltObject(self, data) + + logger.debug( + "Element {} found at x: {} y: {} mobileY: {}", + alt_object.name, + alt_object.x, + alt_object.y, + alt_object.mobileY, + ) + + return alt_object + + def _get_alt_objects(self, data): + if data is None: + return None + + alt_objects = [] + + for element in data: + alt_object = AltObject(self, element) + alt_objects.append(alt_object) + + logger.debug( + "Element {} found at x: {} y: {} mobileY: {}", + alt_object.name, + alt_object.x, + alt_object.y, + alt_object.mobileY, + ) + + return alt_objects + + def stop(self): + """Close the connection to AltTester.""" + + self._connection.close() + + def get_command_response_timeout(self): + """Gets the current command response timeout for the AltTester® connection. + + Return: + int or float: The current command response time. + + """ + + return self._connection.set_command_timeout() + + def set_command_response_timeout(self, timeout): + """Sets the command response timeout for the AltTester® connection. + + Args: + timeout (:obj:`int` or :obj:`float`): The new command response timeout in seconds. + + """ + + self._connection.set_command_timeout(timeout) + + def get_delay_after_command(self): + """Gets the current delay after a command.""" + + return Command.get_delay_after_command() + + def set_delay_after_command(self, delay): + """Set the delay after a command. + + Args: + delay (:obj:`int` or :obj:`float`): The new delay a after a command. + """ + + Command.set_delay_after_command(delay) + + def set_server_logging(self, logger, log_level): + """Sets the level of logging on AltTester. + + Args: + logger (:obj:`AltLogger`): The type of logger. + log_lever (:obj:`AltLogLevel`): The logging level. + + """ + + commands.SetServerLogging.run(self._connection, logger, log_level) + + def call_static_method( + self, type_name, method_name, assembly, parameters=None, type_of_parameters=None + ): + """Invoke a static method from your application. + + Args: + type_name (:obj:`str`): The name of the script. If the script has a namespace the format should look like + this: ``"namespace.typeName"``. + method_name (:obj:`str`): The name of the public method that we want to call. If the method is inside a + static property/field to be able to call that method, methodName need to be the following format + ``"propertyName.MethodName"``. + assembly (:obj:`str`): The name of the assembly containing the script. + parameters (:obj:`list`, :obj:`tuple`, optional): Defaults to ``None``. + type_of_parameters (:obj:`list`, :obj:`tuple`, optional): Defaults to ``None``. + + Return: + str: The value returned by the method is serialized to a JSON string. + + """ + + return commands.CallMethod.run( + self._connection, + type_name, + method_name, + parameters=parameters, + type_of_parameters=type_of_parameters, + assembly=assembly, + ) + + def get_current_scene(self): + """Returns the name of the current scene. + + Returns: + str: The name of the current scene. + + """ + + return commands.GetCurrentScene.run(self._connection) + + def load_scene(self, scene_name, load_single=True): + """Loads a scene. + + Args: + scene_name (:obj:`str`): The name of the scene to be loaded. + load_single (:obj`bool`, optional): Sets the loading mode. If set to ``False`` the scene will be loaded + additive, together with the current loaded scenes. Defaults to ``True``. + + """ + + commands.LoadScene.run(self._connection, scene_name, load_single) + + def wait_for_current_scene_to_be(self, scene_name, timeout=30, interval=1): + """Waits for the scene to be loaded for a specified amount of time. + + Args: + scene_name (:obj:`str`): The name of the scene to wait for. + timeout (obj:`int`, :obj:`float`, optional): The time measured in seconds to wait for the specified scene. + Defaults to ``30``. + interval (obj:`int`, :obj:`float`, optional): How often to check that the scene was loaded in the given + timeout. Defaults to ``1``. + + Returns: + str: The name of the loaded scene. + + """ + + return commands.WaitForCurrentSceneToBe.run( + self._connection, scene_name, timeout, interval + ) + + def unload_scene(self, scene_name): + """Unloads a scene. + + Args: + scene_name (:obj:`str`): The name of the scene to be unloaded. + + """ + + commands.UnloadScene.run(self._connection, scene_name) + + def get_all_loaded_scenes(self): + """Returns all the scenes that have been loaded. + + Returns: + :obj:`list` of :obj:`str`: A list containing the names of all the loaded scenes. + + """ + + return commands.GetAllLoadedScenes.run(self._connection) + + def get_time_scale(self): + """Returns the value of the time scale. + + Returns: + float: The value of the time scale. + + """ + + return commands.GetTimeScale.run(self._connection) + + def set_time_scale(self, time_scale): + """Sets the value of the time scale. + + Args: + time_scale (:obj:`float`, :obj:`int`): The value of the time scale. + + """ + + commands.SetTimeScale.run(self._connection, time_scale) + + def get_player_pref_key(self, key_name, key_type): + """Returns the value for a given key from PlayerPrefs. + + Args: + key_name (:obj:`str`): The name of the key to be retrived. + key_type (:obj:`PlayerPrefKeyType`): The type of the key. + + """ + + return commands.GetPlayerPrefKey.run(self._connection, key_name, key_type) + + def set_player_pref_key(self, key_name, value, key_type): + """Sets the value for a given key in PlayerPrefs. + + Args: + key_name (:obj:`str`): The name of the key to be set. + value (:obj:`str`): The new value of be set. + key_type (:obj:`PlayerPrefKeyType`): The type of the key. + + """ + + commands.SetPlayerPrefKey.run(self._connection, key_name, value, key_type) + + def delete_player_pref_key(self, key_name): + """Removes a key and its corresponding value from PlayerPrefs. + + Args: + key_name (:obj:`str`): The name of the key to be deleted. + + """ + + commands.DeletePlayerPrefKey.run(self._connection, key_name) + + def delete_player_pref(self): + """Removes all keys and values from PlayerPref.""" + + commands.DeletePlayerPref.run(self._connection) + + def __find_object(self, altby: AltBy, camera_altby: AltBy = AltBy.name(""), enabled=True): + """Finds the first object in the scene that respects the given criteria. + + Args: + altby (:obj:`AltBy`): Sets what criteria to use in order to find the object. + camera_altby (:obj:`AltBy`, optional): Set what criteria to use in order to find the camera. + enabled (:obj:`bool`, optional): If ``True`` will match only objects that are active in hierarchy. If + ``False`` will match all objects. Defaults to ``True``. + + Returns: + AltObject: The object. + + """ + data = commands.FindObject.run( + self._connection, + altby.by, + altby.value, + camera_altby.by, + camera_altby.value, + enabled, + ) + + return self._get_alt_object(data) + + def __find_objects(self, altby: AltBy, camera_altby: AltBy = AltBy.name(""), enabled=True): + """Finds all objects in the scene that respects the given criteria. + + Args: + altby (:obj:`AltBy`): Sets what criteria to use in order to find the objects. + camera_altby (:obj:`AltBy`, optional): Set what criteria to use in order to find the camera. + enabled (:obj:`bool`, optional): If ``True`` will match only objects that are active in hierarchy. If + ``False`` will match all objects. Defaults to ``True``. + + Returns: + list of AltObjects: The list of objects. + + """ + data = commands.FindObjects.run( + self._connection, + altby.by, + altby.value, + camera_altby.by, + camera_altby.value, + enabled, + ) + + return self._get_alt_objects(data) + + def __find_object_which_contains( + self, altby: AltBy, camera_altby: AltBy = AltBy.name(""), enabled=True + ): + """Finds the first object in the scene that respects the given criteria. + + Args: + altby (:obj:`AltBy`): Sets what criteria to use in order to find the object. + camera_altby (:obj:`AltBy`, optional): Set what criteria to use in order to find the camera. + enabled (:obj:`bool`, optional): If ``True`` will match only objects that are active in hierarchy. If + ``False`` will match all objects. Defaults to ``True``. + + Returns: + AltObject: The object. + + """ + data = commands.FindObjectWhichContains.run( + self._connection, + altby.by, + altby.value, + camera_altby.by, + camera_altby.value, + enabled, + ) + + return self._get_alt_object(data) + + def __find_objects_which_contain( + self, altby: AltBy, camera_altby: AltBy = AltBy.name(""), enabled=True + ): + """Finds all objects in the scene that respect the given criteria. + + Args: + altby (:obj:`AltBy`): Sets what criteria to use in order to find the objects. + camera_altby (:obj:`AltBy`, optional): Set what criteria to use in order to find the camera. + enabled (:obj:`bool`, optional): If ``True`` will match only objects that are active in hierarchy. If + ``False`` will match all objects. Defaults to ``True``. + + Returns: + list of AltObjects: The list of objects. + + """ + data = commands.FindObjectsWhichContain.run( + self._connection, + altby.by, + altby.value, + camera_altby.by, + camera_altby.value, + enabled, + ) + + return self._get_alt_objects(data) + + def __wait_for_object( + self, + altby: AltBy, + camera_altby: AltBy = AltBy.name(""), + timeout=20, + interval=0.5, + enabled=True, + ): + """Waits until it finds an object that respects the given criteria or until timeout limit is reached. + + Args: + altby (:obj:`AltBy`): Sets what criteria to use in order to find the object. + camera_altby (:obj:`AltBy`, optional): Set what criteria to use in order to find the camera. + timeout (:obj:`int`, :obj:`float`, optional): The number of seconds that it will wait for object. + interval (:obj:`int`, :obj:`float`, optional): The number of seconds after which it will try to find the + object again. The interval should be smaller than the timeout. + enabled (:obj:`bool`, optional): If ``True`` will match only objects that are active in hierarchy. If + ``False`` will match all objects. Defaults to ``True``. + + Returns: + AltObject: The object. + + """ + + data = commands.WaitForObject.run( + self._connection, + altby.by, + altby.value, + camera_altby.by, + camera_altby.value, + timeout, + interval, + enabled, + ) + + return self._get_alt_object(data) + + def __wait_for_object_which_contains( + self, + altby: AltBy, + camera_altby: AltBy = AltBy.name(""), + timeout=20, + interval=0.5, + enabled=True, + ): + """Waits until it finds an object that respects the given criteria or until timeout limit is reached. + + Args: + altby (:obj:`AltBy`): Sets what criteria to use in order to find the object. + camera_altby (:obj:`AltBy`, optional): Set what criteria to use in order to find the camera. + timeout (:obj:`int`, :obj:`float`, optional): The number of seconds that it will wait for object. + interval (:obj:`int`, :obj:`float`, optional): The number of seconds after which it will try to find the + object again. The interval should be smaller than the timeout. + enabled (:obj:`bool`, optional): If ``True`` will match only objects that are active in hierarchy. If + ``False`` will match all objects. Defaults to ``True``. + + Returns: + AltObject: The object. + + """ + + data = commands.WaitForObjectWhichContains.run( + self._connection, + altby.by, + altby.value, + camera_altby.by, + camera_altby.value, + timeout, + interval, + enabled, + ) + + return self._get_alt_object(data) + + def __wait_for_object_to_not_be_present( + self, + altby: AltBy, + camera_altby: AltBy = AltBy.name(""), + timeout=20, + interval=0.5, + enabled=True, + ): + """Waits until the object in the scene that respects the given criteria is no longer in the scene or until + timeout limit is reached. + + Args: + altby (:obj:`AltBy`): Sets what criteria to use in order to find the object. + camera_altby (:obj:`AltBy`, optional): Set what criteria to use in order to find the camera. + timeout (:obj:`int`, :obj:`float`, optional): The number of seconds that it will wait for object. + interval (:obj:`int`, :obj:`float`, optional): The number of seconds after which it will try to find the + object again. The interval should be smaller than the timeout. + enabled (:obj:`bool`, optional): If ``True`` will match only objects that are active in hierarchy. If + ``False`` will match all objects. Defaults to ``True``. + + """ + + commands.WaitForObjectToNotBePresent.run( + self._connection, + altby.by, + altby.value, + camera_altby.by, + camera_altby.value, + timeout, + interval, + enabled, + ) + + def __get_all_elements(self, camera_altby: AltBy = AltBy.name(""), enabled=True): + """Returns information about every objects loaded in the currently loaded scenes. This also means objects that + are set as DontDestroyOnLoad. + + Args: + camera_altby (:obj:`AltBy`, optional): Set what criteria to use in order to find the camera. + enabled (:obj:`bool`, optional): If ``True`` will match only objects that are active in hierarchy. If + ``False`` will match all objects. Defaults to ``True``. + + Returns: + list of AltObjects: The list of objects. + + """ + + return self.__find_objects(AltBy.path("//*"), camera_altby, enabled=enabled) + + def move_mouse(self, coordinates, duration=0.1, wait=True): + """Simulates mouse movement in your application. + + Args: + coordinates (:obj:`dict`): The screen coordinates + duration (:obj:`int`, optional): The time measured in seconds to move the mouse from current position to + the set location. Defaults to ``0.1`` + wait (:obj:`bool`, optional): If set wait for command to finish. Defaults to ``True``. + + """ + + commands.MoveMouse.run(self._connection, coordinates, duration, wait) + + def scroll(self, speed_vertical=1, duration=0.1, wait=True, speed_horizontal=1): + """Simulate scroll mouse action in your application. + + Args: + speed_vertical (:obj:`int`, :obj:`float`): Set how fast to scroll. Positive values will scroll up and + negative values will scroll down. Defaults to ``1`` + duration (:obj:`int`, :obj:`float`, optional): The duration of the scroll in seconds. Defaults to ``0.1``. + wait (:obj:`bool`, optional): If set wait for command to finish. Defaults to ``True``. + speed_horizontal (:obj:`int`, :obj:`float`): Set how fast to scroll right or left. Defaults to ``1`` + + """ + + commands.Scroll.run( + self._connection, speed_vertical, duration, wait, speed_horizontal + ) + + def click(self, coordinates, count=1, interval=0.1, wait=True): + """Click at screen coordinates. + + Args: + coordinates (:obj:`dict`): The screen coordinates. + count (:obj:`int`, optional): Number of taps. Defaults to ``1``. + interval (:obj:`int`, :obj:`float`, optional): The interval between taps in seconds. Defaults to ``0.1``. + wait (:obj:`bool`, optional): If set to ``True`` Wait for command to finish. + + """ + + commands.ClickCoordinates.run( + self._connection, coordinates, count, interval, wait + ) + + def key_down(self, key_code, power=1): + """Simulates that a specific key was pressed without taking into consideration the duration of the press. + + Args: + key_code (:obj:`AltKeyCode`): The key code of the key simulated to be pressed. + power (:obj:`float`, optional): A value between [-1,1] used for joysticks to indicate how hard the button + was pressed. Defaults to ``1``. + + """ + + self.keys_down([key_code], power=power) + + def keys_down(self, key_codes, power=1): + """Simulates that multiple keys were pressed without taking into consideration the duration of the press. + + Args: + key_codes (:obj:`list` of :obj:`AltKeyCode`): The key codes of the keys simulated to be pressed. + power (:obj:`float`): A value between [-1,1] used for joysticks to indicate how hard the button was + pressed. Defaults to ``1``. + + """ + + commands.KeysDown.run(self._connection, key_codes, power) + + def key_up(self, key_code): + """Simulates that a specific key was released. + + Args: + key_code (:obj:`AltKeyCode`): The key code of the key simulated to be released. + + """ + + self.keys_up([key_code]) + + def keys_up(self, key_codes): + """Simulates that multiple keys were released. + + Args: + key_codes (:obj:`list` of :obj:`AltKeyCode`): The key codes of the keys simulated to be released. + + """ + + commands.KeysUp.run(self._connection, key_codes) + + def press_key(self, key_code, power=1, duration=0.1, wait=True): + """Simulates key press action in your application. + + Args: + key_code (:obj:`AltKeyCode`): The key code of the key simulated to be pressed. + power (:obj:`int`, :obj:`float`, optional): A value between [-1,1] used for joysticks to indicate how hard + the button was pressed. Defaults to ``1``. + duration (:obj:`float`, optional): The time measured in seconds from the key press to the key release. + wait (:obj:`bool`, optional): If set wait for command to finish. Defaults to ``True``. + + """ + + self.press_keys([key_code], power=power, duration=duration, wait=wait) + + def press_keys(self, key_codes, power=1, duration=0.1, wait=True): + """Simulates multiple keypress action in your application. + + Args: + key_codes (:obj:`list` of :obj:`AltKeyCode`): The key codes of the keys simulated to be pressed. + power (:obj:`float`): A value between [-1,1] used for joysticks to indicate how hard the buttons were + pressed. Defaults to ``1``. + duration (:obj:`float`): The time measured in seconds from the key press to the key release. + wait (:obj:`bool`): If set wait for command to finish. Defaults to ``True``. + + """ + + commands.PressKeys.run(self._connection, key_codes, power, duration, wait) + + def begin_touch(self, coordinates): + """Simulates starting of a touch on the screen. + + Args: + coordinates (:obj:`dict`): The screen coordinates. + + Returns: + str: A ``finger_id`` to use with ``move_touch`` and ``end_touch``. + + """ + + return commands.BeginTouch.run(self._connection, coordinates) + + def move_touch(self, finger_id, coordinates): + """Simulates a touch movement on the screen. Move the touch created with ``begin_touch`` from the previous + position to the position given as parameters. + + Args: + finger_id (:obj:`str`): The value returned by ``begin_touch``. + coordinates (:obj:`dict`): Screen coordinates where the touch will be moved. + + """ + + commands.MoveTouch.run(self._connection, finger_id, coordinates) + + def end_touch(self, finger_id): + """Simulates ending of a touch on the screen. This command will destroy the touch making it no longer usable to + other movements. + + Args: + finger_id (:obj:`str`): The value returned by ``begin_touch``. + + """ + + commands.EndTouch.run(self._connection, finger_id) + + def swipe(self, start, end, duration=0.1, wait=True): + """Simulates a swipe action between two points. + + Args: + start (:obj:`dict`): Coordinates of the screen where the swipe begins. + end (:obj:`dict`): Coordinates of the screen where the swipe ends. + duration (:obj:`int`, :obj:`float`, optional): The time measured in seconds to move the mouse from start to + end location. Defaults to ``0.1``. + wait (:obj:`bool`, optional): If set wait for command to finish. Defaults to ``True``. + + """ + + commands.Swipe.run(self._connection, start, end, duration, wait) + + def multipoint_swipe(self, positions, duration=0.1, wait=True): + """Simulates a multipoint swipe action. + + Args: + positions (:obj:`List[dict]`): A list of positions on the screen where the swipe be made. + duration (:obj:`float`): The time measured in seconds to swipe from first position to the last position. + Defaults to ``0.1``. + wait (:obj:`bool`): If set wait for command to finish. Defaults to ``True``. + + """ + + commands.MultipointSwipe.run(self._connection, positions, duration, wait) + + def tap(self, coordinates, count=1, interval=0.1, wait=True): + """Tap at screen coordinates. + + Args: + coordinates (:obj:`dict`): The screen coordinates. + count (:obj:`int`, optional): Number of taps. Defaults to ``1``. + interval (:obj:`int`, :obj:`float`, optional): The interval between taps in seconds. Defaults to ``0.1``. + wait (:obj:`bool`, optional): If set wait for command to finish. Defaults to ``True``. + + """ + + commands.TapCoordinates.run( + self._connection, coordinates, count, interval, wait + ) + + def tilt(self, acceleration, duration=0.1, wait=True): + """Simulates device rotation action in your application. + + Args: + acceleration (:obj:`dict`): The linear acceleration of a device. + duration (:obj:`int`, :obj:`float`, optional): How long the rotation will take in seconds. + Defaults to ``0.1``. + wait (:obj:`bool`, optional): If set wait for command to finish. Defaults to ``True``. + + """ + + commands.Tilt.run(self._connection, acceleration, duration, wait) + + def get_application_screensize(self): + screen_width = self.call_static_method( + "UnityEngine.Screen", "get_width", "UnityEngine.CoreModule" + ) + screen_height = self.call_static_method( + "UnityEngine.Screen", "get_height", "UnityEngine.CoreModule" + ) + return (screen_width, screen_height) + + def get_png_screenshot(self, path): + """Creates a screenshot of the current scene in png format at the given path. + + Args: + path (:obj:`str`): The path where the image will be created. + + """ + + commands.GetPNGScreenshot.run(self._connection, path) + + def hold_button(self, coordinates, duration=0.1, wait=True): + """Simulates holding left click button down for a specified amount of time at given coordinates. + + Args: + coordinates (:obj:`dict`): The coordinates where the button is held down + duration ((:obj:`int`, :obj:`float`, optional): The time measured in seconds to keep the button down. + Defaults to ``0.1``. + wait (:obj:`bool`, optional): If set wait for command to finish. Defaults to ``True``. + + """ + + return commands.Swipe.run( + self._connection, coordinates, coordinates, duration, wait + ) + + def get_static_property(self, component_name, property_name, assembly, max_depth=2): + """Returns the value of the static field or property given as parameter. + + Args: + component_name (:obj:`str`): The name of the component containing the field or property + to be retrieved. + property_name (:obj:`str`): The name of the field or property to be retrieved. + assembly (:obj:`str`): The name of the assembly containing the component mentioned above. + max_depth (:obj:`int`, optional): The value determining how deep to go in the hierarchy of objects + to find the field or property. + + """ + + return commands.GetStaticProperty.run( + self._connection, component_name, property_name, assembly, max_depth + ) + + def set_static_property( + self, component_name, property_name, assembly, updated_value + ): + """Set the value of the static field or property given as parameter. + + Args: + component_name (:obj:`str`): The name of the component containing the field or property + to be retrieved. + property_name (:obj:`str`): The name of the field or property to be retrieved. + assembly (:obj:`str`): The name of the assembly containing the component mentioned above. + updated_value (:obj:`str`): The value of the field or property to be updated. + """ + + return commands.SetStaticProperty.run( + self._connection, component_name, property_name, assembly, updated_value + ) + + def find_object_at_coordinates(self, coordinates): + """Retrieves the Unity object at given coordinates + + Uses EventSystem.RaycastAll to find object. If no object is found then it uses UnityEngine.Physics.Raycast + and UnityEngine.Physics2D.Raycast and returns the one closer to the camera. + + Args: + coordinates (:obj:`dict`): The screen coordinates. + + Returns: + AltObject: The UI object hit by event system Raycast, ``None`` otherwise. + + """ + + data = commands.FindObjectAtCoordinates.run(self._connection, coordinates) + return self._get_alt_object(data) + + def add_notification_listener( + self, notification_type, notification_callback, overwrite=True + ): + """Activates a notification that the tester will send. + + Args: + notification_type (:obj:`int`): Flag that indicates which notification to be turned on. + notification_callback (:obj:`method`): callback used when a notification is received. + overwrite (:obj:'bool', optional): Flag to set if the new callback will overwrite the other + callbacks or just append. + + """ + + commands.AddNotificationListener.run( + self._connection, notification_type, notification_callback, overwrite + ) + + def remove_notification_listener(self, notification_type): + """Clear list of callback for the notification type and turn off the notification in tester. + + Args: + notification_type (:obj:`int`): Flag that indicates which notification to be turned off. + + """ + + commands.RemoveNotificationListener.run(self._connection, notification_type) + + def reset_input(self): + """Clear all active input actions simulated by AltTester.""" + + commands.ResetInput.run(self._connection) diff --git a/Bindings~/python/alttester/altdriver_unity.py b/Bindings~/python/alttester/altdriver_unity.py new file mode 100644 index 000000000..7392361e7 --- /dev/null +++ b/Bindings~/python/alttester/altdriver_unity.py @@ -0,0 +1,225 @@ +""" + Copyright(C) 2024 Altom Consulting + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +""" + +import warnings +from alttester.altobject import AltObject +from alttester.altdriver import AltDriver +from alttester.altby import AltBy +from alttester.altdriver_base import AltDriverBase + +warnings.filterwarnings("default", category=DeprecationWarning, module=__name__) + + +class AltDriverUnity(AltDriverBase): + """The driver object will help interacting with all the application objects, their properties and methods. + + When you instantiate an ``AltDriverUnity`` object in your tests, you can use it to “drive” your application like + one of your users would, by interacting with all the application objects, their properties and methods. + An ``AltDriverUnity`` instance will connect to the AltTester® Server. + + Args: + host (:obj:`str`, optional): The host to connect to. + port (:obj:`int`, optional): The port to connect to. + app_name (:obj:`str`, optional): The application name. Defaults to ``__default__``. + enable_logging (:obj:`bool`, optional): If set to ``True`` will turn on logging, by default logging is disabled. + timeout (:obj:`int`, :obj:`float`, optional): The timeout duration for establishing a connection, in seconds. + If set to ``None``, the connection attempt will wait indefinitely. The default value is ``60`` seconds. + + """ + + def __init__( + self, + host="127.0.0.1", + port=13000, + app_name="__default__", + enable_logging=False, + timeout=60, + platform="unknown", + platform_version="unknown", + device_instance_id="unknown", + app_id="unknown", + ): + super().__init__( + host, + port, + app_name, + enable_logging, + timeout, + platform, + platform_version, + device_instance_id, + app_id, + ) + + def find_object( + self, altby: AltBy, camera_altby: AltBy = AltBy.name(""), enabled: bool = True + ) -> AltObject: + # """Finds an object in the scene. + + # Args: + # altby (:obj:`AltBy`): The search criteria. + # camera_altby (:obj:`AltBy`, optional): The camera search criteria. + # enabled (:obj:`bool`, optional): If set to ``True``, the search will only return enabled objects. + + # Returns: + # :obj:`AltObject`: The object found. + + # Raises: + # :obj:`Exception`: If the object is not found. + + # """ + return self._AltDriverBase__find_object(altby, camera_altby, enabled) + + def find_objects( + self, altby: AltBy, camera_altby: AltBy = AltBy.name(""), enabled: bool = True + ) -> list: + """Finds all objects in the scene. + + Args: + altby (:obj:`AltBy`): The search criteria. + camera_altby (:obj:`AltBy`, optional): The camera search criteria. + enabled (:obj:`bool`, optional): If set to ``True``, the search will only return enabled objects. + + Returns: + :obj:`list`: The list of objects found. + + Raises: + :obj:`Exception`: If the object is not found. + + """ + return self._AltDriverBase__find_objects(altby, camera_altby, enabled) + + def find_object_which_contains( + self, altby: AltBy, camera_altby: AltBy = AltBy.name(""), enabled: bool = True + ) -> AltObject: + """Finds an object in the scene which contains the search criteria. + + Args: + altby (:obj:`AltBy`): The search criteria. + camera_altby (:obj:`AltBy`, optional): The camera search criteria. + enabled (:obj:`bool`, optional): If set to ``True``, the search will only return enabled objects. + + Returns: + :obj:`AltObject`: The object found. + + Raises: + :obj:`Exception`: If the object is not found. + + """ + return self._AltDriverBase__find_object_which_contains(altby, camera_altby, enabled) + + def find_objects_which_contain( + self, altby: AltBy, camera_altby: AltBy = AltBy.name(""), enabled: bool = True + ) -> list: + """Finds all objects in the scene which contain the search criteria. + + Args: + altby (:obj:`AltBy`): The search criteria. + camera_altby (:obj:`AltBy`, optional): The camera search criteria. + enabled (:obj:`bool`, optional): If set to ``True``, the search will only return enabled objects. + + Returns: + :obj:`list`: The list of objects found. + + Raises: + :obj:`Exception`: If the object is not found. + + """ + return self._AltDriverBase__find_objects_which_contain(altby, camera_altby, enabled) + + def wait_for_object( + self, + altby: AltBy, + camera_altby: AltBy = AltBy.name(""), + timeout: int = 20, + interval: int = 0.5, + enabled: bool = True + ) -> AltObject: + """Waits for an object to be found in the scene. + + Args: + altby (:obj:`AltBy`): The search criteria. + camera_altby (:obj:`AltBy`, optional): The camera search criteria. + enabled (:obj:`bool`, optional): If set to ``True``, the search will only return enabled objects. + timeout (:obj:`int`, optional): The timeout duration for the search, in seconds. + + Returns: + :obj:`AltObject`: The object found. + + Raises: + :obj:`Exception`: If the object is not found. + + """ + return self._AltDriverBase__wait_for_object(altby, camera_altby, timeout, interval, enabled) + + def wait_for_object_which_contains( + self, + altby: AltBy, + camera_altby: AltBy = AltBy.name(""), + timeout: int = 20, + interval: int = 0.5, + enabled: bool = True + ) -> AltObject: + """Waits for an object to be found in the scene which contains the search criteria. + + Args: + altby (:obj:`AltBy`): The search criteria. + camera_altby (:obj:`AltBy`, optional): The camera search criteria. + enabled (:obj:`bool`, optional): If set to ``True``, the search will only return enabled objects. + timeout (:obj:`int`, optional): The timeout duration for the search, in seconds. + + Returns: + :obj:`AltObject`: The object found. + + Raises: + :obj:`Exception`: If the object is not found. + + """ + return self._AltDriverBase__wait_for_object_which_contains(altby, camera_altby, enabled, timeout, interval) + + def wait_for_object_to_not_be_present( + self, + altby: AltBy, + camera_altby: AltBy = AltBy.name(""), + timeout: int = 20, + interval: int = 0.5, + enabled: bool = True, + ) -> None: + """Waits for an object not to be present in the scene. + + Args: + altby (:obj:`AltBy`): The search criteria. + camera_altby (:obj:`AltBy`, optional): The camera search criteria. + timeout (:obj:`int`, optional): The timeout duration for the search, in seconds. + + Raises: + :obj:`Exception`: If the object is found. + + """ + return self._AltDriverBase__wait_for_object_to_not_be_present(altby, camera_altby, timeout, interval, enabled) + + def get_all_elements(self, camera_altby: AltBy = AltBy.name(""), enabled: bool = True) -> list: + """Returns all the elements in the scene. + + Args: + camera_altby (:obj:`AltBy`, optional): The camera search criteria. + + Returns: + :obj:`list`: The list of objects found. + + """ + return self._AltDriverBase__get_all_elements(camera_altby, enabled) \ No newline at end of file diff --git a/Bindings~/python/alttester/altdriver_unreal.py b/Bindings~/python/alttester/altdriver_unreal.py new file mode 100644 index 000000000..62c75c9b0 --- /dev/null +++ b/Bindings~/python/alttester/altdriver_unreal.py @@ -0,0 +1,334 @@ +""" + Copyright(C) 2024 Altom Consulting + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +""" + +from alttester.altobject_unreal import AltObjectUnreal +from alttester.altdriver_base import AltDriverBase +from alttester.altby import AltBy + + +class AltDriverUnreal(AltDriverBase): + """The driver object will help interacting with all the application objects, their properties and methods. + + When you instantiate an ``AltDriverUnreal`` object in your tests, you can use it to “drive” your application like + one of your users would, by interacting with all the application objects, their properties and methods. + An ``AltDriverUnreal`` instance will connect to the AltTester® Server. + + Args: + host (:obj:`str`, optional): The host to connect to. + port (:obj:`int`, optional): The port to connect to. + app_name (:obj:`str`, optional): The application name. Defaults to ``__default__``. + enable_logging (:obj:`bool`, optional): If set to ``True`` will turn on logging, by default logging is disabled. + timeout (:obj:`int`, :obj:`float`, optional): The timeout duration for establishing a connection, in seconds. + If set to ``None``, the connection attempt will wait indefinitely. The default value is ``60`` seconds. + + """ + + def __init__( + self, + host="127.0.0.1", + port=13000, + app_name="__default__", + enable_logging=False, + timeout=60, + platform="unknown", + platform_version="unknown", + device_instance_id="unknown", + app_id="unknown", + ): + super().__init__( + host, + port, + app_name, + enable_logging, + timeout, + platform, + platform_version, + device_instance_id, + app_id, + ) + + def find_object(self, altby: AltBy, enabled: bool = True) -> AltObjectUnreal: + """Finds an object in the scene. + + Args: + altby (:obj:`AltBy`): The search criteria. + enabled (:obj:`bool`, optional): If set to ``True``, the search will only return enabled objects. + + Returns: + :obj:`AltObjectUnreal`: The object found. + + Raises: + :obj:`Exception`: If the object is not found. + + """ + alt_object = super().find_object(altby, enabled=enabled) + return AltObjectUnreal(self, alt_object.to_json()) + + def find_objects(self, altby: AltBy, enabled: bool = True) -> list: + """Finds all objects in the scene. + + Args: + altby (:obj:`AltBy`): The search criteria. + enabled (:obj:`bool`, optional): If set to ``True``, the search will only return enabled objects. + + Returns: + :obj:`list`: The list of objects found. + + Raises: + :obj:`Exception`: If the object is not found. + + """ + alt_objects = super().find_objects(altby, enabled=enabled) + return [AltObjectUnreal(self, obj.to_json()) for obj in alt_objects] + + def wait_for_object( + self, + altby: AltBy, + timeout: int = 20, + interval: int = 0.5, + enabled: bool = True + ) -> AltObjectUnreal: + """Waits for an object to be found in the scene. + + Args: + altby (:obj:`AltBy`): The search criteria. + enabled (:obj:`bool`, optional): If set to ``True``, the search will only return enabled objects. + timeout (:obj:`int`, optional): The timeout duration for the search, in seconds. + + Returns: + :obj:`AltObjectUnreal`: The object found. + + Raises: + :obj:`Exception`: If the object is not found. + + """ + alt_object = super().wait_for_object( + altby, timeout=timeout, interval=interval, enabled=enabled + ) + return AltObjectUnreal(self, alt_object.to_json()) + + def get_all_elements(self, enabled: bool = True) -> list: + """Returns all the elements in the scene. + + Args: + enabled (:obj:`bool`, optional): If set to ``True``, the search will only return enabled objects. + + Returns: + :obj:`list`: The list of objects found. + + """ + alt_objects = super().get_all_elements(enabled=enabled) + return [AltObjectUnreal(self, obj.to_json()) for obj in alt_objects] + + def find_object_which_contains(self, altby: AltBy, enabled: bool = True) -> AltObjectUnreal: + """Finds an object in the scene which contains the search criteria. + + Args: + altby (:obj:`AltBy`): The search criteria. + enabled (:obj:`bool`, optional): If set to ``True``, the search will only return enabled objects. + + Returns: + :obj:`AltObjectUnreal`: The object found. + + Raises: + :obj:`Exception`: If the object is not found. + + """ + alt_object = super().find_object_which_contains(altby, enabled=enabled) + return AltObjectUnreal(self, alt_object.to_json()) + + def find_objects_which_contain(self, altby: AltBy, enabled: bool = True) -> list: + """Finds all objects in the scene which contain the search criteria. + + Args: + altby (:obj:`AltBy`): The search criteria. + enabled (:obj:`bool`, optional): If set to ``True``, the search will only return enabled objects. + + Returns: + :obj:`list`: The list of objects found. + + Raises: + :obj:`Exception`: If the object is not found. + + """ + alt_objects = super().find_objects_which_contain(altby, enabled=enabled) + return [AltObjectUnreal(self, obj.to_json()) for obj in alt_objects] + + def wait_for_object_which_contains( + self, + altby: AltBy, + timeout: int = 20, + interval: int = 0.5, + enabled: bool = True + ) -> AltObjectUnreal: + """Waits for an object to be found in the scene which contains the search criteria. + + Args: + altby (:obj:`AltBy`): The search criteria. + enabled (:obj:`bool`, optional): If set to ``True``, the search will only return enabled objects. + timeout (:obj:`int`, optional): The timeout duration for the search, in seconds. + + Returns: + :obj:`AltObjectUnreal`: The object found. + + Raises: + :obj:`Exception`: If the object is not found. + + """ + alt_object = super().wait_for_object_which_contains( + altby, timeout=timeout, interval=interval, enabled=enabled + ) + return AltObjectUnreal(self, alt_object.to_json()) + + def wait_for_object_to_not_be_present( + self, + altby: AltBy, + timeout: int = 20, + interval: int = 0.5, + enabled: bool = True, + ) -> None: + """Waits for an object not to be present in the scene. + + Args: + altby (:obj:`AltBy`): The search criteria. + timeout (:obj:`int`, optional): The timeout duration for the search, in seconds. + + Raises: + :obj:`Exception`: If the object is found. + + """ + return super().wait_for_object_to_not_be_present( + altby, timeout=timeout, interval=interval, enabled=enabled + ) + + def key_down(self, key_code, power=1): + """Simulates that a specific key was pressed without taking into consideration the duration of the press. + + Args: + key_code (:obj:`AltKeyCode`): The key code of the key simulated to be pressed. + power (:obj:`float`, optional): A value between [-1,1] used for joysticks to indicate how hard the button + was pressed. Defaults to ``1``. + + """ + super().key_down(key_code, power) + + def keys_down(self, key_codes, power=1): + """Simulates that multiple keys were pressed without taking into consideration the duration of the press. + + Args: + key_codes (:obj:`list` of :obj:`AltKeyCode`): The key codes of the keys simulated to be pressed. + power (:obj:`float`): A value between [-1,1] used for joysticks to indicate how hard the buttons were + pressed. Defaults to ``1``. + + """ + super().keys_down(key_codes, power) + + def press_key(self, key_code, power=1, duration=0.1, wait=True): + """Simulates key press action in your application. + + Args: + key_code (:obj:`AltKeyCode`): The key code of the key simulated to be pressed. + power (:obj:`int`, :obj:`float`, optional): A value between [-1,1] used for joysticks to indicate how hard + the button was pressed. Defaults to ``1``. + duration (:obj:`float`, optional): The time measured in seconds from the key press to the key release. + wait (:obj:`bool`, optional): If set wait for command to finish. Defaults to ``True``. + + """ + super().press_key(key_code, power, duration, wait) + + def press_keys(self, key_codes, power=1, duration=0.1, wait=True): + """Simulates multiple keypress action in your application. + + Args: + key_codes (:obj:`list` of :obj:`AltKeyCode`): The key codes of the keys simulated to be pressed. + power (:obj:`float`): A value between [-1,1] used for joysticks to indicate how hard the buttons were + pressed. Defaults to ``1``. + duration (:obj:`float`): The time measured in seconds from the key press to the key release. + wait (:obj:`bool`): If set wait for command to finish. Defaults to ``True``. + + """ + super().press_keys(key_codes, power, duration, wait) + + def load_level(self, level_name): + """Loads a level. + + Args: + level_name (:obj:`str`): The name of the level to be loaded. + + """ + super().load_scene(level_name) + + def get_current_level(self): + """Returns the name of the current level. + + Returns: + str: The name of the current level. + + """ + return super().get_current_scene() + + def wait_for_current_level_to_be(self, level_name, timeout=10, interval=1): + """Waits for the level to be loaded for a specified amount of time. + + Args: + level_name (:obj:`str`): The name of the level to wait for. + timeout (obj:`int`, :obj:`float`, optional): The time measured in seconds to wait for the specified level. + Defaults to ``10``. + interval (obj:`int`, :obj:`float`, optional): How often to check that the level was loaded in the given + timeout. Defaults to ``1``. + + Returns: + str: The name of the loaded level. + + """ + super().wait_for_current_scene_to_be(level_name, timeout, interval) + + def get_global_time_dilation(self): + """Returns the value of the global time dilation. + + Returns: + float: The value of the global time dilation. + + """ + return super().get_time_scale() + + def set_global_time_dilation(self, time_dilation): + """Sets the value of the global time dilation. + + Args: + time_dilation (:obj:`float`, :obj:`int`): The value of the global time dilation. + + """ + super().set_time_scale(time_dilation) + + def call_static_method(self, type_name, method_name, parameters=None, type_of_parameters=None): + """Invoke a static method from your application. + + Args: + type_name (:obj:`str`): The name of the script. If the script has a namespace the format should look like + this: ``"namespace.typeName"``. + method_name (:obj:`str`): The name of the public method that we want to call. If the method is inside a + static property/field to be able to call that method, methodName need to be the following format + ``"propertyName.MethodName"``. + parameters (:obj:`list`, :obj:`tuple`, optional): Defaults to ``None``. + type_of_parameters (:obj:`list`, :obj:`tuple`, optional): Defaults to ``None``. + + Return: + str: The value returned by the method is serialized to a JSON string. + + """ + return super().call_static_method(type_name, method_name, "", parameters, type_of_parameters) diff --git a/Bindings~/python/alttester/altobject.py b/Bindings~/python/alttester/altobject.py index 0cc3efd21..1cf2cfcc2 100644 --- a/Bindings~/python/alttester/altobject.py +++ b/Bindings~/python/alttester/altobject.py @@ -19,86 +19,23 @@ import alttester.commands as commands import alttester.exceptions as exceptions +from alttester.altby import AltBy from alttester.by import By +from alttester.altobject_base import AltObjectBase -class AltObject: - """The AltObject class represents an object present in the application and it allows you to interact with it. - - It is the return type of the methods in the “find_*” category from the AltDriver class. - """ +class AltObject(AltObjectBase): def __init__(self, altdriver, data): - self._altdriver = altdriver - self._data = data + super().__init__(altdriver, data) def __repr__(self): - return "{}(altdriver, {!r})".format(self.__class__.__name__, self.to_json()) + return f"AltObject({repr(self._altdriver)}, {repr(self.to_json())})" def __str__(self): return json.dumps(self.to_json()) - @property - def _connection(self): - return self._altdriver._connection - - @property - def name(self): - return self._data.get("name", "") - - @property - def id(self): - return self._data.get("id", 0) - - @property - def x(self): - return self._data.get("x", 0) - - @property - def y(self): - return self._data.get("y", 0) - - @property - def z(self): - return self._data.get("z", 0) - - @property - def mobileY(self): - return self._data.get("mobileY", 0) - - @property - def type(self): - return self._data.get("type", "") - - @property - def enabled(self): - return self._data.get("enabled", True) - - @property - def worldX(self): - return self._data.get("worldX", 0.0) - - @property - def worldY(self): - return self._data.get("worldY", 0.0) - - @property - def worldZ(self): - return self._data.get("worldZ", 0.0) - - @property - def idCamera(self): - return self._data.get("idCamera", 0) - - @property - def transformParentId(self): - return self._data.get("transformParentId", 0) - - @property - def transformId(self): - return self._data.get("transformId", 0) - - def to_json(self): + def to_json(self) -> dict: return { "name": self.name, "id": self.id, @@ -113,275 +50,18 @@ def to_json(self): "worldZ": self.worldZ, "transformParentId": self.transformParentId, "transformId": self.transformId, - "idCamera": self.idCamera + "idCamera": self.idCamera, } - def update_object(self): - altObject = commands.FindObject.run( - self._connection, - By.ID, self.id, By.NAME, "", enabled=True - ) - return AltObject(self._altdriver, altObject) - - def get_screen_position(self): - """Returns the screen position. - - Returns: - tuple: A tuple containing ``x`` and ``y``. - - """ - - return self.x, self.y - - def get_world_position(self): - """Returns the world position. - - Returns: - tuple: A tuple containing ``worldX``, ``worldY`` and ``worldZ``. - - """ - - return self.worldX, self.worldY, self.worldZ - - def get_parent(self): - """Returns the parent object. - - Returns: - AltObject: The parent object. - - """ - - data = commands.FindObject.run( - self._connection, - By.PATH, "//*[@id={}]/..".format(self.id), By.NAME, "", enabled=True - ) - - return AltObject(self._altdriver, data) - - def find_object_from_object(self, by, value, camera_by=By.NAME, camera_value="", enabled=True): - """Returns the child of the object that meets the specified conditions.""" - - data = commands.FindObjectFromObject.run(self._connection, - by, value, camera_by, camera_value, enabled, self) - - if data is None: - return None - - alt_object = AltObject(self, data) - - return alt_object - - def get_all_components(self): - """Returns all components.""" - - return commands.GetAllComponents.run(self._connection, self) - - def wait_for_component_property(self, component_name, property_name, - property_value, assembly, timeout=20, interval=0.5, - get_property_as_string=False, max_depth=2): - """Wait until a property has a specific value and returns the value of the given component property. - - Args: - component_name (:obj:`str`): The name of the component. If the component has a namespace the format should - look like this: ``"namespace.componentName"``. - property_name (:obj:`str`): The name of the property of which value you want. If the property is an array - you can specify which element of the array to return by doing ``property[index]``, or if you want a - property inside of another property you can get by doing ``property.subProperty``. - property_value(:obj:`str`): The value of the component expected - assembly (:obj:`str`): The name of the assembly containing the component. - timeout (:obj:`int`, optional): The number of seconds that it will wait for property. - interval (:obj:`float`, optional): The number of seconds after which it will try to find the object again. - The interval should be smaller than timeout. - get_property_as_string (:obj:`bool`, optional): A boolean value that makes the property_value - to be compared as a string with the property from the instrumented app. - max_depth (:obj:`int`, optional): An integer value that defines the maximum level from which to retrieve - properties. - - Returns: - str: The property value is serialized to a JSON string. - - """ - return commands.WaitForComponentProperty.run( - component_name, property_name, property_value, - assembly, self, timeout, interval, get_property_as_string, max_depth - ) - - def get_component_property(self, component_name, property_name, assembly, max_depth=2): - """Returns the value of the given component property. - - Args: - component_name (:obj:`str`): The name of the component. If the component has a namespace the format should - look like this: ``"namespace.componentName"``. - property_name (:obj:`str`): The name of the property of which value you want. If the property is an array - you can specify which element of the array to return by doing ``property[index]``, or if you want a - property inside of another property you can get by doing ``property.subProperty``. - assembly (:obj:`str`): The name of the assembly containing the component. - maxDepth (:obj:`int`, optional): Set how deep to serialize the property. Defaults to ``2``. - - Returns: - str: The property value is serialized to a JSON string. - - """ - - return commands.GetComponentProperty.run( - self._connection, - component_name, property_name, assembly, max_depth, self - ) - - def set_component_property(self, component_name, property_name, assembly, value): - """Sets a value for a given component property. - - Args: - component_name (:obj:`str`): The name of the component. If the component has a namespace the format should - look like this: ``"namespace.componentName"``. - property_name (:obj:`str`): The name of the property of which value you want to set. - assembly (:obj:`str`): The name of the assembly containing the component. - value (:obj:`str`): The value to be set for the chosen component's property. - - Returns: - str: The property value is serialized to a JSON string. - - """ - - return commands.SetComponentProperty.run( - self._connection, - component_name, property_name, value, assembly, self - ) - - def call_component_method(self, component_name, method_name, assembly, parameters=None, type_of_parameters=None): - """Invokes a method from an existing component of the object. - - Args: - component_name (:obj:`str`): The name of the script. If the script has a namespace the format should look - like this: ``"namespace.typeName"``. - method_name (:obj:`str`): The name of the public method that we want to call. If the method is inside a - static property/field to be able to call that method, methodName need to be the following format - ``"propertyName.MethodName"``. - assembly (:obj:`str`): The name of the assembly containing the script. - parameters (:obj:`list`, :obj:`tuple`, optional): Defaults to ``None``. - type_of_parameters (:obj:`list`, :obj:`tuple`, optional): Defaults to ``None``. - - Return: - str: The value returned by the method is serialized to a JSON string. - - """ - - return commands.CallMethod.run( - self._connection, - component_name, - method_name, - alt_object=self, - parameters=parameters, - type_of_parameters=type_of_parameters, - assembly=assembly - ) - - def get_text(self): - """Returns text value from a Button, Text, InputField. This also works with TextMeshPro elements. - - Returns: - str: The text value of the AltObject. - - """ - - return commands.GetText.run(self._connection, self) - - def set_text(self, text, submit=False): - """Sets text value for a Button, Text or InputField. This also works with TextMeshPro elements. - - Args: - text (obj:`str`): The text to be set. - submit (obj:`bool`): If set will trigger a submit event. - - Returns: - AltObject: The current AltObject. - - """ - - data = commands.SetText.run(self._connection, text, self, submit) - return AltObject(self._altdriver, data) - - def pointer_up(self): - """Simulates pointer up action on the object. - - Returns: - AltObject: The current AltObject. - - """ - - data = commands.PointerUp.run(self._connection, self) - return AltObject(self._altdriver, data) - - def pointer_down(self): - """Simulates pointer down action on the object. - - Returns: - AltObject: The current AltObject. - - """ - - data = commands.PointerDown.run(self._connection, self) - return AltObject(self._altdriver, data) - - def pointer_enter(self): - """Simulates pointer enter action on the object. - - Returns: - AltObject: The current AltObject. - - """ - - data = commands.PointerEnter.run(self._connection, self) - return AltObject(self._altdriver, data) - - def pointer_exit(self): - """Simulates pointer exit action on the object. - - Returns: - AltObject: The current AltObject. - - """ - - data = commands.PointerExit.run(self._connection, self) - return AltObject(self._altdriver, data) - - def tap(self, count=1, interval=0.1, wait=True): - """Taps the current object. - - Args: - count (:obj:`int`, optional): Number of taps. Defaults to ``1``. - interval (:obj:`int`, :obj:`float`, optional): Interval between taps in seconds. Defaults to ``0.1``. - wait (:obj:`int`, optional): Wait for command to finish. Defaults to ``True``. - - Returns: - AltObject: The tapped object. - - """ - - data = commands.TapElement.run( - self._connection, - self, count, interval, wait - ) - return AltObject(self._altdriver, data) - - def click(self, count=1, interval=0.1, wait=True): - """Clicks the current object. - - Args: - count (:obj:`int`, optional): Number of clicks. Defaults to ``1``. - interval (:obj:`int`, :obj:`float`, optional): Interval between clicks in seconds. Defaults to ``0.1``. - wait (:obj:`int`, optional): Wait for command to finish. Defaults to ``True``. - - Returns: - AltObject: The clicked object. - - """ + def update_object(self) -> "AltObject": + return super().update_object() - data = commands.ClickElement.run( - self._connection, - self, count, interval, wait + def find_object( + self, altby: AltBy, camera_altby: AltBy = AltBy.name(""), enabled=True + ) -> "AltObject": + return super().find_object_from_object( + altby.by, altby.value, camera_altby.by, camera_altby.value, enabled ) - return AltObject(self._altdriver, data) def get_visual_element_property(self, property_name: str) -> str: """Gets a value for a given visual element property. @@ -398,7 +78,7 @@ def get_visual_element_property(self, property_name: str) -> str: if self.type != "UIToolkit": raise exceptions.WrongAltObjectTypeException( "This method is only available for VisualElement objects") - return commands.GetVisualElementProperty.run(self._connection, property_name, self) + return commands.GetVisualElementProperty.run(self._connection, property_name, self) def wait_for_visual_element_property(self, property_name, property_value, timeout=20, interval=0.5, diff --git a/Bindings~/python/alttester/altobject_base.py b/Bindings~/python/alttester/altobject_base.py new file mode 100644 index 000000000..5e9bb7b4f --- /dev/null +++ b/Bindings~/python/alttester/altobject_base.py @@ -0,0 +1,369 @@ +""" + Copyright(C) 2025 Altom Consulting + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +""" + +import json +import alttester.commands as commands +import alttester.exceptions as exceptions +from alttester.by import By +from alttester.altby import AltBy + + +class AltObjectBase: + """The AltObjectBase class represents a base object present in the application and it allows you to interact with it.""" + + def __init__(self, altdriver, data): + self._altdriver = altdriver + self._data = data + + @property + def _connection(self): + return self._altdriver._connection + + @property + def name(self): + return self._data.get("name", "") + + @property + def id(self): + return self._data.get("id", 0) + + @property + def x(self): + return self._data.get("x", 0) + + @property + def y(self): + return self._data.get("y", 0) + + @property + def z(self): + return self._data.get("z", 0) + + @property + def mobileY(self): + return self._data.get("mobileY", 0) + + @property + def type(self): + return self._data.get("type", "") + + @property + def enabled(self): + return self._data.get("enabled", True) + + @property + def worldX(self): + return self._data.get("worldX", 0.0) + + @property + def worldY(self): + return self._data.get("worldY", 0.0) + + @property + def worldZ(self): + return self._data.get("worldZ", 0.0) + + @property + def idCamera(self): + return self._data.get("idCamera", 0) + + @property + def transformParentId(self): + return self._data.get("transformParentId", 0) + + @property + def transformId(self): + return self._data.get("transformId", 0) + + def to_json(self) -> dict: + return { + "name": self.name, + "id": self.id, + "x": self.x, + "y": self.y, + "z": self.z, + "mobileY": self.mobileY, + "type": self.type, + "enabled": self.enabled, + "worldX": self.worldX, + "worldY": self.worldY, + "worldZ": self.worldZ, + "transformParentId": self.transformParentId, + "transformId": self.transformId, + "idCamera": self.idCamera + } + + def update_object(self) -> "AltObjectBase": + altObject = commands.FindObject.run( + self._connection, + By.ID, self.id, By.NAME, "", enabled=True + ) + return AltObjectBase(self._altdriver, altObject) + + def get_screen_position(self) -> tuple: + """Returns the screen position. + + Returns: + tuple: A tuple containing ``x`` and ``y``. + + """ + return self.x, self.y + + def get_world_position(self) -> tuple: + """Returns the world position. + + Returns: + tuple: A tuple containing ``worldX``, ``worldY`` and ``worldZ``. + + """ + return self.worldX, self.worldY, self.worldZ + + def get_parent(self) -> "AltObjectBase": + """Returns the parent object. + + Returns: + AltObjectBase: The parent object. + + """ + data = commands.FindObject.run( + self._connection, + By.PATH, "//*[@id={}]/..".format(self.id), By.NAME, "", enabled=True + ) + return AltObjectBase(self._altdriver, data) + + def find_object_from_object(self, by, value, camera_by=By.NAME, camera_value="", enabled=True) -> "AltObjectBase": + """Returns the child of the object that meets the specified conditions.""" + data = commands.FindObjectFromObject.run(self._connection, + by, value, camera_by, camera_value, enabled, self) + if data is None: + return None + return AltObjectBase(self._altdriver, data) + + def get_all_components(self): + """Returns all components.""" + return commands.GetAllComponents.run(self._connection, self) + + def wait_for_component_property(self, component_name, property_name, property_value, assembly, timeout=20, interval=0.5, get_property_as_string=False, max_depth=2) -> str: + """Wait until a property has a specific value and returns the value of the given component property. + + Args: + component_name (:obj:`str`): The name of the component. If the component has a namespace the format should + look like this: ``"namespace.componentName"``. + property_name (:obj:`str`): The name of the property of which value you want. If the property is an array + you can specify which element of the array to return by doing ``property[index]``, or if you want a + property inside of another property you can get by doing ``property.subProperty``. + property_value(:obj:`str`): The value of the component expected + assembly (:obj:`str`): The name of the assembly containing the component. + timeout (:obj:`int`, optional): The number of seconds that it will wait for property. + interval (:obj:`float`, optional): The number of seconds after which it will try to find the object again. + The interval should be smaller than timeout. + get_property_as_string (:obj:`bool`, optional): A boolean value that makes the property_value + to be compared as a string with the property from the instrumented app. + max_depth (:obj:`int`, optional): An integer value that defines the maximum level from which to retrieve + properties. + + Returns: + str: The property value is serialized to a JSON string. + + """ + return commands.WaitForComponentProperty.run( + component_name, property_name, property_value, + assembly, self, timeout, interval, get_property_as_string, max_depth + ) + + def get_component_property(self, component_name, property_name, assembly, max_depth=2) -> str: + """Returns the value of the given component property. + + Args: + component_name (:obj:`str`): The name of the component. If the component has a namespace the format should + look like this: ``"namespace.componentName"``. + property_name (:obj:`str`): The name of the property of which value you want. If the property is an array + you can specify which element of the array to return by doing ``property[index]``, or if you want a + property inside of another property you can get by doing ``property.subProperty``. + assembly (:obj:`str`): The name of the assembly containing the component. + maxDepth (:obj:`int`, optional): Set how deep to serialize the property. Defaults to ``2``. + + Returns: + str: The property value is serialized to a JSON string. + + """ + return commands.GetComponentProperty.run( + self._connection, + component_name, property_name, assembly, max_depth, self + ) + + def set_component_property(self, component_name, property_name, assembly, value) -> str: + """Sets a value for a given component property. + + Args: + component_name (:obj:`str`): The name of the component. If the component has a namespace the format should + look like this: ``"namespace.componentName"``. + property_name (:obj:`str`): The name of the property of which value you want to set. + assembly (:obj:`str`): The name of the assembly containing the component. + value (:obj:`str`): The value to be set for the chosen component's property. + + Returns: + str: The property value is serialized to a JSON string. + + """ + return commands.SetComponentProperty.run( + self._connection, + component_name, property_name, value, assembly, self + ) + + def call_component_method(self, component_name, method_name, assembly, parameters=None, type_of_parameters=None) -> str: + """Invokes a method from an existing component of the object. + + Args: + component_name (:obj:`str`): The name of the script. If the script has a namespace the format should look + like this: ``"namespace.typeName"``. + method_name (:obj:`str`): The name of the public method that we want to call. If the method is inside a + static property/field to be able to call that method, methodName need to be the following format + ``"propertyName.MethodName"``. + assembly (:obj:`str`): The name of the assembly containing the script. + parameters (:obj:`list`, :obj:`tuple`, optional): Defaults to ``None``. + type_of_parameters (:obj:`list`, :obj:`tuple`, optional): Defaults to ``None``. + + Return: + str: The value returned by the method is serialized to a JSON string. + + """ + return commands.CallMethod.run( + self._connection, + component_name, + method_name, + alt_object=self, + parameters=parameters, + type_of_parameters=type_of_parameters, + assembly=assembly + ) + + def get_text(self) -> str: + """Returns text value from a Button, Text, InputField. This also works with TextMeshPro elements. + + Returns: + str: The text value of the AltObjectBase. + + """ + return commands.GetText.run(self._connection, self) + + def set_text(self, text, submit=False) -> "AltObjectBase": + """Sets text value for a Button, Text or InputField. This also works with TextMeshPro elements. + + Args: + text (obj:`str`): The text to be set. + submit (obj:`bool`): If set will trigger a submit event. + + Returns: + AltObjectBase: The current AltObjectBase. + + """ + data = commands.SetText.run(self._connection, text, self, submit) + return AltObjectBase(self._altdriver, data) + + def pointer_up(self) -> "AltObjectBase": + """Simulates pointer up action on the object. + + Returns: + AltObjectBase: The current AltObjectBase. + + """ + data = commands.PointerUp.run(self._connection, self) + return AltObjectBase(self._altdriver, data) + + def pointer_down(self) -> "AltObjectBase": + """Simulates pointer down action on the object. + + Returns: + AltObjectBase: The current AltObjectBase. + + """ + data = commands.PointerDown.run(self._connection, self) + return AltObjectBase(self._altdriver, data) + + def pointer_enter(self) -> "AltObjectBase": + """Simulates pointer enter action on the object. + + Returns: + AltObjectBase: The current AltObjectBase. + + """ + data = commands.PointerEnter.run(self._connection, self) + return AltObjectBase(self._altdriver, data) + + def pointer_exit(self) -> "AltObjectBase": + """Simulates pointer exit action on the object. + + Returns: + AltObjectBase: The current AltObjectBase. + + """ + data = commands.PointerExit.run(self._connection, self) + return AltObjectBase(self._altdriver, data) + + def tap(self, count=1, interval=0.1, wait=True) -> "AltObjectBase": + """Taps the current object. + + Args: + count (:obj:`int`, optional): Number of taps. Defaults to ``1``. + interval (:obj:`int`, :obj:`float`, optional): Interval between taps in seconds. Defaults to ``0.1``. + wait (:obj:`int`, optional): Wait for command to finish. Defaults to ``True``. + + Returns: + AltObjectBase: The tapped object. + + """ + data = commands.TapElement.run( + self._connection, + self, count, interval, wait + ) + return AltObjectBase(self._altdriver, data) + + def click(self, count=1, interval=0.1, wait=True) -> "AltObjectBase": + """Clicks the current object. + + Args: + count (:obj:`int`, optional): Number of clicks. Defaults to ``1``. + interval (:obj:`int`, :obj:`float`, optional): Interval between clicks in seconds. Defaults to ``0.1``. + wait (:obj:`int`, optional): Wait for command to finish. Defaults to ``True``. + + Returns: + AltObjectBase: The clicked object. + + """ + data = commands.ClickElement.run( + self._connection, + self, count, interval, wait + ) + return AltObjectBase(self._altdriver, data) + + def get_visual_element_property(self, property_name: str) -> str: + """Gets a value for a given visual element property. + + Args: + property_name: The name of the property of which value you want to get. + + Returns: + The property value is serialized to a JSON string. + + Raises: + WrongAltObjectTypeException: The method is called on an object that is not a VisualElement. + """ + if self.type != "UIToolkit": + raise exceptions.WrongAltObjectTypeException( + "This method is only available for VisualElement objects") + return commands.GetVisualElementProperty.run(self._connection, property_name, self) diff --git a/Bindings~/python/alttester/altobject_unreal.py b/Bindings~/python/alttester/altobject_unreal.py new file mode 100644 index 000000000..6dcbe7636 --- /dev/null +++ b/Bindings~/python/alttester/altobject_unreal.py @@ -0,0 +1,137 @@ +""" + Copyright(C) 2024 Altom Consulting + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +""" + +from alttester.altobject import AltObject +from alttester.altby import AltBy +from alttester.by import By + + +class AltObjectUnreal(AltObject): + """The AltObjectUnreal class represents an object present in the Unreal application and it allows you to interact with it. + + It is the return type of the methods in the “find_*” category from the AltDriverUnreal class. + """ + + def __init__(self, altdriver, data): + super().__init__(altdriver, data) + + def __repr__(self): + return f"AltObjectUnreal({repr(self._altdriver)}, {repr(self.to_json())})" + + def __str__(self): + return super().__str__() + + def to_json(self) -> dict: + return super().to_json() + + def get_component_property(self, component_name, property_name): + """Returns the value of the given component property. + + Args: + component_name (:obj:`str`): The name of the component. + property_name (:obj:`str`): The name of the property of which value you want. + + Returns: + str: The property value is serialized to a JSON string. + + """ + return super().get_component_property(component_name, property_name, "") + + def wait_for_component_property( + self, + component_name, + property_name, + property_value, + timeout=20, + interval=0.5, + get_property_as_string=False, + ): + """Wait until a property has a specific value and returns the value of the given component property. + + Args: + component_name (:obj:`str`): The name of the component. + property_name (:obj:`str`): The name of the property of which value you want. + property_value(:obj:`str`): The value of the component expected. + timeout (:obj:`int`, optional): The number of seconds that it will wait for property. + interval (:obj:`float`, optional): The number of seconds after which it will try to find the object again. + get_property_as_string (:obj:`bool`, optional): A boolean value that makes the property_value + to be compared as a string with the property from the instrumented app. + + Returns: + str: The property value is serialized to a JSON string. + + """ + return super().wait_for_component_property( + component_name, + property_name, + property_value, + "", + timeout, + interval, + get_property_as_string, + ) + + def set_component_property(self, component_name, property_name, value): + """Sets a value for a given component property. + + Args: + component_name (:obj:`str`): The name of the component. + property_name (:obj:`str`): The name of the property of which value you want to set. + value (:obj:`str`): The value to be set for the chosen component's property. + + Returns: + str: The property value is serialized to a JSON string. + + """ + return super().set_component_property(component_name, property_name, value, "") + + def call_component_method( + self, component_name, method_name, parameters=None, type_of_parameters=None + ): + """Invokes a method from an existing component of the object. + + Args: + component_name (:obj:`str`): The name of the script. + method_name (:obj:`str`): The name of the public method that we want to call. + parameters (:obj:`list`, :obj:`tuple`, optional): Defaults to ``None``. + type_of_parameters (:obj:`list`, :obj:`tuple`, optional): Defaults to ``None``. + + Return: + str: The value returned by the method is serialized to a JSON string. + + """ + return super().call_component_method( + component_name, method_name, "", parameters, type_of_parameters + ) + + def set_text(self, text) -> "AltObjectUnreal": + """Sets text value for a Button, Text or InputField. + + Args: + text (obj:`str`): The text to be set. + + Returns: + AltObjectUnreal: The current AltObjectUnreal. + + """ + data = super().set_text(text, False) + return AltObjectUnreal(self._altdriver, data.to_json()) + + def find_object(self, altby: AltBy, enabled=True) -> "AltObjectUnreal": + return AltObjectUnreal( + super().find_object_from_object(altby.by, altby.value, By.NAME, "", enabled) + ) diff --git a/Bindings~/python/tests/integration/conftest.py b/Bindings~/python/tests/integration/conftest.py index 15742aa0c..7e63a06ca 100644 --- a/Bindings~/python/tests/integration/conftest.py +++ b/Bindings~/python/tests/integration/conftest.py @@ -21,7 +21,7 @@ import sys import pytest import time -from alttester import AltDriver +from alttester import AltDriver, AltDriverUnity from appium import webdriver from appium.webdriver.common.mobileby import MobileBy from appium.options.android import UiAutomator2Options @@ -61,7 +61,8 @@ def get_port(): - return int(os.environ.get("ALTSERVER_PORT", 13000)) + return int(os.environ.get("ALTSERVER_PORT", 13005)) + #### TO CHANGE THE PORT NUMBER #### def get_host(): @@ -99,6 +100,34 @@ def alt_driver(request, appium_driver, worker_id, current_device): platform_version=current_device["os_version"].split(".")[0], timeout=180 ) + + request.cls.alt_driver = alt_driver + print("Started alt_driver (worker {})".format(worker_id) + + " with device: {}".format(current_device)) + yield alt_driver + + try: + alt_driver.stop() + except Exception: + print("AltDriver was already stopped.") + + +@pytest.fixture(scope="class") +def alt_driver_unity(request, appium_driver, worker_id, current_device): + request.cls.alt_driver.stop() + + platform = current_device["os"] + if current_device["os"] == "ios": + platform = "iphone" + alt_driver = AltDriverUnity( + host=get_host(), + port=get_port(), + app_name=get_app_name(), + platform=platform, + platform_version=current_device["os_version"].split(".")[0], + timeout=180 + ) + request.cls.alt_driver = alt_driver print("Started alt_driver (worker {})".format(worker_id) + " with device: {}".format(current_device)) diff --git a/Bindings~/python/tests/integration/test_find_objects_with_unity_driver.py b/Bindings~/python/tests/integration/test_find_objects_with_unity_driver.py new file mode 100644 index 000000000..80cb0fd4e --- /dev/null +++ b/Bindings~/python/tests/integration/test_find_objects_with_unity_driver.py @@ -0,0 +1,102 @@ +""" + Copyright(C) 2024 Altom Consulting + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +""" + +import pytest +from alttester.altby import AltBy +from .utils import Scenes +from alttester.altdriver_unity import AltDriverUnity + + +@pytest.mark.usefixtures("alt_driver_unity") +class TestFindObjectsWithUnityDriver: + alt_driver: AltDriverUnity + + @pytest.fixture(autouse=True) + def setup(self): + self.alt_driver.reset_input() + self.alt_driver.load_scene(Scenes.Scene01) + + def test_find_object_with_unity_driver(self): + capsule = self.alt_driver.find_object(AltBy.name("CapsuleInfo")) + self.alt_driver.find_object(AltBy.path("//Capsule"), AltBy.name("")) + self.alt_driver.find_object(AltBy.path("//Capsule"), AltBy.name(""), True) + self.alt_driver.find_object(AltBy.tag("plane")) + self.alt_driver.find_object(AltBy.layer("Water")) + capsule_text = capsule.get_text() + self.alt_driver.find_object(AltBy.text(capsule_text)) + self.alt_driver.find_object( + AltBy.name("CapsuleInfo"), AltBy.name(""), enabled=True + ) + self.alt_driver.find_object( + AltBy.name("CapsuleInfo"), AltBy.name(""), enabled=False + ) + + def test_find_objects_with_unity_driver(self): + objects = self.alt_driver.find_objects(AltBy.name("Capsule")) + assert len(objects) > 0 + objects = self.alt_driver.find_objects( + AltBy.name("Capsule"), AltBy.name(""), enabled=True + ) + assert len(objects) > 0 + objects = self.alt_driver.find_objects( + AltBy.name("Capsule"), AltBy.name(""), enabled=False + ) + assert len(objects) > 0 + + def test_find_object_which_contains_with_unity_driver(self): + self.alt_driver.find_object_which_contains(AltBy.name("Capsule")) + self.alt_driver.find_object_which_contains( + AltBy.name("Capsule"), AltBy.name(""), enabled=True + ) + self.alt_driver.find_object_which_contains( + AltBy.name("Capsule"), AltBy.name(""), enabled=False + ) + + def test_find_objects_which_contain_with_unity_driver(self): + objects = self.alt_driver.find_objects_which_contain(AltBy.name("Capsule")) + assert len(objects) > 0 + objects = self.alt_driver.find_objects_which_contain( + AltBy.name("Capsule"), AltBy.name(""), enabled=True + ) + assert len(objects) > 0 + objects = self.alt_driver.find_objects_which_contain( + AltBy.name("Capsule"), AltBy.name(""), enabled=False + ) + assert len(objects) > 0 + + def test_wait_for_object_with_unity_driver(self): + self.alt_driver.wait_for_object(AltBy.name("Capsule")) + self.alt_driver.wait_for_object( + AltBy.name("Capsule"), AltBy.name(""), enabled=True + ) + + def test_wait_for_object_which_contains_with_unity_driver(self): + self.alt_driver.wait_for_object_which_contains(AltBy.name("Capsule")) + self.alt_driver.wait_for_object_which_contains( + AltBy.name("Capsule"), AltBy.name(""), enabled=True + ) + + def test_wait_for_object_to_not_be_present_with_unity_driver(self): + self.alt_driver.wait_for_object_to_not_be_present( + AltBy.name("NonExistentObject"), timeout=1 + ) + self.alt_driver.wait_for_object_to_not_be_present( + AltBy.name("NonExistentObject"), AltBy.name(""), timeout=1, enabled=True + ) + self.alt_driver.wait_for_object_to_not_be_present( + AltBy.name("NonExistentObject"), AltBy.name(""), timeout=1, enabled=False + ) \ No newline at end of file diff --git a/Bindings~/python/tests/integration/test_scene01_part1.py b/Bindings~/python/tests/integration/test_scene01_part1.py index 254c2857c..b8f657bfd 100644 --- a/Bindings~/python/tests/integration/test_scene01_part1.py +++ b/Bindings~/python/tests/integration/test_scene01_part1.py @@ -21,10 +21,12 @@ from .utils import Scenes from alttester import By, PlayerPrefKeyType import alttester.exceptions as exceptions +from alttester.altdriver import AltDriver class TestScene01Part1: - + alt_driver: AltDriver + @pytest.fixture(autouse=True) def setup(self): self.alt_driver.reset_input() @@ -51,7 +53,7 @@ def test_tap_object(self): def test_find_object_by_name(self): plane = self.alt_driver.find_object(By.NAME, "Plane") capsule = self.alt_driver.find_object(By.NAME, "Capsule") - + assert plane.name == "Plane" assert capsule.name == "Capsule" diff --git a/Bindings~/python/tests/integration/test_scene01_part1_unity_driver.py b/Bindings~/python/tests/integration/test_scene01_part1_unity_driver.py new file mode 100644 index 000000000..1154622fe --- /dev/null +++ b/Bindings~/python/tests/integration/test_scene01_part1_unity_driver.py @@ -0,0 +1,546 @@ +""" + Copyright(C) 2024 Altom Consulting + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +""" + +import pytest +import time + +from .utils import Scenes +from alttester import By, PlayerPrefKeyType +from alttester.altby import AltBy +import alttester.exceptions as exceptions +from alttester.altdriver_unity import AltDriverUnity + + +@pytest.mark.usefixtures("alt_driver_unity") +class TestScene01Part1WithUnityDriver: + alt_driver: AltDriverUnity + + @pytest.fixture(autouse=True) + def setup(self): + self.alt_driver.reset_input() + self.alt_driver.load_scene(Scenes.Scene01) + + def test_tap_ui_object(self): + expected_text = "UIButton clicked to jump capsule!" + + self.alt_driver.find_object(AltBy.name("UIButton")).tap() + capsule_info = self.alt_driver.wait_for_object( + AltBy.path("//CapsuleInfo[@text={}]".format(expected_text)), timeout=1) + + assert capsule_info.get_text() == expected_text + + def test_tap_object(self): + expected_text = "Capsule was clicked to jump!" + + self.alt_driver.find_object(AltBy.name("Capsule")).tap() + capsule_info = self.alt_driver.wait_for_object( + AltBy.path("//CapsuleInfo[@text={}]".format(expected_text)), timeout=1) + + assert capsule_info.get_text() == expected_text + + def test_find_object_by_name(self): + plane = self.alt_driver.find_object(AltBy.name("Plane")) + capsule = self.alt_driver.find_object(AltBy.name("Capsule")) + + assert plane.name == "Plane" + assert capsule.name == "Capsule" + + def test_find_object_by_name_and_parent(self): + capsule_element = self.alt_driver.find_object( + AltBy.name("Canvas/CapsuleInfo")) + + assert capsule_element.name == "CapsuleInfo" + + def test_find_object_child(self): + alt_object = self.alt_driver.find_object(AltBy.path("//UIButton/*")) + + assert alt_object.name == "Text" + + def test_find_object_by_tag(self): + alt_object = self.alt_driver.find_object(AltBy.tag("plane")) + assert alt_object.name == "Plane" + + def test_find_object_by_layer(self): + alt_object = self.alt_driver.find_object(AltBy.layer("Water")) + assert alt_object.name == "Capsule" + + def test_find_object_by_text(self): + text = self.alt_driver.find_object(AltBy.name("CapsuleInfo")).get_text() + element = self.alt_driver.find_object(AltBy.text(text)) + + assert element.get_text() == text + + @pytest.mark.parametrize("component, name", [ + ("CapsuleCollider", "Capsule"), + ("AltExampleScriptCapsule", "Capsule"), + ("AltTesterExamples.Scripts.AltExampleScriptCapsule", "Capsule") + ]) + def test_find_object_by_component(self, component, name): + alt_object = self.alt_driver.find_object(AltBy.component(component)) + assert alt_object.name == name + + @pytest.mark.parametrize("partial_name", ["Pla", "EventSy"]) + def test_find_object_which_contains(self, partial_name): + plane = self.alt_driver.find_object_which_contains( + AltBy.name(partial_name)) + assert partial_name in plane.name + + @pytest.mark.parametrize("name, count", [ + ("Plane", 2), + ("something that does not exist", 0) + ]) + def test_find_objects_by_name(self, name, count): + alt_objects = self.alt_driver.find_objects(AltBy.name(name)) + assert len(alt_objects) == count + + def test_find_objects_by_tag(self): + alt_objects = self.alt_driver.find_objects(AltBy.tag("plane")) + + assert len(alt_objects) == 2 + for alt_object in alt_objects: + assert alt_object.name == "Plane" + + def test_find_objects_by_layer(self): + alt_objects = self.alt_driver.find_objects(AltBy.layer("Default")) + assert len(alt_objects) >= 10 + + def test_find_objects_by_component(self): + alt_objects = self.alt_driver.find_objects( + AltBy.component("UnityEngine.MeshFilter")) + assert len(alt_objects) == 5 + + def test_find_parent_using_path(self): + parent = self.alt_driver.find_object(AltBy.path("//CapsuleInfo/..")) + assert parent.name == "Canvas" + + def test_find_objects_which_contain_by_name(self): + alt_objects = self.alt_driver.find_objects_which_contain( + AltBy.name("Capsule")) + + assert len(alt_objects) == 2 + for alt_object in alt_objects: + assert "Capsule" in alt_object.name + + def test_find_object_which_contains_with_not_existing_object(self): + with pytest.raises(exceptions.NotFoundException) as execinfo: + self.alt_driver.find_object_which_contains( + AltBy.name("EventNonExisting")) + + assert str( + execinfo.value) == "Object not found" + + def test_get_all_components(self): + components = self.alt_driver.find_object( + AltBy.name("Canvas")).get_all_components() + assert len(components) == 5 + assert components[0]["componentName"] == "UnityEngine.RectTransform" + assert components[0]["assemblyName"] == "UnityEngine.CoreModule" + + def test_wait_for_object(self): + alt_object = self.alt_driver.wait_for_object(AltBy.name("Capsule")) + assert alt_object.name == "Capsule" + + def test_wait_for_object_with_non_existing_object(self): + object_name = "Non Existing Object" + + with pytest.raises(exceptions.WaitTimeOutException) as execinfo: + self.alt_driver.wait_for_object(AltBy.name(object_name), timeout=1) + + assert str(execinfo.value) == "Element {} not found after 1 seconds".format( + object_name) + + def test_wait_for_object_by_name(self): + plane = self.alt_driver.wait_for_object(AltBy.name("Plane")) + capsule = self.alt_driver.wait_for_object(AltBy.name("Capsule")) + + assert plane.name == "Plane" + assert capsule.name == "Capsule" + + def test_get_application_screen_size(self): + screensize = self.alt_driver.get_application_screensize() + # We cannot set resolution on iOS so we don't know the exact resolution + # we just want to see that it returns a value and is different than 0 + assert screensize[0] != 0 + assert screensize[1] != 0 + + def test_wait_for_object_with_text(self): + text_to_wait_for = self.alt_driver.find_object( + AltBy.name("CapsuleInfo")).get_text() + capsule_info = self.alt_driver.wait_for_object( + AltBy.path("//CapsuleInfo[@text={}]".format(text_to_wait_for)), timeout=1) + + assert capsule_info.name == "CapsuleInfo" + assert capsule_info.get_text() == text_to_wait_for + + def test_wait_for_object_with_wrong_text(self): + with pytest.raises(exceptions.WaitTimeOutException) as execinfo: + self.alt_driver.wait_for_object( + AltBy.path("//CapsuleInfo[@text=aaaaa]"), timeout=1) + + assert str( + execinfo.value) == "Element //CapsuleInfo[@text=aaaaa] not found after 1 seconds" + + def test_wait_for_object_which_contains(self): + alt_object = self.alt_driver.wait_for_object_which_contains( + AltBy.name("Main")) + assert alt_object.name == "Main Camera" + + def test_wait_for_object_to_not_be_present(self): + self.alt_driver.wait_for_object_to_not_be_present(AltBy.name("Capsuule")) + + def test_wait_for_object_to_not_be_present_fail(self): + with pytest.raises(exceptions.WaitTimeOutException) as execinfo: + self.alt_driver.wait_for_object_to_not_be_present( + AltBy.name("Capsule"), timeout=1) + + assert str( + execinfo.value) == "Element Capsule still found after 1 seconds" + + def test_get_text_with_non_english_text(self): + text = self.alt_driver.find_object( + AltBy.name("NonEnglishText")).get_text() + assert text == "BJÖRN'S PASS" + + def test_get_text_with_chinese_letters(self): + text = self.alt_driver.find_object( + AltBy.name("ChineseLetters")).get_text() + assert text == "哦伊娜哦" + + def test_set_text(self): + text_object = self.alt_driver.find_object(AltBy.name("NonEnglishText")) + original_text = text_object.get_text() + after_text = text_object.set_text("ModifiedText").get_text() + + assert original_text != after_text + assert after_text == "ModifiedText" + + def test_double_tap(self): + counter_button = self.alt_driver.find_object(AltBy.name("ButtonCounter")) + counter_button_text = self.alt_driver.find_object( + AltBy.name("ButtonCounter/Text")) + counter_button.tap(count=2) + + # time.sleep(0.5) + + assert counter_button_text.get_text() == "2" + + def test_tap_on_screen_where_there_are_no_objects(self): + counter_button = self.alt_driver.find_object(AltBy.name("ButtonCounter")) + self.alt_driver.tap({"x": 1, "y": counter_button.y + 100}) + + def test_hold_button(self): + button = self.alt_driver.find_object(AltBy.name("UIButton")) + self.alt_driver.hold_button(button.get_screen_position(), duration=1) + + capsule_info = self.alt_driver.find_object(AltBy.name("CapsuleInfo")) + text = capsule_info.get_text() + assert text == "UIButton clicked to jump capsule!" + + def test_hold_button_without_wait(self): + button = self.alt_driver.find_object(AltBy.name("UIButton")) + self.alt_driver.hold_button( + button.get_screen_position(), duration=1, wait=False) + + time.sleep(2) + + capsule_info = self.alt_driver.find_object(AltBy.name("CapsuleInfo")) + text = capsule_info.get_text() + assert text == "UIButton clicked to jump capsule!" + + def test_wait_for_component_property(self): + alt_object = self.alt_driver.find_object(AltBy.name("Capsule")) + result = alt_object.wait_for_component_property( + "AltExampleScriptCapsule", "TestBool", True, + "Assembly-CSharp") + + assert result is True + + @pytest.mark.iOSUnsupported + @pytest.mark.WebGLUnsupported + @pytest.mark.AndroidUnsupported + def test_wait_for_component_property_get_property_as_string(self): + Canvas = self.alt_driver.wait_for_object(AltBy.path("/Canvas")) + Canvas.wait_for_component_property("UnityEngine.RectTransform", "rect.x", "-960.0", + "UnityEngine.CoreModule", 1, get_property_as_string=True) + + Canvas.wait_for_component_property("UnityEngine.RectTransform", "hasChanged", True, + "UnityEngine.CoreModule", 1, get_property_as_string=True) + + Canvas.wait_for_component_property("UnityEngine.RectTransform", "constrainProportionsScale", False, + "UnityEngine.CoreModule", 1, get_property_as_string=True) + + Canvas.wait_for_component_property("UnityEngine.RectTransform", "transform", + "[[],[[]],[[]],[[]],[[]],[[],[],[]],[[[],[],[]]],[],[],[[]],[[]],[[]]]", + "UnityEngine.CoreModule", 1, get_property_as_string=True) + + Canvas.wait_for_component_property("UnityEngine.RectTransform", "name", "Canvas", + "UnityEngine.CoreModule", 1, get_property_as_string=True) + + def test_wait_for_component_property_component_not_found(self): + componentName = "AltTester.AltTesterUnitySDK.Commands.AltRunnerTest" + propertyName = "InstrumentationSettings.AltServerPort" + alt_object = self.alt_driver.find_object(AltBy.name("AltTesterPrefab")) + with pytest.raises(exceptions.WaitTimeOutException) as execinfo: + alt_object.wait_for_component_property( + componentName, + propertyName, + "Test", + "AltTester.AltTesterUnitySDK", + timeout=2, + ) + assert str( + execinfo.value + ) == "After 2 seconds, exception was: Component not found for component: {} and property {}".format( + componentName, propertyName + ) + + def test_wait_for_component_property_not_found(self): + componentName = "AltTester.AltTesterUnitySDK.Commands.AltRunner" + propertyName = "InstrumentationSettings.AltServerPortTest" + alt_object = self.alt_driver.find_object(AltBy.name("AltTesterPrefab")) + with pytest.raises(exceptions.WaitTimeOutException) as execinfo: + alt_object.wait_for_component_property( + componentName, + propertyName, + "Test", + "AltTester.AltTesterUnitySDK", + timeout=2, + ) + assert str(execinfo.value) == ( + "After 2 seconds, exception was: Property AltServerPortTest not found " + "for component: {} and property {}".format( + componentName, propertyName) + ) + + def test_wait_for_component_property_timeout(self): + componentName = "AltTester.AltTesterUnitySDK.Commands.AltRunner" + propertyName = "InstrumentationSettings.AltServerPort" + alt_object = self.alt_driver.find_object(AltBy.name("AltTesterPrefab")) + with pytest.raises(exceptions.WaitTimeOutException) as execinfo: + alt_object.wait_for_component_property( + componentName, propertyName, "Test", "AltTester.AltTesterUnitySDK", 2 + ) + assert ( + str(execinfo.value) + == "Property InstrumentationSettings.AltServerPort was 13005, not Test as expected, after 2 seconds" + ) + + @pytest.mark.iOSUnsupported + @pytest.mark.WebGLUnsupported + def test_wait_for_component_property_assembly_not_found(self): + componentName = "AltExampleScriptCapsule" + propertyName = "InstrumentationSettings.AltServerPort" + alt_object = self.alt_driver.find_object(AltBy.name("AltTesterPrefab")) + with pytest.raises(exceptions.WaitTimeOutException) as execinfo: + alt_object.wait_for_component_property( + componentName, propertyName, "13000", "Assembly-CSharpTest", timeout=2 + ) + assert str( + execinfo.value + ) == "After 2 seconds, exception was: Assembly not found for component: {} and property {}".format( + componentName, propertyName + ) + + def test_get_component_property(self): + alt_object = self.alt_driver.find_object(AltBy.name("Capsule")) + result = alt_object.get_component_property( + "AltExampleScriptCapsule", "arrayOfInts", "Assembly-CSharp") + + assert result == [1, 2, 3] + + def test_get_component_property_with_bool(self): + alt_object = self.alt_driver.find_object(AltBy.name("Capsule")) + result = alt_object.get_component_property( + "AltExampleScriptCapsule", "TestBool", "Assembly-CSharp") + + assert result is True + + def test_set_component_property(self): + alt_object = self.alt_driver.find_object(AltBy.name("Capsule")) + alt_object.set_component_property( + "AltExampleScriptCapsule", "arrayOfInts", "Assembly-CSharp", [2, 3, 4]) + + alt_object = self.alt_driver.find_object(AltBy.name("Capsule")) + result = alt_object.get_component_property( + "AltExampleScriptCapsule", "arrayOfInts", "Assembly-CSharp") + + assert result == [2, 3, 4] + + def test_call_component_method(self): + alt_object = self.alt_driver.find_object(AltBy.name("Capsule")) + result = alt_object.call_component_method( + "AltExampleScriptCapsule", "Jump", "Assembly-CSharp", parameters=["setFromMethod"]) + assert result is None + + self.alt_driver.wait_for_object( + AltBy.path("//CapsuleInfo[@text=setFromMethod]"), timeout=1) + assert self.alt_driver.find_object( + AltBy.name("CapsuleInfo")).get_text() == "setFromMethod" + + def test_call_component_method_with_no_parameters(self): + result = self.alt_driver.find_object(AltBy.path("/Canvas/Button/Text")) + text = result.call_component_method( + "UnityEngine.UI.Text", "get_text", "UnityEngine.UI") + assert text == "Change Camera Mode" + + def test_call_component_method_with_parameters(self): + assembly = "UnityEngine.UI" + expected_font_size = 16 + alt_object = self.alt_driver.find_object( + AltBy.path("/Canvas/UnityUIInputField/Text")) + alt_object.call_component_method( + "UnityEngine.UI.Text", "set_fontSize", assembly, + parameters=["16"] + ) + font_size = alt_object.call_component_method( + "UnityEngine.UI.Text", "get_fontSize", assembly, + parameters=[] + ) + + assert expected_font_size == font_size + + def test_call_component_method_with_assembly(self): + capsule = self.alt_driver.find_object(AltBy.name("Capsule")) + initial_rotation = capsule.get_component_property( + "UnityEngine.Transform", "rotation", "UnityEngine.CoreModule") + capsule.call_component_method( + "UnityEngine.Transform", "Rotate", "UnityEngine.CoreModule", + parameters=["10", "10", "10"], + type_of_parameters=["System.Single", + "System.Single", "System.Single"], + ) + + capsule_after_rotation = self.alt_driver.find_object( + AltBy.name("Capsule")) + final_rotation = capsule_after_rotation.get_component_property( + "UnityEngine.Transform", "rotation", "UnityEngine.CoreModule",) + + assert initial_rotation["x"] != final_rotation["x"] or initial_rotation["y"] != final_rotation["y"] or \ + initial_rotation["z"] != final_rotation["z"] or initial_rotation["w"] != final_rotation["w"] + + @pytest.mark.parametrize("parameters", [ + [1, "stringparam", 0.5, [1, 2, 3]], + (1, "stringparam", 0.5, [1, 2, 3]) + ]) + def test_call_component_method_with_multiple_parameters(self, parameters): + alt_object = self.alt_driver.find_object(AltBy.name("Capsule")) + result = alt_object.call_component_method( + "AltExampleScriptCapsule", + "TestCallComponentMethod", + "Assembly-CSharp", + parameters=parameters + ) + + assert result == "1,stringparam,0.5,[1,2,3]" + + def test_call_component_method_with_multiple_definitions(self): + capsule = self.alt_driver.find_object(AltBy.name("Capsule")) + capsule.call_component_method( + "AltExampleScriptCapsule", "Test", "Assembly-CSharp", + parameters=["2"], + type_of_parameters=["System.Int32"] + ) + capsule_info = self.alt_driver.find_object(AltBy.name("CapsuleInfo")) + + assert capsule_info.get_text() == "6" + + def test_call_component_method_with_invalid_assembly(self): + alt_object = self.alt_driver.find_object(AltBy.name("Capsule")) + + with pytest.raises(exceptions.AssemblyNotFoundException) as execinfo: + alt_object.call_component_method( + "RandomComponent", "TestMethodWithManyParameters", "RandomAssembly", + parameters=[1, "stringparam", 0.5, [1, 2, 3]], + type_of_parameters=[], + ) + + assert str(execinfo.value) == "Assembly not found" + + def test_call_component_method_with_incorrect_number_of_parameters(self): + alt_object = self.alt_driver.find_object(AltBy.name("Capsule")) + + with pytest.raises(exceptions.MethodWithGivenParametersNotFoundException) as execinfo: + alt_object.call_component_method( + "AltExampleScriptCapsule", "TestMethodWithManyParameters", "Assembly-CSharp", + parameters=["stringparam", 0.5, [1, 2, 3]], + type_of_parameters=[] + ) + + assert str(execinfo.value) == \ + "No method found with 3 parameters matching signature: TestMethodWithManyParameters(System.String[])" + + def test_call_component_method_with_invalid_method_argument_types(self): + alt_object = self.alt_driver.find_object(AltBy.name("Capsule")) + + with pytest.raises(exceptions.FailedToParseArgumentsException) as execinfo: + + alt_object.call_component_method( + "AltExampleScriptCapsule", "TestMethodWithManyParameters", "Assembly-CSharp", + parameters=["stringnoint", "stringparams", 0.5, [1, 2, 3]], + type_of_parameters=[] + ) + + assert str( + execinfo.value) == "Could not parse parameter '\"stringnoint\"' to type System.Int32" + + def test_call_static_method(self): + self.alt_driver.call_static_method( + "UnityEngine.PlayerPrefs", "SetInt", "UnityEngine.CoreModule", ["Test", "1"]) + value = self.alt_driver.call_static_method( + "UnityEngine.PlayerPrefs", "GetInt", "UnityEngine.CoreModule", ["Test", "2"]) + + assert value == 1 + + @pytest.mark.parametrize("key_value, key_type", [ + (1, PlayerPrefKeyType.Int), + (1.3, PlayerPrefKeyType.Float), + ("string value", PlayerPrefKeyType.String) + ]) + def test_set_player_pref_keys(self, key_value, key_type): + self.alt_driver.delete_player_pref() + self.alt_driver.set_player_pref_key("test", key_value, key_type) + actual_value = self.alt_driver.get_player_pref_key("test", key_type) + + assert actual_value == key_value + + def test_press_next_scene(self): + initial_scene = self.alt_driver.get_current_scene() + self.alt_driver.find_object(AltBy.name("NextScene")).tap() + + current_scene = self.alt_driver.get_current_scene() + assert initial_scene != current_scene + + def test_acceleration(self): + capsule = self.alt_driver.find_object(AltBy.name("Capsule")) + initial_position = [capsule.worldX, capsule.worldY, capsule.worldZ] + self.alt_driver.tilt([1, 1, 1], duration=0.1, wait=False) + + time.sleep(0.1) + + capsule = self.alt_driver.find_object(AltBy.name("Capsule")) + final_position = [capsule.worldX, capsule.worldY, capsule.worldZ] + + assert initial_position != final_position + + def test_acceleration_and_wait(self): + capsule = self.alt_driver.find_object(AltBy.name("Capsule")) + initial_position = [capsule.worldX, capsule.worldY, capsule.worldZ] + self.alt_driver.tilt([1, 1, 1], duration=0.1) + + capsule = self.alt_driver.find_object(AltBy.name("Capsule")) + final_position = [capsule.worldX, capsule.worldY, capsule.worldZ] + assert initial_position != final_position diff --git a/Bindings~/python/tests/integration/test_scene01_part2.py b/Bindings~/python/tests/integration/test_scene01_part2.py index 08421b421..d2648a4bc 100644 --- a/Bindings~/python/tests/integration/test_scene01_part2.py +++ b/Bindings~/python/tests/integration/test_scene01_part2.py @@ -17,12 +17,16 @@ import pytest +from alttester.altobject import AltObject + from .utils import Scenes from alttester import By, AltKeyCode +from alttester.altdriver import AltDriver import alttester.exceptions as exceptions class TestScene01Part2: + alt_driver: AltDriver @pytest.fixture(autouse=True) def setup(self): @@ -476,14 +480,14 @@ def test_reset_input(self): assert 0 == countKeyDown def test_find_object_in_object_by_tag(self): - parent = self.alt_driver.find_object(By.NAME, "Canvas") - child = parent.find_object_from_object(By.TAG, "Finish") - assert child.name == "Button" + parent = self.alt_driver.find_object(By.NAME, "UIWithWorldSpace") + child = parent.find_object_from_object(By.TAG, "plane") + assert child.name == "Plane" def test_find_object_in_object_by_layer(self): - parent = self.alt_driver.find_object(By.NAME, "Canvas") - child = parent.find_object_from_object(By.LAYER, "ButtonLayer") - assert child.name == "Button" + parent = self.alt_driver.find_object(By.NAME, "UIWithWorldSpace") + child = parent.find_object_from_object(By.LAYER, "Default") + assert child.name == "Plane" def test_find_object_in_object_by_name(self): parent = self.alt_driver.find_object(By.NAME, "Canvas") diff --git a/Bindings~/python/tests/integration/test_scene01_part2_unity_driver.py b/Bindings~/python/tests/integration/test_scene01_part2_unity_driver.py new file mode 100644 index 000000000..63c948071 --- /dev/null +++ b/Bindings~/python/tests/integration/test_scene01_part2_unity_driver.py @@ -0,0 +1,479 @@ +""" + Copyright(C) 2024 Altom Consulting + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +""" +import sys +import os +import pytest + +from .utils import Scenes +from alttester import By, AltKeyCode +import alttester.exceptions as exceptions +from alttester.altdriver_unity import AltDriverUnity +from alttester.altby import AltBy + +@pytest.mark.usefixtures("alt_driver_unity") +class TestScene01Part2WithUnityDriver: + alt_driver: AltDriverUnity + + @pytest.fixture(autouse=True) + def setup(self): + self.alt_driver.reset_input() + self.alt_driver.load_scene(Scenes.Scene01) + + def test_find_object_with_camera_id(self): + alt_button = self.alt_driver.find_object(AltBy.path("//Button")) + alt_button.tap() + alt_button.tap() + + camera = self.alt_driver.find_object(AltBy.path("//Camera")) + alt_object = self.alt_driver.find_object( + AltBy.component("CapsuleCollider"), AltBy.id(str(camera.id))) + assert alt_object.name == "Capsule" + + camera2 = self.alt_driver.find_object(AltBy.path("//Main Camera")) + alt_object2 = self.alt_driver.find_object( + AltBy.component("CapsuleCollider"), AltBy.id(str(camera2.id))) + + assert alt_object.x != alt_object2.x + assert alt_object.y != alt_object2.y + + def test_wait_for_object_with_camera_id(self): + alt_button = self.alt_driver.find_object(AltBy.path("//Button")) + alt_button.tap() + alt_button.tap() + + camera = self.alt_driver.find_object(AltBy.path("//Camera")) + alt_object = self.alt_driver.wait_for_object( + AltBy.component("CapsuleCollider"), AltBy.id(str(camera.id))) + assert alt_object.name == "Capsule" + + camera2 = self.alt_driver.find_object(AltBy.path("//Main Camera")) + alt_object2 = self.alt_driver.wait_for_object( + AltBy.component("CapsuleCollider"), AltBy.id(str(camera2.id))) + + assert alt_object.x != alt_object2.x + assert alt_object.y != alt_object2.y + + def test_find_objects_with_camera_id(self): + alt_button = self.alt_driver.find_object(AltBy.path("//Button")) + alt_button.tap() + alt_button.tap() + + camera = self.alt_driver.find_object(AltBy.path("//Camera")) + alt_objects = self.alt_driver.find_objects( + AltBy.name("Plane"), AltBy.id(str(camera.id))) + assert alt_objects[0].name == "Plane" + + camera2 = self.alt_driver.find_object(AltBy.path("//Main Camera")) + alt_objects2 = self.alt_driver.find_objects( + AltBy.name("Plane"), AltBy.id(str(camera2.id))) + + assert alt_objects[0].x != alt_objects2[0].x + assert alt_objects[0].y != alt_objects2[0].y + + def test_wait_for_object_not_be_present_with_camera_id(self): + camera = self.alt_driver.find_object(AltBy.path("//Main Camera")) + self.alt_driver.wait_for_object_to_not_be_present( + AltBy.name("ObjectDestroyedIn5Secs"), + AltBy.id(str(camera.id)) + ) + + elements = self.alt_driver.get_all_elements() + names = [element.name for element in elements] + assert "ObjectDestroyedIn5Secs" not in names + + def test_wait_for_object_with_text_with_camera_id(self): + name = "CapsuleInfo" + text = self.alt_driver.find_object(AltBy.name(name)).get_text() + camera = self.alt_driver.find_object(AltBy.path("//Main Camera")) + + alt_object = self.alt_driver.wait_for_object( + AltBy.path("//{}[@text={}]".format(name, text)), + AltBy.id(str(camera.id)), + timeout=1 + ) + + assert alt_object is not None + assert alt_object.get_text() == text + + def test_wait_for_object_which_contains_with_camera_id(self): + camera = self.alt_driver.find_object(AltBy.path("//Main Camera")) + alt_object = self.alt_driver.wait_for_object_which_contains( + AltBy.name("Canva"), + AltBy.id(str(camera.id)) + ) + assert alt_object.name == "Canvas" + + def test_find_object_with_tag(self): + alt_button = self.alt_driver.find_object(AltBy.path("//Button")) + alt_button.tap() + alt_button.tap() + + alt_object = self.alt_driver.find_object( + AltBy.component("CapsuleCollider"), AltBy.tag("MainCamera")) + assert alt_object.name == "Capsule" + + alt_object2 = self.alt_driver.find_object( + AltBy.component("CapsuleCollider"), AltBy.tag("Untagged")) + assert alt_object.x != alt_object2.x + assert alt_object.y != alt_object2.y + + def test_wait_for_object_with_tag(self): + alt_button = self.alt_driver.find_object(AltBy.path("//Button")) + alt_button.tap() + alt_button.tap() + + alt_object = self.alt_driver.wait_for_object( + AltBy.component("CapsuleCollider"), AltBy.tag("MainCamera")) + assert alt_object.name == "Capsule" + + alt_object2 = self.alt_driver.wait_for_object( + AltBy.component("CapsuleCollider"), AltBy.tag("Untagged")) + assert alt_object.x != alt_object2.x + assert alt_object.y != alt_object2.y + + def test_find_objects_with_tag(self): + alt_button = self.alt_driver.find_object(AltBy.path("//Button")) + alt_button.tap() + alt_button.tap() + + alt_object = self.alt_driver.find_objects( + AltBy.name("Plane"), AltBy.tag("MainCamera")) + assert alt_object[0].name == "Plane" + + alt_object2 = self.alt_driver.find_objects( + AltBy.name("Plane"), AltBy.tag("Untagged")) + assert alt_object[0].x != alt_object2[0].x + assert alt_object[0].y != alt_object2[0].y + + def test_wait_for_object_not_be_present_with_tag(self): + self.alt_driver.wait_for_object_to_not_be_present( + AltBy.name("ObjectDestroyedIn5Secs"), AltBy.tag("MainCamera")) + + elements = self.alt_driver.get_all_elements() + names = [element.name for element in elements] + assert "ObjectDestroyedIn5Secs" not in names + + def test_wait_for_object_with_text_with_tag(self): + name = "CapsuleInfo" + text = self.alt_driver.find_object(AltBy.name(name)).get_text() + + alt_object = self.alt_driver.wait_for_object( + AltBy.path("//{}[@text={}]".format(name, text)), + AltBy.tag("MainCamera"), + timeout=1 + ) + assert alt_object is not None + assert alt_object.get_text() == text + + def test_find_object_by_camera(self): + button = self.alt_driver.find_object(AltBy.path("//Button")) + button.tap() + button.tap() + + alt_object = self.alt_driver.find_object( + AltBy.component("CapsuleCollider"), + AltBy.name("Camera") + ) + + assert alt_object.name == "Capsule" + alt_object2 = self.alt_driver.find_object( + AltBy.component("CapsuleCollider"), + AltBy.name("Main Camera") + ) + + assert alt_object.x != alt_object2.x + assert alt_object.y != alt_object2.y + + def test_wait_for_object_by_camera(self): + button = self.alt_driver.find_object(AltBy.path("//Button")) + button.tap() + button.tap() + + alt_object = self.alt_driver.wait_for_object( + AltBy.component("CapsuleCollider"), + AltBy.name("Camera") + ) + assert alt_object.name == "Capsule" + + alt_object2 = self.alt_driver.wait_for_object( + AltBy.component("CapsuleCollider"), + AltBy.name("Main Camera") + ) + assert alt_object.x != alt_object2.x + assert alt_object.y != alt_object2.y + + def test_find_objects_by_camera(self): + button = self.alt_driver.find_object(AltBy.path("//Button")) + button.tap() + button.tap() + + alt_object = self.alt_driver.find_objects( + AltBy.name("Plane"), AltBy.name("Camera")) + assert alt_object[0].name == "Plane" + + alt_object2 = self.alt_driver.find_objects( + AltBy.name("Plane"), AltBy.name("Main Camera")) + assert alt_object[0].x != alt_object2[0].x + assert alt_object[0].y != alt_object2[0].y + + def test_wait_for_object_not_be_present_by_camera(self): + self.alt_driver.wait_for_object_to_not_be_present( + AltBy.name("ObjectDestroyedIn5Secs"), + AltBy.name("Main Camera") + ) + + elements = self.alt_driver.get_all_elements() + names = [element.name for element in elements] + assert "ObjectDestroyedIn5Secs" not in names + + def test_wait_for_object_by_camera_2(self): + name = "CapsuleInfo" + text = self.alt_driver.find_object(AltBy.name(name)).get_text() + + alt_object = self.alt_driver.wait_for_object( + AltBy.path("//{}[@text={}]".format(name, text)), + AltBy.name("Main Camera"), + timeout=1 + ) + + assert alt_object is not None + assert alt_object.get_text() == text + + def test_wait_for_object_which_contains_by_camera(self): + alt_object = self.alt_driver.wait_for_object_which_contains( + AltBy.name("Canva"), AltBy.name("Main Camera")) + assert alt_object.name == "Canvas" + + def test_get_component_property_complex_class(self): + component_name = "AltExampleScriptCapsule" + property_name = "AltSampleClass.testInt" + + alt_object = self.alt_driver.find_object(AltBy.name("Capsule")) + assert alt_object is not None + + property_value = alt_object.get_component_property( + component_name, property_name, "Assembly-CSharp", + max_depth=1 + ) + assert property_value == 1 + + def test_get_component_property_complex_class2(self): + component_name = "AltExampleScriptCapsule" + property_name = "listOfSampleClass[1].testString" + alt_object = self.alt_driver.find_object(AltBy.name("Capsule")) + assert alt_object is not None + + property_value = alt_object.get_component_property( + component_name, property_name, "Assembly-CSharp", max_depth=1) + assert property_value == "test2" + + def test_set_component_property_complex_class(self): + component_name = "AltExampleScriptCapsule" + property_name = "AltSampleClass.testInt" + + alt_object = self.alt_driver.find_object(AltBy.name("Capsule")) + assert alt_object is not None + + alt_object.set_component_property( + component_name, property_name, "Assembly-CSharp", 2) + property_value = alt_object.get_component_property( + component_name, property_name, "Assembly-CSharp", max_depth=1) + assert property_value == 2 + + def test_set_component_property_complex_class2(self): + component_name = "AltExampleScriptCapsule" + property_name = "listOfSampleClass[1].testString" + alt_object = self.alt_driver.find_object(AltBy.name("Capsule")) + assert alt_object is not None + + alt_object.set_component_property( + component_name, property_name, "Assembly-CSharp", "test3") + propertyValue = alt_object.get_component_property( + component_name, property_name, "Assembly-CSharp", max_depth=1) + assert propertyValue == "test3" + + def test_get_parent(self): + element = self.alt_driver.find_object(AltBy.name("Canvas/CapsuleInfo")) + element_parent = element.get_parent() + assert element_parent.name == "Canvas" + + def test_tap_coordinates(self): + capsule_element = self.alt_driver.find_object(AltBy.name("Capsule")) + self.alt_driver.tap(capsule_element.get_screen_position()) + self.alt_driver.wait_for_object( + AltBy.path("//CapsuleInfo[@text=Capsule was clicked to jump!]"), timeout=1) + + def test_click_coordinates(self): + capsule_element = self.alt_driver.find_object(AltBy.name("Capsule")) + self.alt_driver.click(capsule_element.get_screen_position()) + self.alt_driver.wait_for_object( + AltBy.path("//CapsuleInfo[@text=Capsule was clicked to jump!]"), timeout=1) + + def test_tap_element(self): + capsule_element = self.alt_driver.find_object(AltBy.name("Capsule")) + capsule_element.tap(1) + self.alt_driver.wait_for_object( + AltBy.path("//CapsuleInfo[@text=Capsule was clicked to jump!]"), timeout=1) + + def test_click_element(self): + capsule_element = self.alt_driver.find_object(AltBy.name("Capsule")) + capsule_element.click() + self.alt_driver.wait_for_object( + AltBy.path("//CapsuleInfo[@text=Capsule was clicked to jump!]"), timeout=1) + + def test_key_down_and_key_up_mouse0(self): + button = self.alt_driver.find_object(AltBy.name("UIButton")) + self.alt_driver.move_mouse( + button.get_screen_position(), duration=0.1, wait=True) + + self.alt_driver.key_down(AltKeyCode.Mouse0) + self.alt_driver.key_up(AltKeyCode.Mouse0) + text = self.alt_driver.find_object( + AltBy.name("ChineseLetters")).get_text() + assert text != "????" + + def test_camera_not_found_exception(self): + with pytest.raises(exceptions.CameraNotFoundException): + self.alt_driver.find_object(AltBy.name("Capsule"), AltBy.name("Camera")) + + def test_input_field_events(self): + input_field = self.alt_driver.find_object( + AltBy.name("UnityUIInputField")).set_text("example", submit=True) + + assert input_field.get_text() == "example" + assert input_field.get_component_property( + "AltInputFieldRaisedEvents", "onValueChangedInvoked", "Assembly-CSharp") + assert input_field.get_component_property( + "AltInputFieldRaisedEvents", "onSubmitInvoked", "Assembly-CSharp") + + @pytest.mark.WebGLUnsupported + @pytest.mark.iOSUnsupported + def test_get_static_property(self): + + self.alt_driver.call_static_method( + "UnityEngine.Screen", "SetResolution", "UnityEngine.CoreModule", + parameters=["1920", "1080", "True"], + type_of_parameters=["System.Int32", + "System.Int32", "System.Boolean"] + ) + width = self.alt_driver.get_static_property( + "UnityEngine.Screen", "currentResolution.width", + "UnityEngine.CoreModule" + ) + + assert int(width) == 1920 + + def test_set_static_property(self): + expectedValue = 5 + self.alt_driver.set_static_property( + "AltExampleScriptCapsule", "privateStaticVariable", "Assembly-CSharp", expectedValue) + value = self.alt_driver.get_static_property( + "AltExampleScriptCapsule", "privateStaticVariable", "Assembly-CSharp") + assert expectedValue == value + + def test_set_static_property2(self): + newValue = 5 + expectedArray = [1, 5, 3] + self.alt_driver.set_static_property( + "AltExampleScriptCapsule", "staticArrayOfInts[1]", "Assembly-CSharp", newValue) + value = self.alt_driver.get_static_property( + "AltExampleScriptCapsule", "staticArrayOfInts", "Assembly-CSharp") + assert expectedArray == value + + def test_get_static_property_instance_null(self): + + screen_width = self.alt_driver.call_static_method( + "UnityEngine.Screen", "get_width", + "UnityEngine.CoreModule" + ) + width = self.alt_driver.get_static_property( + "UnityEngine.Screen", "width", + "UnityEngine.CoreModule" + ) + + assert int(width) == screen_width + + def test_float_world_coordinates(self): + plane = self.alt_driver.find_object(AltBy.name("Plane")) + + assert type(plane.worldX) is float + assert type(plane.worldY) is float + assert type(plane.worldZ) is float + + def test_set_command_response_timeout(self): + self.alt_driver.set_command_response_timeout(1) + with pytest.raises(exceptions.CommandResponseTimeoutException) as execinfo: + self.alt_driver.tilt([1, 1, 1], duration=2, wait=True) + self.alt_driver.set_command_response_timeout(60) + assert str(execinfo.value) == "" + + def test_keys_down(self): + keys = [AltKeyCode.K, AltKeyCode.L] + self.alt_driver.keys_down(keys) + self.alt_driver.keys_up(keys) + + alt_object = self.alt_driver.find_object(AltBy.name("Capsule")) + property_value = alt_object.get_component_property( + "AltExampleScriptCapsule", + "stringToSetFromTests", + "Assembly-CSharp" + ) + assert property_value == "multiple keys pressed" + + def test_press_keys(self): + keys = [AltKeyCode.K, AltKeyCode.L] + self.alt_driver.press_keys(keys) + + alt_object = self.alt_driver.find_object(AltBy.name("Capsule")) + property_value = alt_object.get_component_property( + "AltExampleScriptCapsule", + "stringToSetFromTests", + "Assembly-CSharp" + ) + assert property_value == "multiple keys pressed" + + def test_find_object_by_coordinates(self): + counter_button = self.alt_driver.find_object(AltBy.name("ButtonCounter")) + + element = self.alt_driver.find_object_at_coordinates( + [80 + counter_button.x, 15 + counter_button.y]) + assert element.name == "ButtonCounter" + + def test_find_object_by_coordinates_no_element(self): + element = self.alt_driver.find_object_at_coordinates([-1, -1]) + assert element is None + + def test_call_private_method(self): + capsule_element = self.alt_driver.find_object(AltBy.name("Capsule")) + capsule_element.call_component_method("AltExampleScriptCapsule", + "callJump", "Assembly-CSharp", parameters=[]) + capsule_info = self.alt_driver.find_object(AltBy.name("CapsuleInfo")) + assert capsule_info.get_text() == "Capsule jumps!" + + def test_reset_input(self): + self.alt_driver.key_down(AltKeyCode.P, 1) + assert self.alt_driver.find_object(AltBy.name("AltTesterPrefab")).get_component_property( + "AltTester.AltTesterUnitySDK.InputModule.NewInputSystem", + "Keyboard.pKey.isPressed", "AltTester.AltTesterUnitySDK.InputModule") is True + self.alt_driver.reset_input() + assert self.alt_driver.find_object(AltBy.name("AltTesterPrefab")).get_component_property( + "AltTester.AltTesterUnitySDK.InputModule.NewInputSystem", + "Keyboard.pKey.isPressed", "AltTester.AltTesterUnitySDK.InputModule") is False + + countKeyDown = self.alt_driver.find_object(AltBy.name("AltTesterPrefab")).get_component_property( + "Input", "_keyCodesPressed.Count", "AltTester.AltTesterUnitySDK.InputModule") + assert 0 == countKeyDown diff --git a/Bindings~/python/tests/integration/test_scene02_with_unity_driver.py b/Bindings~/python/tests/integration/test_scene02_with_unity_driver.py new file mode 100644 index 000000000..9cfd59d90 --- /dev/null +++ b/Bindings~/python/tests/integration/test_scene02_with_unity_driver.py @@ -0,0 +1,158 @@ +""" + Copyright(C) 2025 Altom Consulting + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +""" + +import pytest + +from alttester.altdriver_unity import AltDriverUnity + +from .utils import Scenes +from alttester.altby import AltBy + + +@pytest.mark.usefixtures("alt_driver_unity") +class TestScene02: + alt_driver: AltDriverUnity + + @pytest.fixture(autouse=True) + def setup(self): + self.alt_driver.reset_input() + self.alt_driver.load_scene(Scenes.Scene02) + + def test_get_all_elements(self): + elements = self.alt_driver.get_all_elements(enabled=False) + assert elements is not None + + expected_names = { + "EventSystem", "Canvas", "Panel Drag Area", "Panel", + "Header", "Text", "Drag Zone", "Resize Zone", "Close Button", + "Debugging", "SF Scene Elements", "Main Camera", "Background", + "Particle System" + } + + input_marks = [] + names = [] + + for element in elements: + if element.name == "InputMark(Clone)": + input_marks.append(element.transformId) + continue # skip InputMark and direct children + if element.transformParentId in input_marks: + continue # skip InputMark and direct children + + names.append(element.name) + + for name in expected_names: + assert name in names + + def test_get_all_enabled_elements(self): + # time.sleep(1) + + elements = self.alt_driver.get_all_elements(enabled=True) + assert elements is not None + + expected_names = { + "EventSystem", "Canvas", "Panel Drag Area", "Panel", + "Header", "Text", "Drag Zone", "Resize Zone", "Close Button", + "Debugging", "SF Scene Elements", "Main Camera", "Background", + "Particle System" + } + names = [element.name for element in elements] + assert len(names) >= 22 + for name in expected_names: + assert name in names + + def test_resize_panel(self): + alt_object = self.alt_driver.find_object(AltBy.name("Resize Zone")) + position_init = (alt_object.x, alt_object.y) + + self.alt_driver.swipe( + alt_object.get_screen_position(), + (alt_object.x - 200, alt_object.y - 200), + duration=2 + ) + # time.sleep(2) + + alt_object = self.alt_driver.find_object(AltBy.name("Resize Zone")) + position_final = (alt_object.x, alt_object.y) + + assert position_init != position_final + + def test_resize_panel_with_multipoint_swipe(self): + alt_object = self.alt_driver.find_object(AltBy.name("Resize Zone")) + position_init = (alt_object.x, alt_object.y) + + positions = [ + alt_object.get_screen_position(), + [alt_object.x - 200, alt_object.y - 200], + [alt_object.x - 300, alt_object.y - 100], + [alt_object.x - 50, alt_object.y - 100], + [alt_object.x - 100, alt_object.y - 100] + ] + self.alt_driver.multipoint_swipe(positions, duration=4) + + alt_object = self.alt_driver.find_object(AltBy.name("Resize Zone")) + position_final = (alt_object.x, alt_object.y) + + assert position_init != position_final + + def test_pointer_down_from_object(self): + panel = self.alt_driver.find_object(AltBy.name("Panel")) + color1 = panel.get_component_property( + "AltExampleScriptPanel", + "normalColor", + "Assembly-CSharp" + ) + panel.pointer_down() + + color2 = panel.get_component_property( + "AltExampleScriptPanel", + "highlightColor", + "Assembly-CSharp" + ) + + assert color1 != color2 + + def test_pointer_up_from_object(self): + panel = self.alt_driver.find_object(AltBy.name("Panel")) + color1 = panel.get_component_property( + "AltExampleScriptPanel", + "normalColor", + "Assembly-CSharp" + ) + panel.pointer_down() + + panel.pointer_up() + color2 = panel.get_component_property( + "AltExampleScriptPanel", + "highlightColor", + "Assembly-CSharp" + ) + + assert color1 == color2 + + def test_new_touch_commands(self): + draggable_area = self.alt_driver.find_object(AltBy.name("Drag Zone")) + initial_position = draggable_area.get_screen_position() + + finger_id = self.alt_driver.begin_touch( + draggable_area.get_screen_position()) + self.alt_driver.move_touch( + finger_id, [draggable_area.x + 10, draggable_area.y + 10]) + self.alt_driver.end_touch(finger_id) + + draggable_area = self.alt_driver.find_object(AltBy.name("Drag Zone")) + assert initial_position != draggable_area diff --git a/Bindings~/python/tests/integration/test_scene03_with_unity_driver.py b/Bindings~/python/tests/integration/test_scene03_with_unity_driver.py new file mode 100644 index 000000000..51729fd72 --- /dev/null +++ b/Bindings~/python/tests/integration/test_scene03_with_unity_driver.py @@ -0,0 +1,164 @@ +""" + Copyright(C) 2025 Altom Consulting + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +""" + + +import pytest + +from .utils import Scenes +from alttester.altby import AltBy + + +@pytest.mark.usefixtures("alt_driver_unity") +class TestScene03: + + @pytest.fixture(autouse=True) + def setup(self): + self.alt_driver.reset_input() + self.alt_driver.load_scene(Scenes.Scene03) + + def wait_for_swipe_to_finish(self): + self.alt_driver.wait_for_object_to_not_be_present(AltBy.name("icon")) + + def get_sprite_name(self, source_image_name, image_source_drop_zone_name): + image_source = self.alt_driver.find_object( + AltBy.name(source_image_name)).get_component_property( + "UnityEngine.UI.Image", "sprite.name", "UnityEngine.UI") + image_source_drop_zone = self.alt_driver.find_object( + AltBy.name(image_source_drop_zone_name)).get_component_property( + "UnityEngine.UI.Image", "sprite.name", "UnityEngine.UI") + return image_source, image_source_drop_zone + + def drop_image_with_multipoint_swipe(self, object_names, duration, wait): + positions = [] + for name in object_names: + alt_object = self.alt_driver.find_object(AltBy.name(name)) + positions.append(alt_object.get_screen_position()) + + self.alt_driver.multipoint_swipe(positions, duration, wait) + + def drop_image(self, drag_location_name, drop_location_name, duration, wait): + drag_location = self.alt_driver.find_object(AltBy.name(drag_location_name)) + drop_location = self.alt_driver.find_object(AltBy.name(drop_location_name)) + + self.alt_driver.swipe(drag_location.get_screen_position( + ), drop_location.get_screen_position(), duration, wait) + + def test_pointer_enter_and_exit(self): + alt_object = self.alt_driver.find_object(AltBy.name("Drop Image")) + color1 = alt_object.get_component_property( + "AltExampleScriptDropMe", + "highlightColor", + "Assembly-CSharp" + ) + alt_object.pointer_enter() + color2 = alt_object.get_component_property( + "AltExampleScriptDropMe", + "highlightColor", + "Assembly-CSharp" + ) + + assert color1["r"] != color2["r"] or \ + color1["g"] != color2["g"] or \ + color1["b"] != color2["b"] or \ + color1["a"] != color2["a"] + + alt_object.pointer_exit() + color3 = alt_object.get_component_property( + "AltExampleScriptDropMe", + "highlightColor", + "Assembly-CSharp" + ) + + assert color3["r"] != color2["r"] or \ + color3["g"] != color2["g"] or \ + color3["b"] != color2["b"] or \ + color3["a"] != color2["a"] + + assert color3["r"] == color1["r"] and \ + color3["g"] == color1["g"] and \ + color3["b"] == color1["b"] and \ + color3["a"] == color1["a"] + + def test_multiple_swipes(self): + self.drop_image("Drag Image2", "Drop Box2", 1, False) + self.drop_image("Drag Image2", "Drop Box1", 1, False) + self.drop_image("Drag Image1", "Drop Box1", 2, False) + self.wait_for_swipe_to_finish() + image_source, image_source_drop_zone = self.get_sprite_name( + "Drag Image1", "Drop Image") + assert image_source == image_source_drop_zone + + image_source, image_source_drop_zone = self.get_sprite_name( + "Drag Image2", "Drop") + assert image_source == image_source_drop_zone + + def test_multiple_swipe_and_waits(self): + self.drop_image("Drag Image2", "Drop Box2", 1, True) + self.drop_image("Drag Image2", "Drop Box1", 1, True) + self.drop_image("Drag Image1", "Drop Box1", 1, True) + image_source, image_source_drop_zone = self.get_sprite_name( + "Drag Image1", "Drop Image") + assert image_source == image_source_drop_zone + + image_source, image_source_drop_zone = self.get_sprite_name( + "Drag Image2", "Drop") + assert image_source == image_source_drop_zone + + def test_multiple_swipe_with_multipoint_swipe(self): + + self.drop_image_with_multipoint_swipe( + ["Drag Image1", "Drop Box1"], 1, False) + self.drop_image_with_multipoint_swipe( + ["Drag Image2", "Drop Box1", "Drop Box2"], 1, False) + + image_source, image_source_drop_zone = self.get_sprite_name( + "Drag Image1", "Drop Image") + assert image_source == image_source_drop_zone + + image_source, image_source_drop_zone = self.get_sprite_name( + "Drag Image2", "Drop") + assert image_source == image_source_drop_zone + + def test_multiple_swipe_and_waits_with_multipoint_swipe(self): + + self.drop_image_with_multipoint_swipe( + ["Drag Image1", "Drop Box1"], 1, True) + self.drop_image_with_multipoint_swipe( + ["Drag Image2", "Drop Box1", "Drop Box2"], 1, True) + + image_source, image_source_drop_zone = self.get_sprite_name( + "Drag Image1", "Drop Image") + assert image_source == image_source_drop_zone + + image_source, image_source_drop_zone = self.get_sprite_name( + "Drag Image2", "Drop") + assert image_source == image_source_drop_zone + + def test_begin_move_end_touch(self): + alt_object1 = self.alt_driver.find_object(AltBy.name("Drag Image1")) + alt_object2 = self.alt_driver.find_object(AltBy.name("Drop Box1")) + + id = self.alt_driver.begin_touch(alt_object1.get_screen_position()) + self.alt_driver.move_touch(id, alt_object2.get_screen_position()) + self.alt_driver.end_touch(id) + + imageSource = alt_object1.get_component_property( + "UnityEngine.UI.Image", "sprite.name", "UnityEngine.UI") + imageSourceDropZone = self.alt_driver.find_object(AltBy.name("Drop Image")).get_component_property( + "UnityEngine.UI.Image", "sprite.name", "UnityEngine.UI") + + assert imageSource == imageSourceDropZone diff --git a/Bindings~/python/tests/integration/test_scene05_with_unity_driver.py b/Bindings~/python/tests/integration/test_scene05_with_unity_driver.py new file mode 100644 index 000000000..4b3a3e4db --- /dev/null +++ b/Bindings~/python/tests/integration/test_scene05_with_unity_driver.py @@ -0,0 +1,149 @@ +""" + Copyright(C) 2025 Altom Consulting + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +""" + +import time + +import pytest + +from .utils import Scenes +from alttester import AltKeyCode +from alttester.altby import AltBy +from alttester.altdriver_unity import AltDriverUnity + + +@pytest.mark.usefixtures("alt_driver_unity") +class TestScene05: + alt_driver: AltDriverUnity + + @pytest.fixture(autouse=True) + def setup(self): + self.alt_driver.reset_input() + self.alt_driver.load_scene(Scenes.Scene05) + + def test_movement_cube(self): + cube = self.alt_driver.find_object(AltBy.name("Player1")) + initial_position = (cube.worldX, cube.worldY, cube.worldZ) + + self.alt_driver.scroll(speed_vertical=30, duration=0.1, wait=False) + self.alt_driver.press_key( + AltKeyCode.K, power=1, duration=0.1, wait=False) + + self.alt_driver.press_key(AltKeyCode.O, power=1, duration=0.1) + cube = self.alt_driver.find_object(AltBy.name("Player1")) + final_position = (cube.worldX, cube.worldY, cube.worldZ) + + assert initial_position != final_position + + def test_camera_movement(self): + cube = self.alt_driver.find_object(AltBy.name("Player1")) + initial_position = (cube.worldX, cube.worldY, cube.worldY) + + self.alt_driver.press_key( + AltKeyCode.W, power=1, duration=0.1, wait=False) + # time.sleep(2) + + cube = self.alt_driver.find_object(AltBy.name("Player1")) + final_position = (cube.worldX, cube.worldY, cube.worldY) + + assert initial_position != final_position + + def test_update_altObject(self): + + cube = self.alt_driver.find_object(AltBy.name("Player1")) + initial_position_z = cube.worldZ + + self.alt_driver.press_key( + AltKeyCode.W, power=1, duration=0.1, wait=False) + time.sleep(5) + + assert initial_position_z != cube.update_object().worldZ + + def test_creating_stars(self): + stars = self.alt_driver.find_objects_which_contain( + AltBy.name("Star"), AltBy.name("Player2")) + assert len(stars) == 1 + + self.alt_driver.find_objects_which_contain( + AltBy.name("Player"), AltBy.name("Player2")) + pressing_point_1 = self.alt_driver.find_object( + AltBy.name("PressingPoint1"), AltBy.name("Player2")) + + self.alt_driver.move_mouse( + pressing_point_1.get_screen_position(), duration=0.1, wait=False) + time.sleep(0.1) + + self.alt_driver.press_key( + AltKeyCode.Mouse0, power=1, duration=0.1, wait=False) + pressing_point_2 = self.alt_driver.find_object( + AltBy.name("PressingPoint2"), AltBy.name("Player2")) + self.alt_driver.move_mouse( + pressing_point_2.get_screen_position(), duration=0.1) + self.alt_driver.press_key( + AltKeyCode.Mouse0, power=1, duration=0.1, wait=False) + time.sleep(0.1) + + stars = self.alt_driver.find_objects_which_contain(AltBy.name("Star")) + assert len(stars) == 3 + + def test_power_joystick(self): + button_names = ["Horizontal", "Vertical"] + keys_to_press = [AltKeyCode.D, AltKeyCode.W] + + axis_name = self.alt_driver.find_object(AltBy.name("AxisName")) + axis_value = self.alt_driver.find_object(AltBy.name("AxisValue")) + + for button_name, key in zip(button_names, keys_to_press): + self.alt_driver.press_key(key, power=0.5, duration=1) + + assert axis_value.get_text() == "0.5" + assert axis_name.get_text() == button_name + + def test_scroll(self): + player2 = self.alt_driver.find_object(AltBy.name("Player2")) + cube_initial_position = [player2.worldX, + player2.worldY, player2.worldY] + self.alt_driver.scroll(4, duration=1, wait=False) + time.sleep(1) + + player2 = self.alt_driver.find_object(AltBy.name("Player2")) + cube_final_position = [player2.worldX, player2.worldY, player2.worldY] + assert cube_initial_position != cube_final_position + + def test_scroll_and_wait(self): + player2 = self.alt_driver.find_object(AltBy.name("Player2")) + cube_initial_position = [player2.worldX, + player2.worldY, player2.worldY] + self.alt_driver.scroll(4, duration=0.3) + + player2 = self.alt_driver.find_object(AltBy.name("Player2")) + cube_final_position = [player2.worldX, player2.worldY, player2.worldY] + assert cube_initial_position != cube_final_position + + def test_key_down_and_key_up(self): + self.alt_driver.key_down(AltKeyCode.A) + + last_key_down = self.alt_driver.find_object(AltBy.name("LastKeyDownValue")) + last_key_press = self.alt_driver.find_object( + AltBy.name("LastKeyPressedValue")) + + assert last_key_down.get_text() == "97" + assert last_key_press.get_text() == "97" + + self.alt_driver.key_up(AltKeyCode.A) + last_key_up = self.alt_driver.find_object(AltBy.name("LastKeyUpValue")) + + assert last_key_up.get_text() == "97" diff --git a/Bindings~/python/tests/integration/test_scene07_with_unity_driver.py b/Bindings~/python/tests/integration/test_scene07_with_unity_driver.py new file mode 100644 index 000000000..0ae93f28d --- /dev/null +++ b/Bindings~/python/tests/integration/test_scene07_with_unity_driver.py @@ -0,0 +1,129 @@ +""" + Copyright(C) 2025 Altom Consulting + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +""" + +import pytest +from alttester.altby import AltBy +from alttester.altdriver_unity import AltDriverUnity +from .utils import Scenes + + +@pytest.mark.usefixtures("alt_driver_unity") +class TestScene07A: + alt_driver: AltDriverUnity + + @pytest.fixture(autouse=True) + def setup(self): + self.alt_driver.reset_input() + self.alt_driver.load_scene(Scenes.Scene07A) + + def test_tap_element_NIS(self): + capsule = self.alt_driver.find_object(AltBy.name("Capsule")) + capsule.tap() + + component_name = "AltExampleNewInputSystem" + property_name = "jumpCounter" + + property_value = capsule.get_component_property( + component_name, property_name, "Assembly-CSharp", + max_depth=1 + ) + + assert property_value == 1 + + def test_tap_coordinates_NIS(self): + capsule = self.alt_driver.find_object(AltBy.name("Capsule")) + self.alt_driver.tap(capsule.get_screen_position()) + + action_info = self.alt_driver.wait_for_object( + AltBy.path("//ActionText[@text=Capsule was tapped!]"), timeout=1) + + assert action_info.get_text() == "Capsule was tapped!" + + def test_click_element_NIS(self): + capsule = self.alt_driver.find_object(AltBy.name("Capsule")) + capsule.click() + + component_name = "AltExampleNewInputSystem" + property_name = "jumpCounter" + property_value = capsule.get_component_property( + component_name, property_name, "Assembly-CSharp", + max_depth=1 + ) + + assert property_value == 1 + + def test_click_coordinates_NIS(self): + capsule = self.alt_driver.find_object(AltBy.name("Capsule")) + self.alt_driver.click(capsule.get_screen_position()) + action_info = self.alt_driver.wait_for_object( + AltBy.path("//ActionText[@text=Capsule was clicked!]"), + timeout=1 + ) + + assert action_info.get_text() == "Capsule was clicked!" + + def test_tilt(self): + cube = self.alt_driver.find_object(AltBy.name("Cube (1)")) + initial_position = cube.get_world_position() + self.alt_driver.tilt([1000, 10, 10], duration=1) + assert initial_position != self.alt_driver.find_object( + AltBy.name("Cube (1)")).get_world_position() + + is_moved = cube.get_component_property( + "AltCubeNIS", "isMoved", "Assembly-CSharp") + assert is_moved + + +@pytest.mark.usefixtures("alt_driver_unity") +class TestScene07B: + alt_driver: AltDriverUnity + + @pytest.fixture(autouse=True) + def setup(self): + self.alt_driver.reset_input() + self.alt_driver.load_scene(Scenes.Scene07B) + + def get_sprite_name(self, source_image_name, image_source_drop_zone_name): + image_source = self.alt_driver.find_object(AltBy.name(source_image_name)).get_component_property( + "UnityEngine.UI.Image", "sprite.name", assembly="UnityEngine.UI") + image_source_drop_zone = self.alt_driver.find_object( + AltBy.name(image_source_drop_zone_name)).get_component_property( + "UnityEngine.UI.Image", "sprite.name", assembly="UnityEngine.UI") + return image_source, image_source_drop_zone + + def drop_image_with_multipoint_swipe(self, object_names, duration, wait): + positions = [] + for name in object_names: + alt_object = self.alt_driver.find_object(AltBy.name(name)) + positions.append(alt_object.get_screen_position()) + + self.alt_driver.multipoint_swipe( + positions, duration=duration, wait=wait) + + def test_multipoint_swipe_NIS(self): + self.drop_image_with_multipoint_swipe( + ["Drag Image1", "Drop Box1"], 1, False) + self.drop_image_with_multipoint_swipe( + ["Drag Image2", "Drop Box1", "Drop Box2"], 1, False) + + image_source, image_source_drop_zone = self.get_sprite_name( + "Drag Image1", "Drop Image") + assert image_source == image_source_drop_zone + + image_source, image_source_drop_zone = self.get_sprite_name( + "Drag Image2", "Drop") + assert image_source == image_source_drop_zone diff --git a/Bindings~/python/tests/integration/test_scene09_with_unity_driver.py b/Bindings~/python/tests/integration/test_scene09_with_unity_driver.py new file mode 100644 index 000000000..fd3175080 --- /dev/null +++ b/Bindings~/python/tests/integration/test_scene09_with_unity_driver.py @@ -0,0 +1,62 @@ +""" + Copyright(C) 2025 Altom Consulting + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +""" + +import pytest + +from .utils import Scenes +from alttester.altby import AltBy +from alttester.altdriver_unity import AltDriverUnity + +@pytest.mark.usefixtures("alt_driver_unity") +class TestScene09: + alt_driver: AltDriverUnity + + @pytest.fixture(autouse=True) + def setup(self): + self.alt_driver.reset_input() + self.alt_driver.load_scene(Scenes.Scene09) + + def test_scroll_element_NIS(self): + scrollbar_initial = self.alt_driver.find_object( + AltBy.name("Scrollbar Vertical")) + scrollbar_initial_value = scrollbar_initial.get_component_property( + "UnityEngine.UI.Scrollbar", "value", "UnityEngine.UI") + self.alt_driver.move_mouse(self.alt_driver.find_object( + AltBy.name("Scroll View")).get_screen_position(), duration=0.3, wait=True) + self.alt_driver.scroll(-3000, duration=0.5, wait=True) + + scrollbar_final = self.alt_driver.find_object( + AltBy.name("Scrollbar Vertical")) + scrollbar_final_value = scrollbar_final.get_component_property( + "UnityEngine.UI.Scrollbar", "value", "UnityEngine.UI") + + assert scrollbar_initial_value != scrollbar_final_value + + def test_swipe_NIS(self): + scrollbar = self.alt_driver.find_object(AltBy.name("Handle")) + scrollbar_position = scrollbar.get_screen_position() + button = self.alt_driver.find_object( + AltBy.path("//Scroll View/Viewport/Content/Button (4)")) + self.alt_driver.swipe( + button.get_screen_position(), + (button.x, button.y + 20), + duration=0.5 + ) + + scrollbar_final = self.alt_driver.find_object(AltBy.name("Handle")) + scrollbar_final_position = scrollbar_final.get_screen_position() + assert scrollbar_position != scrollbar_final_position diff --git a/Bindings~/python/tests/integration/test_scene10_with_unity_driver.py b/Bindings~/python/tests/integration/test_scene10_with_unity_driver.py new file mode 100644 index 000000000..b86844946 --- /dev/null +++ b/Bindings~/python/tests/integration/test_scene10_with_unity_driver.py @@ -0,0 +1,125 @@ +""" + Copyright(C) 2025 Altom Consulting + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +""" + +import pytest + +from .utils import Scenes +from alttester import AltKeyCode +from alttester.altby import AltBy +from alttester.altdriver_unity import AltDriverUnity + +@pytest.mark.usefixtures("alt_driver_unity") +class TestScene10: + alt_driver: AltDriverUnity + + @pytest.fixture(autouse=True) + def setup(self): + self.alt_driver.reset_input() + self.alt_driver.load_scene(Scenes.Scene10) + + def test_scroll_NIS(self): + player = self.alt_driver.find_object(AltBy.name("Player")) + + assert not player.get_component_property( + "AltNIPDebugScript", + "wasScrolled", + "Assembly-CSharp" + ) + + self.alt_driver.scroll(300, duration=1, wait=True) + + assert player.get_component_property( + "AltNIPDebugScript", + "wasScrolled", + "Assembly-CSharp" + ) + + def test_key_down_and_key_up_NIS(self): + player = self.alt_driver.find_object(AltBy.name("Player")) + initial_position = player.get_component_property( + "UnityEngine.Transform", "position", "UnityEngine.CoreModule") + + self.alt_driver.key_down(AltKeyCode.A) + self.alt_driver.key_up(AltKeyCode.A) + lef_position = player.get_component_property( + "UnityEngine.Transform", "position", "UnityEngine.CoreModule") + + assert lef_position != initial_position + + self.alt_driver.key_down(AltKeyCode.D) + self.alt_driver.key_up(AltKeyCode.D) + right_position = player.get_component_property( + "UnityEngine.Transform", "position", "UnityEngine.CoreModule") + + assert right_position != lef_position + + self.alt_driver.key_down(AltKeyCode.W) + self.alt_driver.key_up(AltKeyCode.W) + up_position = player.get_component_property( + "UnityEngine.Transform", "position", "UnityEngine.CoreModule") + + assert up_position != right_position + + self.alt_driver.key_down(AltKeyCode.S) + self.alt_driver.key_up(AltKeyCode.S) + down_position = player.get_component_property( + "UnityEngine.Transform", "position", "UnityEngine.CoreModule") + + assert down_position != up_position + + def test_press_key_NIS(self): + player = self.alt_driver.find_object(AltBy.name("Player")) + initial_position = player.get_component_property( + "UnityEngine.Transform", "position", "UnityEngine.CoreModule") + + self.alt_driver.press_key(AltKeyCode.A) + left_position = player.get_component_property( + "UnityEngine.Transform", "position", "UnityEngine.CoreModule") + + assert left_position != initial_position + + self.alt_driver.press_key(AltKeyCode.D) + right_position = player.get_component_property( + "UnityEngine.Transform", "position", "UnityEngine.CoreModule") + + assert right_position != left_position + + self.alt_driver.press_key(AltKeyCode.W) + up_position = player.get_component_property( + "UnityEngine.Transform", "position", "UnityEngine.CoreModule") + + assert up_position != right_position + + self.alt_driver.press_key(AltKeyCode.S) + down_position = player.get_component_property( + "UnityEngine.Transform", "position", "UnityEngine.CoreModule") + + assert down_position != up_position + + def test_press_keys_NIS(self): + player = self.alt_driver.find_object(AltBy.name("Player")) + initial_position = player.get_component_property( + "UnityEngine.Transform", "position", "UnityEngine.CoreModule") + + keys = [AltKeyCode.W, AltKeyCode.Mouse0] + self.alt_driver.press_keys(keys) + + final_position = player.get_component_property( + "UnityEngine.Transform", "position", "UnityEngine.CoreModule") + assert initial_position != final_position + + self.alt_driver.wait_for_object(AltBy.name("SimpleProjectile(Clone)"))