Skip to content

Commit f770f8b

Browse files
author
Julia Schwarz
committed
Add test that captures the bug
1 parent 3ade391 commit f770f8b

File tree

3 files changed

+164
-29
lines changed

3 files changed

+164
-29
lines changed
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
//------------------------------------------------------------------------------
2+
// <auto-generated>
3+
// This code was generated by a tool.
4+
// Runtime Version:4.0.30319.42000
5+
//
6+
// Changes to this file may cause incorrect behavior and will be lost if
7+
// the code is regenerated.
8+
// </auto-generated>
9+
//------------------------------------------------------------------------------
10+
11+
using System;
12+
using System.Reflection;
13+
14+
[assembly: System.Reflection.AssemblyCompanyAttribute("DotNetAdapter")]
15+
[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
16+
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
17+
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0")]
18+
[assembly: System.Reflection.AssemblyProductAttribute("DotNetAdapter")]
19+
[assembly: System.Reflection.AssemblyTitleAttribute("DotNetAdapter")]
20+
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
21+
22+
// Generated by the MSBuild WriteCodeFragment class.
23+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
using System;
2+
using System.IO;
3+
using System.Runtime.InteropServices;
4+
using UnityEditor;
5+
using UnityEngine;
6+
7+
namespace Microsoft.Windows.MixedReality.DotNetWinRT
8+
{
9+
[InitializeOnLoad]
10+
internal class Init : MonoBehaviour
11+
{
12+
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
13+
[return: MarshalAs(UnmanagedType.Bool)]
14+
static extern int AddDllDirectory(string lpPathName);
15+
16+
[DllImport("kernel32.dll", SetLastError = true)]
17+
static extern IntPtr LoadLibraryExW([MarshalAs(UnmanagedType.LPWStr)] string fileName, IntPtr fileHandle, uint flags);
18+
19+
[DllImport("kernel32.dll", SetLastError = true)]
20+
[return: MarshalAs(UnmanagedType.Bool)]
21+
static extern bool FreeLibrary(IntPtr moduleHandle);
22+
23+
24+
const uint LOAD_LIBRARY_SEARCH_DEFAULT_DIRS = 0x00001000;
25+
26+
static Init()
27+
{
28+
IntPtr modulePtr = LoadLibraryExW("MonoSupport.dll", IntPtr.Zero, LOAD_LIBRARY_SEARCH_DEFAULT_DIRS);
29+
if (modulePtr != IntPtr.Zero)
30+
{
31+
// DLL search paths already configured in this process; nothing more to do.
32+
FreeLibrary(modulePtr);
33+
return;
34+
}
35+
36+
// Find the path to this script
37+
string assetName = $"{typeof(Init).FullName}.cs";
38+
var assets = AssetDatabase.FindAssets(Path.GetFileNameWithoutExtension(assetName));
39+
if (assets.Length != 1)
40+
{
41+
Debug.LogError($"Failed to find single asset for {assetName}; found {assets.Length} instead!");
42+
return;
43+
}
44+
45+
char[] delims = { '/', '\\' };
46+
var assetDirectoryPath = Application.dataPath;
47+
var lastDelim = assetDirectoryPath.TrimEnd(delims).LastIndexOfAny(delims); // trim off Assets folder since it's also included in asset path
48+
var dllDirectory = Path.Combine(assetDirectoryPath.Substring(0, lastDelim), Path.GetDirectoryName(AssetDatabase.GUIDToAssetPath(assets[0]))).Replace('/', '\\');
49+
dllDirectory = Path.Combine(dllDirectory.Substring(0, dllDirectory.LastIndexOf("Editor")), @"x64");
50+
if (AddDllDirectory(dllDirectory) == 0)
51+
{
52+
Debug.LogError($"Failed to set DLL directory {dllDirectory}: Win32 error {Marshal.GetLastWin32Error()}");
53+
return;
54+
}
55+
56+
Debug.Log(string.Format("Added DLL directory {0} to the user search path.", dllDirectory));
57+
}
58+
}
59+
}

Assets/MixedRealityToolkit.Tests/PlayModeTests/PointerTests.cs

Lines changed: 82 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -101,43 +101,60 @@ public IEnumerator TestLinePointers()
101101
}
102102

103103

104-
private IEnumerator TestPointerFieldOfViewHelper(IMixedRealityPointer myPointer, GameObject cube, TestHand testHand)
104+
/// <summary>
105+
/// Test pointers are correctly enabled when interacting with colliders that are visible, but whose
106+
/// bounds are outside the camera FOV.
107+
/// </summary>
108+
[UnityTest]
109+
public IEnumerator TestPointerFieldOfViewLargeCollider()
105110
{
106-
cube.transform.SetPositionAndRotation(Vector3.forward * 1f, Quaternion.identity);
107-
cube.transform.localScale = Vector3.one * 0.1f;
108-
yield return testHand.MoveTo(cube.transform.position);
111+
var rightHand = new TestHand(Handedness.Right);
109112
yield return PlayModeTestUtilities.WaitForInputSystemUpdate();
110-
Assert.IsTrue(myPointer.IsInteractionEnabled, $"Pointer {myPointer.PointerName} should be enabled, cube in front camera. Cube size {cube.transform.localScale} location {cube.transform.position}.");
111113

112-
// Make cube no longer visible
113-
cube.transform.Translate(Vector3.up * 10);
114-
yield return testHand.MoveTo(cube.transform.position);
114+
var cube = GameObject.CreatePrimitive(PrimitiveType.Cube);
115+
cube.AddComponent<NearInteractionGrabbable>();
116+
cube.AddComponent<NearInteractionTouchableVolume>();
117+
yield return rightHand.Show(Vector3.zero);
115118
yield return PlayModeTestUtilities.WaitForInputSystemUpdate();
116-
Assert.IsFalse(myPointer.IsInteractionEnabled, $"Pointer {myPointer.PointerName} should NOT be enabled, cube behind camera. Cube size {cube.transform.localScale} location {cube.transform.position}.");
117119

118-
// For sphere and poke pointers, test that setting IgnoreCollidersNotInFOV works
119-
if (myPointer is SpherePointer spherePointer)
120-
{
121-
spherePointer.IgnoreCollidersNotInFOV = false;
122-
yield return PlayModeTestUtilities.WaitForInputSystemUpdate();
123-
Assert.IsTrue(myPointer.IsInteractionEnabled, $"Pointer {myPointer.PointerName} should be enabled because IgnoreCollidersNotInFOV is false.");
124-
spherePointer.IgnoreCollidersNotInFOV = true;
125-
}
126-
else if (myPointer is PokePointer pokePointer)
120+
var spherePointer = PointerUtils.GetPointer<SpherePointer>(Handedness.Right);
121+
var pokePointer = PointerUtils.GetPointer<PokePointer>(Handedness.Right);
122+
123+
yield return TestPointerFieldOfViewLargeColliderHelper(spherePointer, cube, rightHand);
124+
yield return TestPointerFieldOfViewLargeColliderHelper(pokePointer, cube, rightHand);
125+
126+
rightHand.Hide();
127+
GameObject.Destroy(cube);
128+
}
129+
130+
131+
private IEnumerator TestPointerFieldOfViewLargeColliderHelper(IMixedRealityPointer myPointer, GameObject cube, TestHand testHand)
132+
{
133+
cube.transform.localScale = new Vector3(1, 1, 0.05f);
134+
float[] yOffsets = new float[] { -1f, 0f, 1f };
135+
float[] xOffsets = new float[] { -1f, 0f, 1f };
136+
float[] zOffsets = new float[] { 1f, -1f };
137+
138+
foreach (var zOffset in zOffsets)
127139
{
128-
pokePointer.IgnoreCollidersNotInFOV = false;
129-
yield return PlayModeTestUtilities.WaitForInputSystemUpdate();
130-
Assert.IsTrue(myPointer.IsInteractionEnabled, $"Pointer {myPointer.PointerName} should be enabled because IgnoreCollidersNotInFOV is false.");
131-
pokePointer.IgnoreCollidersNotInFOV = true;
140+
foreach (var yOffset in yOffsets)
141+
{
142+
foreach (var xOffset in xOffsets)
143+
{
144+
var cameraPos = CameraCache.Main.transform.position;
145+
var pos = new Vector3(cameraPos.x + xOffset, cameraPos.y + yOffset, cameraPos.z + zOffset);
146+
cube.transform.position = pos;
147+
cube.transform.LookAt(CameraCache.Main.transform);
148+
yield return testHand.MoveTo(cube.transform.position);
149+
yield return PlayModeTestUtilities.WaitForInputSystemUpdate();
150+
Assert.IsTrue(zOffset == 1f ? myPointer.IsInteractionEnabled : !myPointer.IsInteractionEnabled,
151+
$"Pointer {myPointer.PointerName} in incorrect state. Cube size {cube.transform.localScale} location {cube.transform.position}.");
152+
}
153+
}
132154
}
133-
134-
// Move it back to be visible again
135-
cube.transform.Translate(Vector3.up * -10f);
136-
yield return testHand.MoveTo(cube.transform.position);
137-
yield return PlayModeTestUtilities.WaitForInputSystemUpdate();
138-
Assert.IsTrue(myPointer.IsInteractionEnabled, $"Pointer {myPointer.PointerName} should be enabled because it is near object inside of FOV. Cube size {cube.transform.localScale} location {cube.transform.position}.");
139155
}
140156

157+
141158
/// <summary>
142159
/// Tests that pointers behave correctly when interacting with objects inside and outside
143160
/// its field of view
@@ -164,7 +181,43 @@ public IEnumerator TestPointerFieldOfView()
164181
GameObject.Destroy(cube);
165182
}
166183

167-
184+
private IEnumerator TestPointerFieldOfViewHelper(IMixedRealityPointer myPointer, GameObject cube, TestHand testHand)
185+
{
186+
// Cube in front of camera
187+
cube.transform.SetPositionAndRotation(Vector3.forward * 1f, Quaternion.identity);
188+
cube.transform.localScale = Vector3.one * 0.1f;
189+
yield return testHand.MoveTo(cube.transform.position);
190+
yield return PlayModeTestUtilities.WaitForInputSystemUpdate();
191+
Assert.IsTrue(myPointer.IsInteractionEnabled, $"Pointer {myPointer.PointerName} should be enabled, cube in front camera. Cube size {cube.transform.localScale} location {cube.transform.position}.");
192+
193+
// Cube above camera
194+
cube.transform.Translate(Vector3.up * 10);
195+
yield return testHand.MoveTo(cube.transform.position);
196+
yield return PlayModeTestUtilities.WaitForInputSystemUpdate();
197+
Assert.IsFalse(myPointer.IsInteractionEnabled, $"Pointer {myPointer.PointerName} should NOT be enabled, cube behind camera. Cube size {cube.transform.localScale} location {cube.transform.position}.");
198+
199+
// For sphere and poke pointers, test that setting IgnoreCollidersNotInFOV works
200+
if (myPointer is SpherePointer spherePointer)
201+
{
202+
spherePointer.IgnoreCollidersNotInFOV = false;
203+
yield return PlayModeTestUtilities.WaitForInputSystemUpdate();
204+
Assert.IsTrue(myPointer.IsInteractionEnabled, $"Pointer {myPointer.PointerName} should be enabled because IgnoreCollidersNotInFOV is false.");
205+
spherePointer.IgnoreCollidersNotInFOV = true;
206+
}
207+
else if (myPointer is PokePointer pokePointer)
208+
{
209+
pokePointer.IgnoreCollidersNotInFOV = false;
210+
yield return PlayModeTestUtilities.WaitForInputSystemUpdate();
211+
Assert.IsTrue(myPointer.IsInteractionEnabled, $"Pointer {myPointer.PointerName} should be enabled because IgnoreCollidersNotInFOV is false.");
212+
pokePointer.IgnoreCollidersNotInFOV = true;
213+
}
214+
215+
// Move it back to be visible again
216+
cube.transform.Translate(Vector3.up * -10f);
217+
yield return testHand.MoveTo(cube.transform.position);
218+
yield return PlayModeTestUtilities.WaitForInputSystemUpdate();
219+
Assert.IsTrue(myPointer.IsInteractionEnabled, $"Pointer {myPointer.PointerName} should be enabled because it is near object inside of FOV. Cube size {cube.transform.localScale} location {cube.transform.position}.");
220+
}
168221
/// <summary>
169222
/// Tests that sphere pointer grabs object when hand is inside a giant grabbable
170223
/// </summary>

0 commit comments

Comments
 (0)