Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions Assets/Tests/Editor/EditorWindowCaptureUtilityTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
using NUnit.Framework;
using UnityEngine;

namespace io.github.hatayama.uLoopMCP.Tests.Editor
{
public class EditorWindowCaptureUtilityTests
{
[Test]
public void CalculateImageToInputOffsetY_WhenRenderTextureIsShorterThanGameView_ShouldReturnTopOffset()
{
Vector2 gameViewSize = new Vector2(1768f, 1383f);

int offsetY = EditorWindowCaptureUtility.CalculateImageToInputOffsetY(gameViewSize, 1080);

Assert.That(offsetY, Is.EqualTo(303));
}

[Test]
public void CalculateImageToInputOffsetY_WhenRenderTextureMatchesGameView_ShouldReturnZero()
{
Vector2 gameViewSize = new Vector2(1920f, 1080f);

int offsetY = EditorWindowCaptureUtility.CalculateImageToInputOffsetY(gameViewSize, 1080);

Assert.That(offsetY, Is.EqualTo(0));
}

[Test]
public void CreateUnavailableGameRenderingImageInfo_WhenRenderTextureIsMissing_ShouldUseGameViewSizeAndZeroOffset()
{
Vector2 gameViewSize = new Vector2(1768f, 1383f);

GameRenderingImageInfo renderingImageInfo =
EditorWindowCaptureUtility.CreateUnavailableGameRenderingImageInfo(gameViewSize);

Assert.That(renderingImageInfo.GameViewSize, Is.EqualTo(gameViewSize));
Assert.That(renderingImageInfo.RenderingImageSize, Is.EqualTo(gameViewSize));
Assert.That(renderingImageInfo.ImageToInputOffsetY, Is.EqualTo(0));
}
}
}
11 changes: 11 additions & 0 deletions Assets/Tests/Editor/EditorWindowCaptureUtilityTests.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

46 changes: 46 additions & 0 deletions Assets/Tests/Editor/GameViewCoordinateUtilityTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
using NUnit.Framework;
using UnityEngine;

namespace io.github.hatayama.uLoopMCP.Tests.Editor
{
public class GameViewCoordinateUtilityTests
{
[Test]
public void ConvertInputToUnity_WhenInputIsNearTop_ShouldFlipYWithinGameView()
{
Vector2 gameViewSize = new Vector2(1920f, 1080f);
Vector2 inputPosition = new Vector2(0f, 100f);

GameViewCoordinateConversion conversion =
GameViewCoordinateUtility.ConvertInputToUnity(inputPosition, gameViewSize);

Assert.That(conversion.InputPosition, Is.EqualTo(inputPosition));
Assert.That(conversion.InjectedUnityPosition, Is.EqualTo(new Vector2(0f, 980f)));
Assert.That(conversion.GameViewSize, Is.EqualTo(gameViewSize));
}

[Test]
public void ConvertInputToUnity_WhenInputIsAtCenter_ShouldKeepCenterY()
{
Vector2 gameViewSize = new Vector2(1920f, 1080f);
Vector2 inputPosition = new Vector2(960f, 540f);

GameViewCoordinateConversion conversion =
GameViewCoordinateUtility.ConvertInputToUnity(inputPosition, gameViewSize);

Assert.That(conversion.InjectedUnityPosition, Is.EqualTo(new Vector2(960f, 540f)));
}

[Test]
public void ConvertInputToUnity_WhenInputIsAtBottomRight_ShouldMapToBottomLeftOrigin()
{
Vector2 gameViewSize = new Vector2(1920f, 1080f);
Vector2 inputPosition = new Vector2(1920f, 1080f);

GameViewCoordinateConversion conversion =
GameViewCoordinateUtility.ConvertInputToUnity(inputPosition, gameViewSize);

Assert.That(conversion.InjectedUnityPosition, Is.EqualTo(new Vector2(1920f, 0f)));
}
}
}
11 changes: 11 additions & 0 deletions Assets/Tests/Editor/GameViewCoordinateUtilityTests.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

69 changes: 69 additions & 0 deletions Assets/Tests/Editor/RaycastGridAnnotatorTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
using System.Collections.Generic;
using NUnit.Framework;
using UnityEngine;

namespace io.github.hatayama.uLoopMCP.Tests.Editor
{
public class RaycastGridAnnotatorTests
{
[Test]
public void CalculateGridInputPosition_WhenRenderingHasTopOffset_ShouldSampleInsideCapturedImage()
{
Vector2 renderingImageSize = new Vector2(1200f, 1080f);

Vector2 inputPosition =
RaycastGridAnnotator.CalculateGridInputPosition(renderingImageSize, 303, 1, 3);

Assert.That(inputPosition.x, Is.EqualTo(600f));
Assert.That(inputPosition.y, Is.EqualTo(483f));
}

[Test]
public void CalculateGridInputPosition_WhenRenderingHasTopOffset_ShouldKeepBottomRowVisible()
{
Vector2 renderingImageSize = new Vector2(1200f, 1080f);

Vector2 inputPosition =
RaycastGridAnnotator.CalculateGridInputPosition(renderingImageSize, 303, 5, 3);

Assert.That(inputPosition.x, Is.EqualTo(600f));
Assert.That(inputPosition.y, Is.EqualTo(1203f));
}

[Test]
public void CreateOverlayElements_WhenPointsIncludeMisses_ShouldAnnotateOnlyHits()
{
List<RaycastGridPointInfo> points = new List<RaycastGridPointInfo>
{
new RaycastGridPointInfo
{
Label = "R1",
Hit = true,
InputX = 100f,
InputY = 200f,
InjectedUnityPositionX = 100f,
InjectedUnityPositionY = 880f,
HitGameObjectName = "Cube",
HitGameObjectPath = "Cube"
},
new RaycastGridPointInfo
{
Label = "R2",
Hit = false,
InputX = 200f,
InputY = 300f,
InjectedUnityPositionX = 200f,
InjectedUnityPositionY = 780f
}
};

List<UIElementInfo> overlayElements = RaycastGridAnnotator.CreateOverlayElements(points);

Assert.That(overlayElements.Count, Is.EqualTo(1));
Assert.That(overlayElements[0].Label, Is.EqualTo("R1"));
Assert.That(overlayElements[0].Name, Is.EqualTo("Cube"));
Assert.That(overlayElements[0].Type, Is.EqualTo("RaycastHit"));
Assert.That(overlayElements[0].Interaction, Is.EqualTo("Raycast"));
}
}
}
11 changes: 11 additions & 0 deletions Assets/Tests/Editor/RaycastGridAnnotatorTests.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

190 changes: 190 additions & 0 deletions Assets/Tests/Editor/RaycastToolTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
#nullable enable
using System.Collections.Generic;
using System.Threading.Tasks;
using Newtonsoft.Json.Linq;
using NUnit.Framework;
using UnityEngine;

namespace io.github.hatayama.uLoopMCP.Tests.Editor
{
public class RaycastToolTests
{
private GameObject? _cameraObject;
private GameObject? _cubeObject;
private bool _originalAutoSyncTransforms;
private readonly List<GameObject> _retaggedMainCameraObjects = new List<GameObject>();

[SetUp]
public void SetUp()
{
_originalAutoSyncTransforms = Physics.autoSyncTransforms;
}

[TearDown]
public void TearDown()
{
Physics.autoSyncTransforms = _originalAutoSyncTransforms;

if (_cubeObject != null)
{
Object.DestroyImmediate(_cubeObject);
}

if (_cameraObject != null)
{
Object.DestroyImmediate(_cameraObject);
}

foreach (GameObject retaggedObject in _retaggedMainCameraObjects)
{
if (retaggedObject != null)
{
retaggedObject.tag = "MainCamera";
}
}

_retaggedMainCameraObjects.Clear();
_cubeObject = null;
_cameraObject = null;
}

[Test]
public async Task ExecuteAsync_WhenCoordinateIntersectsCollider_ShouldReturnHitAndConversionMetadata()
{
CreateRaycastScene();
Vector2 gameViewSize = GameViewCoordinateUtility.GetMainGameViewSize();
Vector2 inputPosition = new Vector2(gameViewSize.x / 2f, gameViewSize.y / 2f);

RaycastResponse response = await ExecuteRaycast(inputPosition);

Assert.That(response.Success, Is.True);
Assert.That(response.Hit, Is.True);
Assert.That(response.HitGameObjectName, Is.EqualTo("RaycastToolTestsCube"));
Assert.That(response.InputCoordinateSystem, Is.EqualTo(McpConstants.COORDINATE_SYSTEM_TOP_LEFT_GAME_VIEW));
Assert.That(response.UnityCoordinateSystem, Is.EqualTo(McpConstants.COORDINATE_SYSTEM_BOTTOM_LEFT_GAME_VIEW));
Assert.That(response.CoordinateConversionFormula, Is.EqualTo(McpConstants.COORDINATE_CONVERSION_FORMULA_GAME_VIEW_INPUT_TO_UNITY));
Assert.That(response.InputPositionX, Is.EqualTo(inputPosition.x));
Assert.That(response.InputPositionY, Is.EqualTo(inputPosition.y));
Assert.That(response.InjectedUnityPositionX, Is.EqualTo(inputPosition.x));
Assert.That(response.InjectedUnityPositionY, Is.EqualTo(gameViewSize.y - inputPosition.y));
}

[Test]
public async Task ExecuteAsync_WhenCoordinateMissesCollider_ShouldReturnNoHit()
{
CreateRaycastScene();
Vector2 inputPosition = new Vector2(0f, 0f);

RaycastResponse response = await ExecuteRaycast(inputPosition);

Assert.That(response.Success, Is.True);
Assert.That(response.Hit, Is.False);
Assert.That(response.HitGameObjectName, Is.Null);
}

[Test]
public async Task ExecuteAsync_WhenCameraIsMissing_ShouldReturnConversionMetadata()
{
RetagExistingMainCameras();
Vector2 gameViewSize = GameViewCoordinateUtility.GetMainGameViewSize();
Vector2 inputPosition = new Vector2(gameViewSize.x / 2f, gameViewSize.y / 2f);

RaycastResponse response = await ExecuteRaycast(inputPosition);

Assert.That(response.Success, Is.False);
Assert.That(response.Message, Does.Contain("Camera.main"));
Assert.That(response.InputPositionX, Is.EqualTo(inputPosition.x));
Assert.That(response.InputPositionY, Is.EqualTo(inputPosition.y));
Assert.That(response.InjectedUnityPositionX, Is.EqualTo(inputPosition.x));
Assert.That(response.InjectedUnityPositionY, Is.EqualTo(gameViewSize.y - inputPosition.y));
Assert.That(response.CoordinateConversionFormula, Is.EqualTo(McpConstants.COORDINATE_CONVERSION_FORMULA_GAME_VIEW_INPUT_TO_UNITY));
}

[Test]
public async Task ExecuteAsync_WhenColliderLayerIsHiddenByCamera_ShouldReturnNoHit()
{
CreateRaycastScene();
if (_cameraObject == null)
{
Assert.Fail("Camera should be created.");
return;
}

Camera camera = _cameraObject.GetComponent<Camera>();
camera.cullingMask = 0;
Vector2 gameViewSize = GameViewCoordinateUtility.GetMainGameViewSize();
Vector2 inputPosition = new Vector2(gameViewSize.x / 2f, gameViewSize.y / 2f);

RaycastResponse response = await ExecuteRaycast(inputPosition);

Assert.That(response.Success, Is.True);
Assert.That(response.Hit, Is.False);
}

[Test]
public async Task ExecuteAsync_WhenAutoSyncTransformsIsDisabled_ShouldRaycastAgainstLatestTransform()
{
Physics.autoSyncTransforms = false;
CreateRaycastScene();
if (_cubeObject == null)
{
Assert.Fail("Cube should be created.");
return;
}

_cubeObject.transform.position = new Vector3(100f, 0f, 0f);
Vector2 gameViewSize = GameViewCoordinateUtility.GetMainGameViewSize();
Vector2 inputPosition = new Vector2(gameViewSize.x / 2f, gameViewSize.y / 2f);

RaycastResponse response = await ExecuteRaycast(inputPosition);

Assert.That(response.Success, Is.True);
Assert.That(response.Hit, Is.False);
}

private void CreateRaycastScene()
{
RetagExistingMainCameras();

_cameraObject = new GameObject("RaycastToolTestsCamera");
Camera camera = _cameraObject.AddComponent<Camera>();
_cameraObject.tag = "MainCamera";
_cameraObject.transform.position = new Vector3(0f, 0f, -10f);
_cameraObject.transform.rotation = Quaternion.identity;
camera.nearClipPlane = 0.1f;
camera.farClipPlane = 100f;

_cubeObject = GameObject.CreatePrimitive(PrimitiveType.Cube);
_cubeObject.name = "RaycastToolTestsCube";
_cubeObject.transform.position = Vector3.zero;
}

private void RetagExistingMainCameras()
{
Camera[] cameras = Object.FindObjectsByType<Camera>(FindObjectsSortMode.None);
foreach (Camera camera in cameras)
{
if (!camera.CompareTag("MainCamera"))
{
continue;
}

_retaggedMainCameraObjects.Add(camera.gameObject);
camera.gameObject.tag = "Untagged";
}
}

private static async Task<RaycastResponse> ExecuteRaycast(Vector2 inputPosition)
{
RaycastTool tool = new RaycastTool();
JObject parameters = new JObject
{
["x"] = inputPosition.x,
["y"] = inputPosition.y
};

BaseToolResponse baseResponse = await tool.ExecuteAsync(parameters);
return (RaycastResponse)baseResponse;
}
}
}
11 changes: 11 additions & 0 deletions Assets/Tests/Editor/RaycastToolTests.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading