Skip to content
Open
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
1 change: 1 addition & 0 deletions org.mixedrealitytoolkit.core/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).

* Fixed "The type `MixedReality.Toolkit.Core MixedReality.Toolkit.Experimental.BubbleChildHoverEvents/TrickleChildHoverEvents/BubbleChildSelectEvents/TrickleChildSelectEvents` is being serialized by `[SerializeReference]`, but is missing the `[Serializable]` attribute." on Unity 6.3. [PR #1107](https://github.com/MixedRealityToolkit/MixedRealityToolkit-Unity/pull/1107)
* Fixed object identity API usages and obsolete sorting warnings for Unity 6.4 and newer in `FindObjectUtility`. [PR #1136](https://github.com/MixedRealityToolkit/MixedRealityToolkit-Unity/pull/1136)
* Fixed PlayMode tests failing when XR is initialized by ensuring XR is disabled during test runs. [PR #1141](https://github.com/MixedRealityToolkit/MixedRealityToolkit-Unity/pull/1141)

## Deprecated

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ namespace MixedReality.Toolkit.Core.Tests
/// <summary>
/// Base class to handle typical code setup/teardown and test utilities
/// </summary>
[PrebuildSetup(typeof(PlayModeTestXRDisabler))]
[PostBuildCleanup(typeof(PlayModeTestXRDisabler))]
public abstract class BaseRuntimeTests
{
int originalTargetFrameRate = 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@
"Unity.InputSystem",
"UnityEditor.TestRunner",
"UnityEngine.TestRunner",
"Unity.asset-store-validation.Editor"
"Unity.asset-store-validation.Editor",
"Unity.XR.Management",
"Unity.XR.Management.Editor"
],
"includePlatforms": [],
"excludePlatforms": [],
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
// Copyright (c) Mixed Reality Toolkit Contributors
// Licensed under the BSD 3-Clause

// Disable "missing XML comment" warning for tests. While nice to have, this documentation is not required.
#pragma warning disable CS1591

using System;
using UnityEngine;
using UnityEngine.TestTools;

#if UNITY_EDITOR
using UnityEditor;
using UnityEditor.XR.Management;
using UnityEngine.XR.Management;
#endif

namespace MixedReality.Toolkit.Core.Tests
{
/// <summary>
/// Ensures XR initialization on start is disabled during PlayMode tests so that input simulation
/// and synthetic hands function properly without being suppressed by active OpenXR or XR displays.
/// State is maintained in memory without dirtying project settings on disk, and restored to its original value upon test completion or exit.
/// </summary>
internal class PlayModeTestXRDisabler : IPrebuildSetup, IPostBuildCleanup
{
private const string WasXRDisabledByTestKey = "MRTK_PlayModeTest_XRDisabledByTest";
private static bool isSubscribed;

/// <inheritdoc />
void IPrebuildSetup.Setup()
{
#if UNITY_EDITOR
SubscribePlayModeStateChanged();
DisableXRIfEnabled();
#endif
}

/// <inheritdoc />
void IPostBuildCleanup.Cleanup()
{
#if UNITY_EDITOR
UnsubscribePlayModeStateChanged();
RestoreXR();
#endif
}

[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
private static void OnSubsystemRegistration()
{
#if UNITY_EDITOR
if (SessionState.GetBool(WasXRDisabledByTestKey, false))
{
ApplyDisableXR();
}
#endif
}

#if UNITY_EDITOR
private static void SubscribePlayModeStateChanged()
{
if (!isSubscribed)
{
EditorApplication.playModeStateChanged += OnPlayModeStateChanged;
isSubscribed = true;
}
}

private static void UnsubscribePlayModeStateChanged()
{
if (isSubscribed)
{
EditorApplication.playModeStateChanged -= OnPlayModeStateChanged;
isSubscribed = false;
}
}

private static void OnPlayModeStateChanged(PlayModeStateChange state)
{
if (state == PlayModeStateChange.EnteredEditMode)
{
UnsubscribePlayModeStateChanged();
RestoreXR();
}
}

private static XRGeneralSettings GetTargetSettings()
{
try
{
BuildTargetGroup targetGroup = BuildPipeline.GetBuildTargetGroup(EditorUserBuildSettings.activeBuildTarget);
return XRGeneralSettingsPerBuildTarget.XRGeneralSettingsForBuildTarget(targetGroup);
}
catch (Exception e)
{
Debug.LogWarning($"[PlayModeTestXRDisabler] Failed to get XRGeneralSettings: {e.Message}");
return null;
}
}

private static void DisableXRIfEnabled()
{
XRGeneralSettings settings = GetTargetSettings();
bool isEnabledOnTarget = settings != null && settings.InitManagerOnStart;
bool isEnabledOnInstance = XRGeneralSettings.Instance != null && XRGeneralSettings.Instance.InitManagerOnStart;

if (isEnabledOnTarget || isEnabledOnInstance)
{
SessionState.SetBool(WasXRDisabledByTestKey, true);
ApplyDisableXR();
}
}

private static void ApplyDisableXR()
{
if (XRGeneralSettings.Instance != null)
{
XRGeneralSettings.Instance.InitManagerOnStart = false;
}

XRGeneralSettings settings = GetTargetSettings();
if (settings != null)
{
settings.InitManagerOnStart = false;
}
}

private static void RestoreXR()
{
if (!SessionState.GetBool(WasXRDisabledByTestKey, false))
{
return;
}

SessionState.EraseBool(WasXRDisabledByTestKey);

XRGeneralSettings settings = GetTargetSettings();
if (settings != null)
{
settings.InitManagerOnStart = true;
}

if (XRGeneralSettings.Instance != null)
{
XRGeneralSettings.Instance.InitManagerOnStart = true;
}
}
#endif
}
}
#pragma warning restore CS1591

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

1 change: 1 addition & 0 deletions org.mixedrealitytoolkit.uxcore/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).

* Fixed "leaked managed shell" issue in `UGUIInputAdapter`. [PR #1096](https://github.com/MixedRealityToolkit/MixedRealityToolkit-Unity/pull/1096)
* Fixed "Attribute 'SerializeField' is not valid on this declaration type. It is only valid on 'field' declarations" error on `DialogButton` in Unity 6.3. [PR #1108](https://github.com/MixedRealityToolkit/MixedRealityToolkit-Unity/pull/1108)
* Fixed `SeeItSayItLabelEnablerTests` failure when running on platforms without speech recognition support. [PR #1141](https://github.com/MixedRealityToolkit/MixedRealityToolkit-Unity/pull/1141)

## [3.3.0] - 2025-11-12

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,7 @@ public IEnumerator TestEnableAndSetLabel()
interactor.gameObject.SetActive(true);

yield return null;
if (Application.isBatchMode)
{
LogAssert.Expect(LogType.Exception, new Regex("Speech recognition is not supported on this machine"));
}
ExpectSpeechRecognitionNotSupportedException();

Transform sublabel = label.transform.GetChild(0);
Assert.IsTrue(label.gameObject.activeSelf, "Label is enabled");
Expand All @@ -64,10 +61,7 @@ public IEnumerator TestAutoUpdateLabel()
interactor.gameObject.SetActive(true);

yield return null;
if (Application.isBatchMode)
{
LogAssert.Expect(LogType.Exception, new Regex("Speech recognition is not supported on this machine"));
}
ExpectSpeechRecognitionNotSupportedException();

Transform sublabel = label.transform.GetChild(0);
TMP_Text text = label.gameObject.GetComponentInChildren<TMP_Text>(true);
Expand All @@ -84,10 +78,7 @@ public IEnumerator TestAutoUpdateLabel()
// Wait for a frame to give Unity a change to actually destroy the object
yield return null;
// The speech recognition keyword change will trigger this exception at next update when speech recognition is not supported
if (Application.isBatchMode)
{
LogAssert.Expect(LogType.Exception, new Regex("Speech recognition is not supported on this machine"));
}
ExpectSpeechRecognitionNotSupportedException();
}

[UnityTest]
Expand Down Expand Up @@ -115,10 +106,7 @@ public IEnumerator TestPositionCanvasLabel()
interactor.gameObject.SetActive(true);

yield return null;
if (Application.isBatchMode)
{
LogAssert.Expect(LogType.Exception, new Regex("Speech recognition is not supported on this machine"));
}
ExpectSpeechRecognitionNotSupportedException();

RectTransform sublabel = label.transform.GetChild(0) as RectTransform;
Assert.AreEqual(sublabel.anchoredPosition3D, new Vector3(10, -30, -10), "Label is positioned correctly");
Expand All @@ -142,10 +130,7 @@ public IEnumerator TestPositionNonCanvasLabel()
interactor.gameObject.SetActive(true);

yield return null;
if (Application.isBatchMode)
{
LogAssert.Expect(LogType.Exception, new Regex("Speech recognition is not supported on this machine"));
}
ExpectSpeechRecognitionNotSupportedException();

Assert.AreEqual(label.transform.localPosition, new Vector3(10f, -.504f, -.004f), "Label is positioned correctly");
#else
Expand Down Expand Up @@ -203,6 +188,16 @@ private GameObject SetUpButton(bool allowSelectByVoice, Control control)
return testButton;
}

private static void ExpectSpeechRecognitionNotSupportedException()
{
#if MRTK_INPUT_PRESENT && MRTK_SPEECH_PRESENT && (UNITY_STANDALONE_WIN || UNITY_WSA || UNITY_EDITOR_WIN)
if (!UnityEngine.Windows.Speech.PhraseRecognitionSystem.isSupported)
{
LogAssert.Expect(LogType.Exception, new Regex("Speech recognition is not supported on this machine"));
}
#endif
}

private enum Control
{
None,
Expand Down
Loading