Skip to content

Commit

Permalink
Obsolete InteractiveComponent class
Browse files Browse the repository at this point in the history
And change Monkey.RunStep parameters
  • Loading branch information
nowsprinting committed Apr 20, 2024
1 parent adf2d13 commit 2d831d7
Show file tree
Hide file tree
Showing 7 changed files with 100 additions and 76 deletions.
2 changes: 1 addition & 1 deletion Runtime/InteractiveComponent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ namespace TestHelper.Monkey
/// <summary>
/// Wrapped component that provide interaction for user.
/// </summary>
// TODO: Rename to InteractableComponent
[Obsolete]
public class InteractiveComponent
{
/// <summary>
Expand Down
37 changes: 22 additions & 15 deletions Runtime/InteractiveComponentCollector.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@

using System;
using System.Collections.Generic;
using System.Linq;
using TestHelper.Monkey.DefaultStrategies;
using TestHelper.Monkey.Extensions;
using TestHelper.Monkey.Operators;
using UnityEngine;
using UnityEngine.EventSystems;
Expand Down Expand Up @@ -47,26 +49,22 @@ public InteractiveComponentCollector(
///
/// Note: If you only need UI elements, using UnityEngine.UI.Selectable.allSelectablesArray is faster.
/// </summary>
/// <returns>Interactive components</returns>
public IEnumerable<InteractiveComponent> FindInteractableComponents()
/// <returns>Interactable components</returns>
public IEnumerable<Component> FindInteractableComponents()
{
foreach (var component in FindMonoBehaviours())
{
if (_isInteractable.Invoke(component))
{
yield return InteractiveComponent.CreateInteractableComponent(component,
_isReachable,
_isInteractable,
_operators);
yield return component;
}
}
}

[Obsolete("Use FindInteractableComponents() instead")]
public static IEnumerable<InteractiveComponent> FindInteractiveComponents()
{
var instance = new InteractiveComponentCollector();
return instance.FindInteractableComponents();
throw new NotImplementedException("Use FindInteractableComponents() instead");
}

/// <summary>
Expand All @@ -75,24 +73,33 @@ public static IEnumerable<InteractiveComponent> FindInteractiveComponents()
///
/// Note: If you only need UI elements, using UnityEngine.UI.Selectable.allSelectablesArray is faster.
/// </summary>
/// <returns>Really interactive components</returns>
public IEnumerable<InteractiveComponent> FindReachableInteractableComponents()
/// <returns>Reachable and Interactable components</returns>
public IEnumerable<Component> FindReachableInteractableComponents()
{
foreach (var interactiveComponent in FindInteractableComponents())
foreach (var interactableComponent in FindInteractableComponents())
{
if (_isReachable.Invoke(interactiveComponent.gameObject, _eventData, _results))
if (_isReachable.Invoke(interactableComponent.gameObject, _eventData, _results))
{
yield return interactiveComponent;
yield return interactableComponent;
}
}
}

/// <summary>
/// Returns tuple of interactable component and operator.
/// Note: Not check reachable from user.
/// </summary>
/// <returns>Tuple of interactable component and operator</returns>
public IEnumerable<(Component, IOperator)> FindInteractableComponentsAndOperators()
{
return FindInteractableComponents().SelectMany(x => x.SelectOperators(_operators), (x, o) => (x, o));
}

[Obsolete("Use FindReachableInteractableComponents() instead")]
public static IEnumerable<InteractiveComponent> FindReallyInteractiveComponents(
Func<GameObject, Vector2> screenPointStrategy)
{
var instance = new InteractiveComponentCollector();
return instance.FindReachableInteractableComponents();
throw new NotImplementedException("Use FindReachableInteractableComponents() instead");
}

private static IEnumerable<MonoBehaviour> FindMonoBehaviours()
Expand Down
61 changes: 36 additions & 25 deletions Runtime/Monkey.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@
using System.Threading;
using Cysharp.Threading.Tasks;
using TestHelper.Monkey.Annotations;
using TestHelper.Monkey.Extensions;
using TestHelper.Monkey.Operators;
using TestHelper.Random;
using TestHelper.RuntimeInternals;
using UnityEngine;
using UnityEngine.Assertions;
using UnityEngine.EventSystems;

namespace TestHelper.Monkey
{
Expand Down Expand Up @@ -46,7 +48,7 @@ public static async UniTask Run(MonkeyConfig config, CancellationToken cancellat
GameViewControlHelper.SetGizmos(true);
}

var interactiveComponentCollector = new InteractiveComponentCollector(
var interactableComponentCollector = new InteractiveComponentCollector(
isReachable: config.IsReachable,
isInteractable: config.IsInteractable,
operators: config.Operators);
Expand All @@ -57,7 +59,13 @@ public static async UniTask Run(MonkeyConfig config, CancellationToken cancellat
{
while (Time.time < endTime)
{
var didAct = await RunStep(config, interactiveComponentCollector, cancellationToken);
var didAct = await RunStep(
config.Random,
config.Logger,
config.Screenshots,
config.IsReachable,
interactableComponentCollector,
cancellationToken);
if (didAct)
{
lastOperationTime = Time.time;
Expand Down Expand Up @@ -89,59 +97,62 @@ private class CoroutineRunner : MonoBehaviour
/// <summary>
/// Run a step of monkey testing.
/// </summary>
/// <param name="config">Run configuration for monkey testing</param>
/// <param name="interactiveComponentCollector"></param>
/// <param name="random">Random number generator from <c>MonkeyConfig</c></param>
/// <param name="logger">Logger from <c>MonkeyConfig</c></param>
/// <param name="screenshotOptions">Take screenshots options from <c>MonkeyConfig</c></param>
/// <param name="isReachable">Function returns the <c>GameObject</c> is reachable from user or not. from <c>MonkeyConfig</c></param>
/// <param name="interactableComponentCollector">InteractableComponentCollector instance includes isReachable, isInteractable, and operators</param>
/// <param name="cancellationToken">Cancellation token</param>
/// <returns></returns>
/// <returns>True if any operator was executed</returns>
public static async UniTask<bool> RunStep(
MonkeyConfig config,
InteractiveComponentCollector interactiveComponentCollector,
IRandom random,
ILogger logger,
ScreenshotOptions screenshotOptions,
Func<GameObject, PointerEventData, List<RaycastResult>, bool> isReachable,
InteractiveComponentCollector interactableComponentCollector,
CancellationToken cancellationToken = default)
{
var operators = GetOperators(interactiveComponentCollector);
var (selectedComponent, selectedOperator) = LotteryOperator(operators.ToList(), config.Random);
var operators = GetOperators(interactableComponentCollector);
var (selectedComponent, selectedOperator) = LotteryOperator(operators.ToList(), random, isReachable);
if (selectedComponent == null || selectedOperator == null)
{
return false;
}

if (config.Screenshots != null)
if (screenshotOptions != null)
{
if (s_coroutineRunner == null || (bool)s_coroutineRunner == false)
{
s_coroutineRunner = new GameObject("CoroutineRunner").AddComponent<CoroutineRunner>();
}

await ScreenshotHelper.TakeScreenshot(
directory: config.Screenshots.Directory,
filename: config.Screenshots.FilenameStrategy.GetFilename(),
superSize: config.Screenshots.SuperSize,
stereoCaptureMode: config.Screenshots.StereoCaptureMode
directory: screenshotOptions.Directory,
filename: screenshotOptions.FilenameStrategy.GetFilename(),
superSize: screenshotOptions.SuperSize,
stereoCaptureMode: screenshotOptions.StereoCaptureMode
)
.ToUniTask(s_coroutineRunner);
}

config.Logger.Log($"{selectedOperator} operates to {selectedComponent.gameObject.name}");
await selectedOperator.OperateAsync(selectedComponent.component, cancellationToken);
logger.Log($"{selectedOperator} operates to {selectedComponent.gameObject.name}");
await selectedOperator.OperateAsync(selectedComponent, cancellationToken);
return true;
}

internal static IEnumerable<(InteractiveComponent, IOperator)> GetOperators(
InteractiveComponentCollector interactiveComponentCollector)
internal static IEnumerable<(Component, IOperator)> GetOperators(InteractiveComponentCollector collector)
{
var components = interactiveComponentCollector.FindInteractableComponents()
.Where(x => !x.gameObject.TryGetComponent(typeof(IgnoreAnnotation), out _));
return components.SelectMany(x => x.GetOperators(), (x, o) => (x, o));
return collector.FindInteractableComponentsAndOperators()
.Where(x => !x.Item1.gameObject.TryGetComponent(typeof(IgnoreAnnotation), out _));
}

internal static (InteractiveComponent, IOperator) LotteryOperator(
List<(InteractiveComponent, IOperator)> operators,
IRandom random)
internal static (Component, IOperator) LotteryOperator(List<(Component, IOperator)> operators, IRandom random,
Func<GameObject, PointerEventData, List<RaycastResult>, bool> isReachable)
{
while (operators.Count > 0)
{
var (selectedComponent, selectedOperator) = operators[random.Next(operators.Count)];
if (selectedComponent.IsReachable())
if (selectedComponent.gameObject.IsReachable(isReachable))
{
return (selectedComponent, selectedOperator);
}
Expand Down
26 changes: 10 additions & 16 deletions Samples~/uGUI Demo/Tests/Runtime/ScenarioTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,8 @@
using System.Threading.Tasks;
using NUnit.Framework;
using TestHelper.Attributes;
using TestHelper.Monkey.Extensions;
using TestHelper.Monkey.Operators;
using UnityEngine;
using UnityEngine.UI;

namespace TestHelper.Monkey.Samples.UGUIDemo
{
Expand All @@ -30,32 +29,27 @@ public async Task OpenSubScreens(string target)

// When click Start button, then open Home screen.
var startButton = await _finder.FindByNameAsync("StartButton", interactable: true);
var startComponent = CreateInteractableComponent(startButton.GetComponent<Button>());
var startClickOperator = startComponent.GetOperatorsByType(OperatorType.Click).First();
startClickOperator.OperateAsync(startComponent.component);
var startComponent = startButton.GetInteractableComponents().First();
var startOperator = startComponent.SelectOperatorsOfType(_config.Operators, OperatorType.Click).First();
startOperator.OperateAsync(startComponent);

await _finder.FindByNameAsync("Home");

// When click target button, then open target screen.
var targetButton = await _finder.FindByNameAsync($"{target}Button", interactable: true);
var targetComponent = CreateInteractableComponent(targetButton.GetComponent<Button>());
var targetClickOperator = targetComponent.GetOperatorsByType(OperatorType.Click).First();
targetClickOperator.OperateAsync(targetComponent.component);
var targetComponent = targetButton.GetInteractableComponents().First();
var targetOperator = targetComponent.SelectOperatorsOfType(_config.Operators, OperatorType.Click).First();
targetOperator.OperateAsync(targetComponent);

await _finder.FindByNameAsync(target);

// When click Back button, then return Home screen.
var backButton = await _finder.FindByPathAsync($"**/{target}/BackButton", interactable: true);
var backComponent = CreateInteractableComponent(backButton.GetComponent<Button>());
var backClickOperator = backComponent.GetOperatorsByType(OperatorType.Click).First();
backClickOperator.OperateAsync(backComponent.component);
var backComponent = backButton.GetInteractableComponents().First();
var backOperator = backComponent.SelectOperatorsOfType(_config.Operators, OperatorType.Click).First();
backOperator.OperateAsync(backComponent);

await _finder.FindByNameAsync("Home");
}

private InteractiveComponent CreateInteractableComponent(MonoBehaviour component)
{
return InteractiveComponent.CreateInteractableComponent(component, operators: _config.Operators);
}
}
}
4 changes: 3 additions & 1 deletion Tests/Runtime/Annotations/PositionAnnotationTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
using System.Linq;
using NUnit.Framework;
using TestHelper.Attributes;
using TestHelper.Monkey.DefaultStrategies;
using TestHelper.Monkey.Extensions;

namespace TestHelper.Monkey.Annotations
{
Expand Down Expand Up @@ -31,7 +33,7 @@ string name
// Without no position annotations, IsReallyInteractiveFromUser() is always false because
// gameObject.transform.position is not in the mesh. So IsReallyInteractiveFromUser() is true means
// the position annotation work well
Assert.That(target.IsReachable(), Is.True);
Assert.That(target.gameObject.IsReachable(DefaultReachableStrategy.IsReachable), Is.True);
}
}
}
10 changes: 6 additions & 4 deletions Tests/Runtime/InteractiveComponentTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
using System.Threading.Tasks;
using NUnit.Framework;
using TestHelper.Attributes;
using TestHelper.Monkey.DefaultStrategies;
using TestHelper.Monkey.Extensions;
using TestHelper.Monkey.Operators;
using TestHelper.Monkey.TestDoubles;
using UnityEngine;
Expand Down Expand Up @@ -67,7 +69,7 @@ public void IsReachable_reachableObjects_returnTrue(string targetName)
var target = new InteractiveComponentCollector().FindInteractableComponents()
.First(x => x.gameObject.name == targetName);

Assert.That(target.IsReachable(), Is.True);
Assert.That(target.gameObject.IsReachable(DefaultReachableStrategy.IsReachable), Is.True);
}

[TestCase("BeyondTheWall")] // Beyond the another object
Expand All @@ -78,7 +80,7 @@ public void IsReachable_unreachableObjects_returnFalse(string targetName)
var target = new InteractiveComponentCollector().FindInteractableComponents()
.First(x => x.gameObject.name == targetName);

Assert.That(target.IsReachable(), Is.False);
Assert.That(target.gameObject.IsReachable(DefaultReachableStrategy.IsReachable), Is.False);
}
}

Expand All @@ -102,7 +104,7 @@ public async Task IsReachable_reachableObjects_returnTrue(string targetName)
var target = new InteractiveComponentCollector().FindInteractableComponents()
.First(x => x.gameObject.name == targetName);

Assert.That(target.IsReachable(), Is.True);
Assert.That(target.gameObject.IsReachable(DefaultReachableStrategy.IsReachable), Is.True);
}

[TestCase("BeyondTheWall")] // Beyond the another object
Expand All @@ -117,7 +119,7 @@ public async Task IsReachable_unreachableObjects_returnFalse(string targetName)
var target = new InteractiveComponentCollector().FindInteractableComponents()
.First(x => x.gameObject.name == targetName);

Assert.That(target.IsReachable(), Is.False);
Assert.That(target.gameObject.IsReachable(DefaultReachableStrategy.IsReachable), Is.False);
}
}

Expand Down
Loading

0 comments on commit 2d831d7

Please sign in to comment.