上传YomovSDK
This commit is contained in:
8
Packages/com.unity.xr.openxr/Tests/Editor.meta
Normal file
8
Packages/com.unity.xr.openxr/Tests/Editor.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 54a2f503330754db18fa779653556e95
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,62 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using NUnit.Framework;
|
||||
using UnityEditor.Build.Reporting;
|
||||
using UnityEditor.VersionControl;
|
||||
using UnityEditor.XR.OpenXR.Features;
|
||||
using UnityEngine;
|
||||
using UnityEngine.XR.OpenXR;
|
||||
using UnityEngine.XR.OpenXR.Features;
|
||||
using UnityEngine.XR.OpenXR.Features.Interactions;
|
||||
using UnityEngine.XR.OpenXR.Features.Mock;
|
||||
using Assert = UnityEngine.Assertions.Assert;
|
||||
using UnityEngine.XR.OpenXR.Tests;
|
||||
using static UnityEditor.XR.OpenXR.Tests.OpenXREditorTestHelpers;
|
||||
|
||||
namespace UnityEditor.XR.OpenXR.Tests
|
||||
{
|
||||
internal class BootConfigEditorTests : OpenXRLoaderSetup
|
||||
{
|
||||
[Test]
|
||||
public void TestCanCreateBootConfigAndroid()
|
||||
{
|
||||
TestBuildTarget(BuildTarget.Android);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestCanCreateBootConfigWindows()
|
||||
{
|
||||
TestBuildTarget(BuildTarget.StandaloneWindows);
|
||||
TestBuildTarget(BuildTarget.StandaloneWindows64);
|
||||
}
|
||||
|
||||
private void TestBuildTarget(BuildTarget buildTarget)
|
||||
{
|
||||
var bootConfig = new BootConfig(buildTarget);
|
||||
bootConfig.ReadBootConfig();
|
||||
|
||||
// Check to see that we do not have the following key in the boot config
|
||||
Assert.IsFalse(bootConfig.TryGetValue("xr-sample-bootconfig-key01", out string value));
|
||||
Assert.AreEqual(value, null);
|
||||
|
||||
// Check to see that we can store a key and retrieve it.
|
||||
bootConfig.SetValueForKey("xr-sample-bootconfig-key02", "primary value");
|
||||
Assert.IsTrue(bootConfig.TryGetValue("xr-sample-bootconfig-key02", out string key02value));
|
||||
Assert.AreEqual(key02value, "primary value");
|
||||
Assert.IsTrue(bootConfig.CheckValuePairExists("xr-sample-bootconfig-key02", "primary value"));
|
||||
|
||||
// check to see that we can write the keys to the boot config and ensure that we can
|
||||
// retrieve the stored values
|
||||
bootConfig.WriteBootConfig();
|
||||
|
||||
var cloneBootConfig = new BootConfig(buildTarget);
|
||||
cloneBootConfig.ReadBootConfig();
|
||||
Assert.IsTrue(cloneBootConfig.TryGetValue("xr-sample-bootconfig-key02", out key02value));
|
||||
Assert.AreEqual(key02value, "primary value");
|
||||
Assert.IsTrue(cloneBootConfig.CheckValuePairExists("xr-sample-bootconfig-key02", "primary value"));
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a1c3182e62fd44cea6ebdbb5b36feec5
|
||||
timeCreated: 1677640135
|
||||
@@ -0,0 +1,57 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using NUnit.Framework;
|
||||
using UnityEditor.Build.Reporting;
|
||||
using UnityEditor.VersionControl;
|
||||
using UnityEditor.XR.OpenXR.Features;
|
||||
using UnityEngine;
|
||||
using UnityEngine.XR.OpenXR;
|
||||
using UnityEngine.XR.OpenXR.Features;
|
||||
using UnityEngine.XR.OpenXR.Features.Interactions;
|
||||
using UnityEngine.XR.OpenXR.Features.Mock;
|
||||
using Assert = UnityEngine.Assertions.Assert;
|
||||
using UnityEngine.XR.OpenXR.Tests;
|
||||
using static UnityEditor.XR.OpenXR.Tests.OpenXREditorTestHelpers;
|
||||
|
||||
namespace UnityEditor.XR.OpenXR.Tests
|
||||
{
|
||||
internal class FeatureModifyingTests : OpenXRLoaderSetup
|
||||
{
|
||||
// Override AfterTest to prevent OpenXRSettings.Instance.features from getting reset.
|
||||
// This test suite destroys and restores OpenXRSettings.Instance.features manually.
|
||||
public override void AfterTest()
|
||||
{
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DuplicateSettingAssetTest()
|
||||
{
|
||||
// Local OpenXR filepath that contains the test OpenXR Package Settings.asset
|
||||
string openXRFolder = Path.GetFullPath("Packages/com.unity.xr.openxr");
|
||||
|
||||
string settingsFilePath = OpenXRPackageSettings.OpenXRPackageSettingsAssetPath();
|
||||
string metaFilePath = settingsFilePath + ".meta";
|
||||
|
||||
string testAssetName = "OpenXR Package Settings With Duplicates.testasset";
|
||||
string testAssetPath = Path.Combine(openXRFolder, "Tests", "Editor", testAssetName);
|
||||
string testMetaAssetPath = testAssetPath + ".meta";
|
||||
|
||||
// Copy in the test files (the files with duplicate settings)
|
||||
File.Delete(settingsFilePath);
|
||||
File.Delete(metaFilePath);
|
||||
File.Copy(testAssetPath, settingsFilePath);
|
||||
File.Copy(testMetaAssetPath, metaFilePath);
|
||||
|
||||
// Verify that we detect duplicates in the test file.
|
||||
Assert.IsFalse(OpenXRProjectValidation.AssetHasNoDuplicates(), "The duplicate settings on the bad asset should be detected.");
|
||||
|
||||
// Regenerate the asset (as if the user clicks on the Fix button in the validation window)
|
||||
OpenXRProjectValidation.RegenerateXRPackageSettingsAsset();
|
||||
|
||||
// Verify that there are no duplicates in the settings file now.
|
||||
Assert.IsTrue(OpenXRProjectValidation.AssetHasNoDuplicates(), "After regenerating the asset, the duplicate settings should be removed.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2755597eb30ac124c861b2694debd290
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
408
Packages/com.unity.xr.openxr/Tests/Editor/FeatureSetTests.cs
Normal file
408
Packages/com.unity.xr.openxr/Tests/Editor/FeatureSetTests.cs
Normal file
@@ -0,0 +1,408 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using UnityEditor.XR.OpenXR.Features;
|
||||
using UnityEngine.XR.OpenXR.Features.Interactions;
|
||||
using UnityEngine.XR.OpenXR.Features;
|
||||
using Assert = UnityEngine.Assertions.Assert;
|
||||
using static UnityEditor.XR.OpenXR.Features.OpenXRFeatureSetManager;
|
||||
using static UnityEditor.XR.OpenXR.Tests.OpenXREditorTestHelpers;
|
||||
using UnityEngine.XR.OpenXR.Tests;
|
||||
|
||||
namespace UnityEditor.XR.OpenXR.Tests
|
||||
{
|
||||
internal class FeatureSetTests : OpenXRLoaderSetup
|
||||
{
|
||||
const string k_KnownFeatureSetName = "Known Test";
|
||||
const string k_TestFeatureSetName = "Test Feature Set";
|
||||
const string k_TestFeatureSetNameHandAndEye = "Test Feature Set Hand and Eye Tracking";
|
||||
const string k_TestFeatureSetNameHand = "Test Feature Set Hand Tracking";
|
||||
const string k_TestFeatureSetDescription = "Test feature set";
|
||||
const string k_TestFeatureSetId = "com.unity.xr.test.featureset";
|
||||
const string k_TestFeatureSetIdTwo = "com.unity.xr.test.featureset2";
|
||||
const string k_TestFeatureSetIdThree = "com.unity.xr.test.featureset3";
|
||||
const string k_TestFeatureSetIdFour = "com.unity.xr.test.featureset4";
|
||||
|
||||
[OpenXRFeatureSet(
|
||||
FeatureIds = new string[]
|
||||
{
|
||||
MicrosoftHandInteraction.featureId
|
||||
},
|
||||
UiName = k_TestFeatureSetName,
|
||||
Description = k_TestFeatureSetDescription,
|
||||
FeatureSetId = k_TestFeatureSetId,
|
||||
SupportedBuildTargets = new BuildTargetGroup[] { BuildTargetGroup.Standalone },
|
||||
RequiredFeatureIds = new string[]
|
||||
{
|
||||
MicrosoftHandInteraction.featureId
|
||||
}
|
||||
)]
|
||||
[OpenXRFeatureSet(
|
||||
FeatureIds = new string[]
|
||||
{
|
||||
MicrosoftHandInteraction.featureId,
|
||||
EyeGazeInteraction.featureId,
|
||||
},
|
||||
UiName = k_TestFeatureSetNameHandAndEye,
|
||||
Description = k_TestFeatureSetDescription,
|
||||
FeatureSetId = k_TestFeatureSetIdTwo,
|
||||
SupportedBuildTargets = new BuildTargetGroup[] { BuildTargetGroup.WSA },
|
||||
RequiredFeatureIds = new string[]
|
||||
{
|
||||
MicrosoftHandInteraction.featureId,
|
||||
EyeGazeInteraction.featureId,
|
||||
}
|
||||
)]
|
||||
[OpenXRFeatureSet(
|
||||
FeatureIds = new string[]
|
||||
{
|
||||
MicrosoftHandInteraction.featureId,
|
||||
},
|
||||
UiName = k_TestFeatureSetNameHand,
|
||||
Description = k_TestFeatureSetDescription,
|
||||
FeatureSetId = k_TestFeatureSetIdThree,
|
||||
SupportedBuildTargets = new BuildTargetGroup[] { BuildTargetGroup.WSA },
|
||||
RequiredFeatureIds = new string[]
|
||||
{
|
||||
MicrosoftHandInteraction.featureId,
|
||||
}
|
||||
)]
|
||||
[OpenXRFeatureSet(
|
||||
FeatureIds = new string[]
|
||||
{
|
||||
MicrosoftHandInteraction.featureId,
|
||||
EyeGazeInteraction.featureId,
|
||||
},
|
||||
UiName = k_TestFeatureSetName,
|
||||
Description = k_TestFeatureSetDescription,
|
||||
FeatureSetId = k_TestFeatureSetId,
|
||||
SupportedBuildTargets = new BuildTargetGroup[] { BuildTargetGroup.Android },
|
||||
RequiredFeatureIds = new string[]
|
||||
{
|
||||
MicrosoftHandInteraction.featureId,
|
||||
EyeGazeInteraction.featureId,
|
||||
}
|
||||
)]
|
||||
[OpenXRFeatureSet(
|
||||
FeatureIds = new string[]
|
||||
{
|
||||
MicrosoftHandInteraction.featureId,
|
||||
EyeGazeInteraction.featureId,
|
||||
HTCViveControllerProfile.featureId,
|
||||
OculusTouchControllerProfile.featureId,
|
||||
},
|
||||
UiName = k_TestFeatureSetName,
|
||||
Description = k_TestFeatureSetDescription,
|
||||
FeatureSetId = k_TestFeatureSetIdFour,
|
||||
SupportedBuildTargets = new BuildTargetGroup[] { BuildTargetGroup.Standalone },
|
||||
RequiredFeatureIds = new string[]
|
||||
{
|
||||
MicrosoftHandInteraction.featureId,
|
||||
EyeGazeInteraction.featureId,
|
||||
},
|
||||
DefaultFeatureIds = new string[]
|
||||
{
|
||||
HTCViveControllerProfile.featureId,
|
||||
}
|
||||
)]
|
||||
sealed class TestFeatureSet { }
|
||||
|
||||
|
||||
public override void BeforeTest()
|
||||
{
|
||||
base.BeforeTest();
|
||||
OpenXRFeature.canSetFeatureDisabled = null;
|
||||
|
||||
InitializeFeatureSets(true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the feature sets by disabling all features sets and all features
|
||||
/// </summary>
|
||||
/// <param name="addTestFeatures">True to include test features</param>
|
||||
private void InitializeFeatureSets(bool addTestFeatures)
|
||||
{
|
||||
// Initialize first with test feature sets so we can make sure all feature sets are disabled
|
||||
OpenXRFeatureSetManager.InitializeFeatureSets(true);
|
||||
|
||||
foreach (var buildTargetGroup in GetBuildTargetGroups())
|
||||
{
|
||||
// Disable all feature sets for this build target
|
||||
foreach (var featureSetInfo in FeatureSetInfosForBuildTarget(buildTargetGroup))
|
||||
{
|
||||
featureSetInfo.isEnabled = false;
|
||||
featureSetInfo.wasEnabled = false;
|
||||
OpenXREditorSettings.Instance.SetFeatureSetSelected(buildTargetGroup, featureSetInfo.featureSetId, false);
|
||||
}
|
||||
|
||||
// Disable all features for this build target
|
||||
var extInfo = FeatureHelpersInternal.GetAllFeatureInfo(buildTargetGroup);
|
||||
foreach (var ext in extInfo.Features)
|
||||
{
|
||||
ext.Feature.enabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
// If requested with no feature sets then reinitialize
|
||||
if (!addTestFeatures)
|
||||
OpenXRFeatureSetManager.InitializeFeatureSets(false);
|
||||
|
||||
foreach (var buildTargetGroup in GetBuildTargetGroups())
|
||||
{
|
||||
// No feature sets should be enabled for any build target
|
||||
Assert.IsFalse(FeatureSetInfosForBuildTarget(buildTargetGroup).Any(f => f.isEnabled));
|
||||
|
||||
// No features should be enabled
|
||||
AssertAllFeatures(buildTargetGroup, FeatureDisabled);
|
||||
}
|
||||
}
|
||||
|
||||
public override void AfterTest()
|
||||
{
|
||||
base.AfterTest();
|
||||
OpenXRFeature.canSetFeatureDisabled = OpenXRFeatureSetManager.CanFeatureBeDisabled;
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void NoFeatureSetsReturnsEmptyList()
|
||||
{
|
||||
var featureSets = FeatureSetsForBuildTarget(BuildTargetGroup.iOS);
|
||||
Assert.AreEqual(0, featureSets.Count);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void FoundExpectedFeatureSets()
|
||||
{
|
||||
InitializeFeatureSets(false);
|
||||
|
||||
string[] expectedFeatureSets = new string[]
|
||||
{
|
||||
KnownFeatureSetsContent.s_MicrosoftHoloLensFeatureSetId
|
||||
};
|
||||
|
||||
var featureSets = FeatureSetsForBuildTarget(BuildTargetGroup.WSA);
|
||||
Assert.IsNotNull(featureSets);
|
||||
Assert.AreEqual(expectedFeatureSets.Length, featureSets.Count);
|
||||
|
||||
foreach (var featureSet in featureSets)
|
||||
{
|
||||
if (Array.IndexOf(expectedFeatureSets, featureSet.featureSetId) == -1)
|
||||
Assert.IsTrue(false, $"Found unexpected feature set id {featureSet.featureSetId}!");
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void UnknownFeatureSetRerturnNull()
|
||||
{
|
||||
// For this test we do not want the test features enabled so rerun the initilization with
|
||||
InitializeFeatureSets(false);
|
||||
var foundFeatureSet = GetFeatureSetWithId(BuildTargetGroup.iOS, k_TestFeatureSetId);
|
||||
Assert.IsNull(foundFeatureSet);
|
||||
foundFeatureSet = GetFeatureSetWithId(BuildTargetGroup.Standalone, "BAD FEATURE SET ID");
|
||||
Assert.IsNull(foundFeatureSet);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void OverrideKnownTestFeatureSet()
|
||||
{
|
||||
var foundFeatureSet = GetFeatureSetWithId(BuildTargetGroup.Standalone, k_TestFeatureSetId);
|
||||
Assert.IsNotNull(foundFeatureSet);
|
||||
Assert.AreEqual(0, String.Compare(foundFeatureSet.name, k_TestFeatureSetName, true));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void NonoverrideKnownTestFeatureSet()
|
||||
{
|
||||
var foundFeatureSet = GetFeatureSetWithId(BuildTargetGroup.WSA, k_TestFeatureSetId);
|
||||
Assert.IsNotNull(foundFeatureSet);
|
||||
Assert.AreEqual(0, String.Compare(foundFeatureSet.name, k_KnownFeatureSetName, true));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void EnableFeatureSetEnablesFeatures()
|
||||
{
|
||||
EnableFeatureSet(BuildTargetGroup.Standalone, k_TestFeatureSetId, enabled: true);
|
||||
AssertOnlyFeatures(BuildTargetGroup.Standalone, new string[] { MicrosoftHandInteraction.featureId }, FeatureEnabled);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DisableFeatureSetDisabledFeatures()
|
||||
{
|
||||
// Enable the feature set and make sure only its features are enabled
|
||||
EnableFeatureSet(BuildTargetGroup.Standalone, k_TestFeatureSetId, true);
|
||||
AssertOnlyFeatures(BuildTargetGroup.Standalone, new string[] { MicrosoftHandInteraction.featureId }, FeatureEnabled);
|
||||
|
||||
// Disable the feature set an make sure its features are disabled
|
||||
EnableFeatureSet(BuildTargetGroup.Standalone, k_TestFeatureSetId, false);
|
||||
AssertAllFeatures(BuildTargetGroup.Standalone, FeatureDisabled);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DisableSharedFeaturesLeaveSharedFeaturesEnabled()
|
||||
{
|
||||
// Ensable all WSA feature sets and make sure only the WSA feature set features are enabled
|
||||
EnableFeatureSets(BuildTargetGroup.WSA, enabled: true);
|
||||
|
||||
AssertOnlyFeatures(BuildTargetGroup.WSA, new string[]
|
||||
{
|
||||
MicrosoftHandInteraction.featureId,
|
||||
EyeGazeInteraction.featureId,
|
||||
}, FeatureEnabled);
|
||||
|
||||
// Disable the feature seth with both features set as required
|
||||
EnableFeatureSet(BuildTargetGroup.WSA, k_TestFeatureSetIdTwo, enabled: false);
|
||||
AssertOnlyFeatures(BuildTargetGroup.WSA, new string[]
|
||||
{
|
||||
MicrosoftHandInteraction.featureId,
|
||||
}, FeatureEnabled);
|
||||
|
||||
// Disable all WSA feature sets and make sure all features are disabled
|
||||
EnableFeatureSets(BuildTargetGroup.WSA, enabled: false);
|
||||
AssertAllFeatures(BuildTargetGroup.WSA, FeatureDisabled);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DisableSharedFeaturesLeaveOthersFeaturesEnabled()
|
||||
{
|
||||
string[] allFeatureIds = new string[]
|
||||
{
|
||||
MicrosoftHandInteraction.featureId,
|
||||
EyeGazeInteraction.featureId,
|
||||
MicrosoftMotionControllerProfile.featureId,
|
||||
};
|
||||
|
||||
string[] otherFeatureIds = new string[]
|
||||
{
|
||||
MicrosoftMotionControllerProfile.featureId,
|
||||
};
|
||||
|
||||
EnableFeatureInfos(BuildTargetGroup.WSA, otherFeatureIds, true);
|
||||
|
||||
// Enable the second feature set and ensure that only features in the `all` list are enabled
|
||||
var featureSetToEnable = GetFeatureSetInfoWithId(BuildTargetGroup.WSA, k_TestFeatureSetIdTwo);
|
||||
EnableFeatureSet(BuildTargetGroup.WSA, featureSetToEnable.featureSetId, true);
|
||||
AssertOnlyFeatures(BuildTargetGroup.WSA, allFeatureIds, FeatureEnabled);
|
||||
|
||||
// Disable the second feature set and ensure only features in the `others` list are enabled
|
||||
var featureSetToDisable = GetFeatureSetInfoWithId(BuildTargetGroup.WSA, k_TestFeatureSetIdTwo);
|
||||
Assert.IsNotNull(featureSetToDisable);
|
||||
EnableFeatureSet(BuildTargetGroup.WSA, featureSetToDisable.featureSetId, enabled: false);
|
||||
AssertOnlyFeatures(BuildTargetGroup.WSA, otherFeatureIds, FeatureEnabled);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void EnablingFeatureSetEnabledDefaultFeatures()
|
||||
{
|
||||
var foundFeatureSet = GetFeatureSetInfoWithId(BuildTargetGroup.Standalone, k_TestFeatureSetIdFour);
|
||||
Assert.IsNotNull(foundFeatureSet);
|
||||
EnableFeatureSet(BuildTargetGroup.Standalone, foundFeatureSet.featureSetId, true);
|
||||
|
||||
// Ensure that only the non-optional features are enabled
|
||||
AssertOnlyFeatures(BuildTargetGroup.Standalone, foundFeatureSet.featureIds, (f) => f.Feature.enabled == !FeatureIsOptional(foundFeatureSet, f));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void EnablingFeatureSetLeavesOptionFeaturesEnabled()
|
||||
{
|
||||
// Enable the feature set
|
||||
var foundFeatureSet = GetFeatureSetInfoWithId(BuildTargetGroup.Standalone, k_TestFeatureSetIdFour);
|
||||
Assert.IsNotNull(foundFeatureSet);
|
||||
EnableFeatureSet(BuildTargetGroup.Standalone, foundFeatureSet.featureSetId, true);
|
||||
|
||||
// Ensure the Optional features are all disabled
|
||||
AssertAllFeatures(BuildTargetGroup.Standalone, (f) => !FeatureIsOptional(foundFeatureSet, f) || !f.Feature.enabled);
|
||||
|
||||
// Disable the feature set and ensure the optional features are disabled
|
||||
EnableFeatureSet(BuildTargetGroup.Standalone, foundFeatureSet.featureSetId, false);
|
||||
AssertAllFeatures(BuildTargetGroup.Standalone, (f) => !FeatureIsOptional(foundFeatureSet, f) || !f.Feature.enabled);
|
||||
|
||||
// Enable the optional features and the feature set and ensure the optional features are still enabled
|
||||
EnableFeatureInfos(BuildTargetGroup.Standalone, true, (f) => FeatureIsOptional(foundFeatureSet, f));
|
||||
EnableFeatureSet(BuildTargetGroup.Standalone, foundFeatureSet.featureSetId, enabled: true);
|
||||
AssertAllFeatures(BuildTargetGroup.Standalone, (f) => !FeatureIsOptional(foundFeatureSet, f) || f.Feature.enabled);
|
||||
|
||||
// Enable the feature set again and make sure the optional features are still enabled
|
||||
EnableFeatureSet(BuildTargetGroup.Standalone, foundFeatureSet.featureSetId, enabled: true);
|
||||
AssertAllFeatures(BuildTargetGroup.Standalone, (f) => !FeatureIsOptional(foundFeatureSet, f) || f.Feature.enabled);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DisablingFeatureSetLeavesDefaultFeaturesEnabled()
|
||||
{
|
||||
var foundFeatureSet = GetFeatureSetInfoWithId(BuildTargetGroup.Standalone, k_TestFeatureSetIdFour);
|
||||
Assert.IsNotNull(foundFeatureSet);
|
||||
EnableFeatureSet(BuildTargetGroup.Standalone, foundFeatureSet.featureSetId, true);
|
||||
|
||||
// Ensure that the only enabled features are the non optional features
|
||||
AssertAllFeatures(BuildTargetGroup.Standalone, foundFeatureSet.featureIds, (f) => f.Feature.enabled == !FeatureIsOptional(foundFeatureSet, f));
|
||||
|
||||
// Disabling the feature set should disable the required components but not the default ones
|
||||
EnableFeatureSet(BuildTargetGroup.Standalone, foundFeatureSet.featureSetId, enabled: false);
|
||||
AssertAllFeatures(BuildTargetGroup.Standalone, foundFeatureSet.requiredFeatureIds, FeatureDisabled);
|
||||
AssertAllFeatures(BuildTargetGroup.Standalone, foundFeatureSet.defaultFeatureIds, FeatureEnabled);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DisablingFeatureSetLeavesDisabledDefaultFeaturesDisabled()
|
||||
{
|
||||
var buildTargetGroup = BuildTargetGroup.Standalone;
|
||||
var foundFeatureSet = GetFeatureSetInfoWithId(buildTargetGroup, k_TestFeatureSetIdFour);
|
||||
Assert.IsNotNull(foundFeatureSet);
|
||||
|
||||
EnableFeatureSet(buildTargetGroup, foundFeatureSet.featureSetId, enabled: true);
|
||||
|
||||
// Ensure that only the non optional features are enabled
|
||||
AssertOnlyFeatures(buildTargetGroup, foundFeatureSet.featureIds, (f) => f.Feature.enabled == !FeatureIsOptional(foundFeatureSet, f));
|
||||
|
||||
// Disable all features in the default feature list
|
||||
EnableFeatureInfos(buildTargetGroup, foundFeatureSet.defaultFeatureIds, enable: false);
|
||||
AssertAllFeatures(buildTargetGroup, foundFeatureSet.defaultFeatureIds, FeatureDisabled);
|
||||
|
||||
// Ensure that all features in the required list are enabled and that all features in the default features list are disabled
|
||||
AssertAllFeatures(buildTargetGroup, foundFeatureSet.requiredFeatureIds, FeatureEnabled);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CanNotChangeEnabledStateOfRequiredFeature()
|
||||
{
|
||||
OpenXRFeatureSetManager.activeBuildTarget = BuildTargetGroup.Standalone;
|
||||
|
||||
var foundFeatureSet = GetFeatureSetInfoWithId(BuildTargetGroup.Standalone, k_TestFeatureSetIdFour);
|
||||
Assert.IsNotNull(foundFeatureSet);
|
||||
|
||||
var featureInfos = GetFeatureInfos(BuildTargetGroup.Standalone, foundFeatureSet.requiredFeatureIds);
|
||||
foreach (var featureInfo in featureInfos)
|
||||
{
|
||||
AssertFeatureEnabled(featureInfo, false);
|
||||
featureInfo.Feature.enabled = true;
|
||||
AssertFeatureEnabled(featureInfo, true);
|
||||
featureInfo.Feature.enabled = false;
|
||||
AssertFeatureEnabled(featureInfo, false);
|
||||
}
|
||||
|
||||
EnableFeatureSet(BuildTargetGroup.Standalone, foundFeatureSet.featureSetId, enabled: true);
|
||||
|
||||
OpenXRFeature.canSetFeatureDisabled = OpenXRFeatureSetManager.CanFeatureBeDisabled;
|
||||
|
||||
foreach (var featureInfo in featureInfos)
|
||||
{
|
||||
AssertFeatureEnabled(featureInfo, true);
|
||||
featureInfo.Feature.enabled = false;
|
||||
AssertFeatureEnabled(featureInfo, true);
|
||||
}
|
||||
|
||||
EnableFeatureSet(BuildTargetGroup.Standalone, foundFeatureSet.featureSetId, enabled: false);
|
||||
|
||||
foreach (var featureInfo in featureInfos)
|
||||
{
|
||||
AssertFeatureEnabled(featureInfo, false);
|
||||
featureInfo.Feature.enabled = true;
|
||||
AssertFeatureEnabled(featureInfo, true);
|
||||
featureInfo.Feature.enabled = false;
|
||||
AssertFeatureEnabled(featureInfo, false);
|
||||
}
|
||||
|
||||
OpenXRFeatureSetManager.activeBuildTarget = BuildTargetGroup.Unknown;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 328562bda27130c4a853afe94025c8a6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
122
Packages/com.unity.xr.openxr/Tests/Editor/FeatureTests.cs
Normal file
122
Packages/com.unity.xr.openxr/Tests/Editor/FeatureTests.cs
Normal file
@@ -0,0 +1,122 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using NUnit.Framework;
|
||||
using UnityEditor.Build.Reporting;
|
||||
using UnityEditor.XR.OpenXR.Features;
|
||||
using UnityEngine.XR.OpenXR.Features;
|
||||
using UnityEngine.XR.OpenXR.Features.Interactions;
|
||||
using UnityEngine.XR.OpenXR.Features.Mock;
|
||||
using Assert = UnityEngine.Assertions.Assert;
|
||||
using UnityEngine.XR.OpenXR.Tests;
|
||||
using static UnityEditor.XR.OpenXR.Tests.OpenXREditorTestHelpers;
|
||||
|
||||
namespace UnityEditor.XR.OpenXR.Tests
|
||||
{
|
||||
internal class FeatureTests : OpenXRLoaderSetup
|
||||
{
|
||||
[Test]
|
||||
public void EnableFeatures()
|
||||
{
|
||||
var featureInfos = GetFeatureInfos(BuildTargetGroup.Standalone);
|
||||
featureInfos.SingleOrDefault(ext => ext.Attribute.UiName == "Mock Runtime").Feature.enabled = true;
|
||||
Assert.IsTrue(MockRuntime.Instance.enabled);
|
||||
|
||||
featureInfos.SingleOrDefault(ext => ext.Attribute.UiName == "Mock Runtime").Feature.enabled = false;
|
||||
Assert.IsFalse(MockRuntime.Instance.enabled);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CheckDefaultValues()
|
||||
{
|
||||
var featureInfos = GetFeatureInfos(BuildTargetGroup.Standalone);
|
||||
var mockExtInfo = featureInfos.SingleOrDefault(ext => ext.Attribute.UiName == "Mock Runtime");
|
||||
|
||||
Assert.AreEqual(mockExtInfo.Attribute.UiName, mockExtInfo.Feature.nameUi);
|
||||
Assert.AreEqual(mockExtInfo.Attribute.Version, mockExtInfo.Feature.version);
|
||||
Assert.AreEqual(mockExtInfo.Attribute.OpenxrExtensionStrings, mockExtInfo.Feature.openxrExtensionStrings);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ValidationError()
|
||||
{
|
||||
bool errorFixed = false;
|
||||
|
||||
// Set up a validation check ...
|
||||
MockRuntime.Instance.TestCallback = (s, o) =>
|
||||
{
|
||||
if (s == "GetValidationChecks")
|
||||
{
|
||||
var validationChecks = o as List<OpenXRFeature.ValidationRule>;
|
||||
validationChecks?.Add(new OpenXRFeature.ValidationRule
|
||||
{
|
||||
message = "Mock Validation Fail",
|
||||
checkPredicate = () => errorFixed,
|
||||
fixIt = () => errorFixed = true,
|
||||
error = true
|
||||
});
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
// Try to build the player ...
|
||||
var report = zBuildHookTests.BuildMockPlayer();
|
||||
|
||||
// It will fail because of the above validation issue ...
|
||||
Assert.AreEqual(BuildResult.Failed, report.summary.result);
|
||||
|
||||
// There's one validation issue ...
|
||||
var validationIssues = new List<OpenXRFeature.ValidationRule>();
|
||||
OpenXRProjectValidation.GetCurrentValidationIssues(validationIssues, BuildTargetGroup.Standalone);
|
||||
Assert.AreEqual(1, validationIssues.Count);
|
||||
|
||||
// Fix it ...
|
||||
Assert.IsFalse(errorFixed);
|
||||
validationIssues[0].fixIt.Invoke();
|
||||
Assert.IsTrue(errorFixed);
|
||||
|
||||
// Now there's zero validation issues ...
|
||||
OpenXRProjectValidation.GetCurrentValidationIssues(validationIssues, BuildTargetGroup.Standalone);
|
||||
Assert.AreEqual(0, validationIssues.Count);
|
||||
|
||||
// Close the validation window ...
|
||||
OpenXRProjectValidationRulesSetup.CloseWindow();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetFeatureByFeatureId()
|
||||
{
|
||||
var feature = FeatureHelpers.GetFeatureWithIdForActiveBuildTarget(MockRuntime.featureId);
|
||||
Assert.IsNotNull(feature);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetFeatureByUnknownFeatureIdReturnsNull()
|
||||
{
|
||||
var feature = FeatureHelpers.GetFeatureWithIdForActiveBuildTarget("some.unknown.feature.id");
|
||||
Assert.IsNull(feature);
|
||||
|
||||
feature = FeatureHelpers.GetFeatureWithIdForActiveBuildTarget("");
|
||||
Assert.IsNull(feature);
|
||||
|
||||
feature = FeatureHelpers.GetFeatureWithIdForActiveBuildTarget(null);
|
||||
Assert.IsNull(feature);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetFeaturesWithIdsReturnsFeatures()
|
||||
{
|
||||
var featureIds = new string[] { MockRuntime.featureId, EyeGazeInteraction.featureId };
|
||||
var features = FeatureHelpers.GetFeaturesWithIdsForActiveBuildTarget(featureIds);
|
||||
Assert.IsNotNull(features);
|
||||
Assert.IsTrue(features.Length == 2);
|
||||
|
||||
var expectedTypes = new Type[] { typeof(MockRuntime), typeof(EyeGazeInteraction) };
|
||||
foreach (var feature in features)
|
||||
{
|
||||
Assert.IsTrue(Array.IndexOf(expectedTypes, feature.GetType()) > -1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7a788d71a20869d4db1291a18e647426
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2bcf50ca7a335ea40b37e3bcf05eea52
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,51 @@
|
||||
using NUnit.Framework;
|
||||
using UnityEngine.XR.OpenXR.Features;
|
||||
using UnityEngine.XR.OpenXR.Features.Mock;
|
||||
using UnityEngine.XR.OpenXR.Tests;
|
||||
using Assert = UnityEngine.Assertions.Assert;
|
||||
|
||||
namespace UnityEditor.XR.OpenXR.Tests
|
||||
{
|
||||
internal class OpenXRCallbackTests : OpenXRLoaderSetup
|
||||
{
|
||||
[Test]
|
||||
public void InstanceCreated()
|
||||
{
|
||||
bool instanceCreated = false;
|
||||
MockRuntime.Instance.TestCallback = (methodName, param) =>
|
||||
{
|
||||
if (methodName == nameof(OpenXRFeature.OnInstanceCreate))
|
||||
{
|
||||
instanceCreated = true;
|
||||
Assert.AreEqual(10, (ulong)param);
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
AddExtension(MockRuntime.XR_UNITY_mock_test);
|
||||
|
||||
base.InitializeAndStart();
|
||||
|
||||
Assert.IsTrue(instanceCreated);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SessionCreated()
|
||||
{
|
||||
bool sessionCreated = false;
|
||||
MockRuntime.Instance.TestCallback = (methodName, param) =>
|
||||
{
|
||||
if (methodName == nameof(OpenXRFeature.OnSessionCreate))
|
||||
{
|
||||
sessionCreated = true;
|
||||
Assert.AreEqual(3, (ulong)param);
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
base.InitializeAndStart();
|
||||
|
||||
Assert.IsTrue(sessionCreated);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8dc9dc15a0724a46ba08d4aecabca976
|
||||
timeCreated: 1586986782
|
||||
@@ -0,0 +1,62 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using UnityEngine.XR.OpenXR;
|
||||
using UnityEngine.XR.OpenXR.Tests;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using UnityEngine.XR;
|
||||
using UnityEngine.TestTools;
|
||||
using UnityEngine.Scripting;
|
||||
using Assert = UnityEngine.Assertions.Assert;
|
||||
|
||||
namespace UnityEditor.XR.OpenXR.Tests
|
||||
{
|
||||
#if false // Tests are temporarily disabled due to an issue with [BeforeTest] being called twice during a domain reload
|
||||
|
||||
/// <summary>
|
||||
/// Tests domain reloads within OpenXR. Note that his class was named with two z's in front
|
||||
/// because the domain reloads during this test were causing other tests to fail that ran after it.
|
||||
/// </summary>
|
||||
internal class zzOpenXRDomainReloadTests : OpenXRLoaderSetup
|
||||
{
|
||||
[UnityTest]
|
||||
public IEnumerator AfterInitialize()
|
||||
{
|
||||
MockRuntimeFeature.Instance.TestCallback = (methodName, param) => true;
|
||||
|
||||
Assert.IsNull(OpenXRLoaderBase.Instance);
|
||||
|
||||
base.InitializeAndStart();
|
||||
|
||||
// Perform a domain reload and wait for it to complete
|
||||
EditorUtility.RequestScriptReload();
|
||||
yield return new WaitForDomainReload();
|
||||
|
||||
Assert.IsNotNull(OpenXRLoaderBase.Instance);
|
||||
|
||||
base.StopAndShutdown();
|
||||
|
||||
Assert.IsNull(OpenXRLoaderBase.Instance);
|
||||
|
||||
yield return null;
|
||||
}
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator BeforeInitialize()
|
||||
{
|
||||
MockRuntimeFeature.Instance.TestCallback = (methodName, param) => true;
|
||||
|
||||
// Perform a domain reload and wait for it to complete
|
||||
EditorUtility.RequestScriptReload();
|
||||
yield return new WaitForDomainReload();
|
||||
|
||||
base.InitializeAndStart();
|
||||
base.StopAndShutdown();
|
||||
|
||||
yield return null;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 742a28a82ea69ef4fba68040fee09aac
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,230 @@
|
||||
using NUnit.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEditor.XR.OpenXR.Features;
|
||||
using static UnityEditor.XR.OpenXR.Features.OpenXRFeatureSetManager;
|
||||
|
||||
namespace UnityEditor.XR.OpenXR.Tests
|
||||
{
|
||||
internal static class OpenXREditorTestHelpers
|
||||
{
|
||||
public delegate bool FeatureInfoPredicate(FeatureHelpersInternal.FeatureInfo featureInfo);
|
||||
|
||||
public static bool FeatureEnabled(FeatureHelpersInternal.FeatureInfo featureInfo) => featureInfo.Feature.enabled;
|
||||
public static bool FeatureDisabled(FeatureHelpersInternal.FeatureInfo featureInfo) => !featureInfo.Feature.enabled;
|
||||
|
||||
private static Dictionary<BuildTargetGroup, FeatureHelpersInternal.FeatureInfo[]> s_FeatureInfos;
|
||||
|
||||
private static BuildTargetGroup[] s_BuildTargetGroups =
|
||||
((BuildTargetGroup[])Enum.GetValues(typeof(BuildTargetGroup))).Distinct().ToArray();
|
||||
|
||||
/// <summary>
|
||||
/// Return the distinct list of build target groups to test
|
||||
/// </summary>
|
||||
public static BuildTargetGroup[] GetBuildTargetGroups() => s_BuildTargetGroups;
|
||||
|
||||
/// <summary>
|
||||
/// Clear the FeatureInfo cache
|
||||
/// </summary>
|
||||
public static void ClearFeatureInfos()
|
||||
{
|
||||
s_FeatureInfos = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper function to retrieve the cached FeatureInfos for a given build target group
|
||||
/// </summary>
|
||||
/// <param name="buildTargetGroup">Build target group</param>
|
||||
/// <returns>Array of FeatureInfos for the build target group</returns>
|
||||
public static FeatureHelpersInternal.FeatureInfo[] GetFeatureInfos(BuildTargetGroup buildTargetGroup)
|
||||
{
|
||||
if (null == s_FeatureInfos)
|
||||
s_FeatureInfos = new Dictionary<BuildTargetGroup, FeatureHelpersInternal.FeatureInfo[]>();
|
||||
|
||||
if (!s_FeatureInfos.TryGetValue(buildTargetGroup, out var featureInfos))
|
||||
{
|
||||
featureInfos = FeatureHelpersInternal.GetAllFeatureInfo(buildTargetGroup).Features.ToArray();
|
||||
s_FeatureInfos[buildTargetGroup] = featureInfos;
|
||||
}
|
||||
|
||||
return featureInfos;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper function to retrieve a subset of FeatureInfos in <paramref name="buildTargetGroup"/>.
|
||||
/// </summary>
|
||||
/// <param name="buildTargetGroup">Build target group</param>
|
||||
/// <param name="featureIds">Specific feature identifiers to retrieve</param>
|
||||
/// <returns>Array of FeatureInfos matching request</returns>
|
||||
public static FeatureHelpersInternal.FeatureInfo[] GetFeatureInfos(BuildTargetGroup buildTargetGroup, string[] featureIds)
|
||||
{
|
||||
return GetFeatureInfos(buildTargetGroup).Where(f => featureIds.Contains(f.Attribute.FeatureId)).ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enable or Disable a feature set
|
||||
/// </summary>
|
||||
/// <param name="buildTargetGroup">Build target group containing feature set</param>
|
||||
/// <param name="featureSetId">Identifier of feature</param>
|
||||
/// <param name="enabled">True to enable, false to disable</param>
|
||||
/// <param name="changed">True if the FeatureSet enabled state should be considered changed</param>
|
||||
/// <param name="commit">True if the FeatureSet enabled state should be automatically commmitted with `SetFeaturesFromEnabledFeatureSets`</param>
|
||||
public static void EnableFeatureSet(BuildTargetGroup buildTargetGroup, string featureSetId, bool enabled = true, bool changed = true, bool commit = true)
|
||||
{
|
||||
var featureSetInfo = GetFeatureSetInfoWithId(buildTargetGroup, featureSetId);
|
||||
Assert.IsNotNull(featureSetInfo, $"FeatureSetInfo '{featureSetId}' not found ");
|
||||
|
||||
featureSetInfo.isEnabled = enabled;
|
||||
featureSetInfo.wasEnabled = changed ? !enabled : enabled;
|
||||
|
||||
if (commit)
|
||||
SetFeaturesFromEnabledFeatureSets(buildTargetGroup);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enable/Disable all feature sets for <paramref name="buildTargetGroup"/>
|
||||
/// </summary>
|
||||
/// <param name="buildTargetGroup">Build Target Group</param>
|
||||
/// <param name="enabled">Enabled state</param>
|
||||
/// <param name="changed">True if the feature set should be marked as changed, false if not</param>
|
||||
public static void EnableFeatureSets(BuildTargetGroup buildTargetGroup, bool enabled, bool changed = true, bool commit = true)
|
||||
{
|
||||
foreach (var featureSetInfo in FeatureSetInfosForBuildTarget(buildTargetGroup))
|
||||
{
|
||||
featureSetInfo.isEnabled = enabled;
|
||||
featureSetInfo.wasEnabled = changed ? !enabled : enabled;
|
||||
}
|
||||
|
||||
if (commit)
|
||||
SetFeaturesFromEnabledFeatureSets(buildTargetGroup);
|
||||
}
|
||||
|
||||
public static void EnableFeatureInfos(BuildTargetGroup buildTargetGroup, string[] featureIds, bool enable, FeatureInfoPredicate predicate = null)
|
||||
{
|
||||
foreach (var featureInfo in GetFeatureInfos(buildTargetGroup, featureIds))
|
||||
{
|
||||
if (null == predicate || predicate(featureInfo))
|
||||
featureInfo.Feature.enabled = enable;
|
||||
}
|
||||
}
|
||||
|
||||
public static void EnableFeatureInfos(BuildTargetGroup buildTargetGroup, bool enable, FeatureInfoPredicate predicate = null)
|
||||
{
|
||||
foreach (var featureInfo in GetFeatureInfos(buildTargetGroup))
|
||||
{
|
||||
if (null == predicate || predicate(featureInfo))
|
||||
featureInfo.Feature.enabled = enable;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds a comma separated list of features that pass the <paramref name="check"/> within <paramref name="featureInfos"/>
|
||||
/// </summary>
|
||||
/// <param name="featureInfos">Feature infos</param>
|
||||
/// <param name="check">Delegate used to check status</param>
|
||||
/// <param name="exclude">Optional list of feature identifiers exclude</param>
|
||||
/// <returns>Comma separated list of features</returns>
|
||||
public static string FeaturesToString(FeatureHelpersInternal.FeatureInfo[] featureInfos, FeatureInfoPredicate check, string[] exclude = null)
|
||||
{
|
||||
return string.Join(",", featureInfos
|
||||
.Where(f => (exclude == null || !exclude.Contains(f.Attribute.FeatureId)) && check(f))
|
||||
.Select(f => f.Attribute.FeatureId));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds a comma separated list of features from <paramref name="featureIds"/> in <paramref name="buildTargetGroup"/> that pass the <paramref name="check"/>
|
||||
/// </summary>
|
||||
/// <param name="buildTargetGroup">Build Target Group</param>
|
||||
/// <param name="featureIds">Feature ids to include</param>
|
||||
/// <param name="check">Delegate used to check status</param>
|
||||
/// <returns>Comma separated list of features</returns>
|
||||
public static string FeaturesToString(BuildTargetGroup buildTargetGroup, string[] featureIds, FeatureInfoPredicate check)
|
||||
{
|
||||
return FeaturesToString(GetFeatureInfos(buildTargetGroup).Where(f => featureIds.Contains(f.Attribute.FeatureId)).ToArray(), check);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds a comma separated list of features identifier that pass the <paramref name="check"/> within <paramref name="buildTargetGroup"/>
|
||||
/// </summary>
|
||||
/// <param name="buildTargetGroup">Build Target Group</param>
|
||||
/// <param name="check">Delegate used to check status</param>
|
||||
/// <param name="exclude">Optional list of feature identifiers to exclude from the list</param>
|
||||
/// <returns>Comma separated list of features</returns>
|
||||
public static string FeaturesToString(BuildTargetGroup buildTargetGroup, FeatureInfoPredicate check, string[] exclude = null)
|
||||
{
|
||||
return FeaturesToString(GetFeatureInfos(buildTargetGroup), check, exclude: exclude);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if all features for the <paramref name="buildTargetGroup"/> pass the <paramref name="check"/>.
|
||||
/// </summary>
|
||||
/// <param name="buildTargetGroup">Build Target Group</param>
|
||||
/// <param name="check">Delegate used to check status</param>
|
||||
/// <returns>True if the check passes, false if not</returns>
|
||||
public static bool CheckAllFeatures(BuildTargetGroup buildTargetGroup, FeatureInfoPredicate check)
|
||||
{
|
||||
return GetFeatureInfos(buildTargetGroup).All(f => check(f));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check that all features in <paramref name="featureIds"/> in <paramref name="buildTargetGroup"/> match the pass the <paramref name="check"/>.
|
||||
/// </summary>
|
||||
/// <param name="buildTargetGroup">Build target group</param>
|
||||
/// <param name="featureIds">Features to check</param>
|
||||
/// <param name="check">Delegate used to check status</param>
|
||||
/// <returns>True if the check passes, false if not</returns>
|
||||
public static bool CheckAllFeatures(BuildTargetGroup buildTargetGroup, string[] featureIds, FeatureInfoPredicate check)
|
||||
{
|
||||
return GetFeatureInfos(buildTargetGroup).All(f => !featureIds.Contains(f.Attribute.FeatureId) || check(f));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check that only features in <paramref name="featureIds"/> for <paramref name="buildTargetGroup"/> pass the <paramref name="check"/>.
|
||||
/// </summary>
|
||||
/// <param name="buildTargetGroup">Build target group</param>
|
||||
/// <param name="featureIds">Features to check</param>
|
||||
/// <param name="check">Delegate used to check a FeatureInfo</param>
|
||||
/// <returns>True if the check passes, false if not</returns>
|
||||
public static bool CheckOnlyFeatures(BuildTargetGroup buildTargetGroup, string[] featureIds, FeatureInfoPredicate check)
|
||||
{
|
||||
return GetFeatureInfos(buildTargetGroup).All(f => featureIds.Contains(f.Attribute.FeatureId) == check(f));
|
||||
}
|
||||
|
||||
private static string AssertFeaturesMessage(string features) =>
|
||||
$"The following features failed the check: {features}";
|
||||
|
||||
public static void AssertOnlyFeatures(BuildTargetGroup buildTargetGroup, string[] featureIds, FeatureInfoPredicate check)
|
||||
{
|
||||
Assert.IsTrue(
|
||||
CheckOnlyFeatures(buildTargetGroup, featureIds, check),
|
||||
AssertFeaturesMessage(FeaturesToString(buildTargetGroup, featureIds, (f) => !check(f))));
|
||||
}
|
||||
|
||||
public static void AssertAllFeatures(BuildTargetGroup buildTargetGroup, string[] featureIds, FeatureInfoPredicate check)
|
||||
{
|
||||
Assert.IsTrue(
|
||||
CheckAllFeatures(buildTargetGroup, featureIds, check),
|
||||
AssertFeaturesMessage(FeaturesToString(buildTargetGroup, featureIds, (f) => !check(f))));
|
||||
}
|
||||
|
||||
public static void AssertAllFeatures(BuildTargetGroup buildTargetGroup, FeatureInfoPredicate check)
|
||||
{
|
||||
Assert.IsTrue(
|
||||
CheckAllFeatures(buildTargetGroup, check),
|
||||
AssertFeaturesMessage(FeaturesToString(buildTargetGroup, (f) => !check(f))));
|
||||
}
|
||||
|
||||
public static void AssertFeatureEnabled(FeatureHelpersInternal.FeatureInfo featureInfo, bool enabled = true)
|
||||
{
|
||||
Assert.IsTrue(featureInfo.Feature.enabled == enabled, $"{featureInfo.Attribute.FeatureId} should be {(enabled ? "enabled" : "disabled")}");
|
||||
}
|
||||
|
||||
public static bool FeatureIsOptional(FeatureSetInfo featureSet, FeatureHelpersInternal.FeatureInfo feature)
|
||||
{
|
||||
return Array.IndexOf(featureSet.featureIds, feature.Attribute.FeatureId) > -1 &&
|
||||
(featureSet.requiredFeatureIds == null || Array.IndexOf(featureSet.requiredFeatureIds, feature.Attribute.FeatureId) == -1) &&
|
||||
(featureSet.defaultFeatureIds == null || Array.IndexOf(featureSet.defaultFeatureIds, feature.Attribute.FeatureId) == -1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f87eee2b71c3bf24aa0e63794ac22ae5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,39 @@
|
||||
using System;
|
||||
using NUnit.Framework;
|
||||
using System.Reflection;
|
||||
using System.Text.RegularExpressions;
|
||||
using UnityEditor.XR.OpenXR.Features;
|
||||
using UnityEngine.XR.OpenXR.Features.Interactions;
|
||||
using UnityEngine.XR.OpenXR;
|
||||
|
||||
namespace UnityEditor.XR.OpenXR.Tests
|
||||
{
|
||||
internal class OpenXREditorTests
|
||||
{
|
||||
[Test]
|
||||
public void DocumentationVersion()
|
||||
{
|
||||
var version = PackageManager.PackageInfo.FindForAssembly(typeof(OpenXREditorTests).Assembly)?.version;
|
||||
var majorminor = "@" + OpenXRFeatureAttribute.k_PackageVersionRegex.Match(version).Groups[1].Value + "/";
|
||||
UnityEngine.Debug.Log(typeof(KHRSimpleControllerProfile).GetCustomAttribute<OpenXRFeatureAttribute>().InternalDocumentationLink);
|
||||
Assert.IsTrue(typeof(KHRSimpleControllerProfile).GetCustomAttribute<OpenXRFeatureAttribute>().InternalDocumentationLink.Contains(majorminor));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PluginVersion()
|
||||
{
|
||||
var version = PackageManager.PackageInfo.FindForAssembly(typeof(OpenXREditorTests).Assembly)?.version;
|
||||
System.Text.RegularExpressions.Regex ReleaseVersion = new System.Text.RegularExpressions.Regex(@"^(\d+\.\d+\.\d+)$");
|
||||
//check for release build provider version number matches package version
|
||||
if (ReleaseVersion.IsMatch(version))
|
||||
{
|
||||
var tag = OpenXRRuntime.pluginVersion;
|
||||
Assert.AreEqual(0, String.Compare(version, tag), "Tag in github must match the package version number.");
|
||||
return;
|
||||
}
|
||||
//check for non-release build package version number supposed to be x.x.x-pre.x(pre-release) or x.x.x-exp.x(experimental)
|
||||
System.Text.RegularExpressions.Regex PreviewVersion = new System.Text.RegularExpressions.Regex(@"^(\d+\.\d+\.\d+\-\w+\.\d+)$");
|
||||
Assert.IsTrue(PreviewVersion.IsMatch(version), "Wrong package version format! Non-release branch should follow x.x.x-pre.x(pre-release) or x.x.x-exp.x(experimental)");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2c99482b3559f1f47babd8d8fec8a820
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,81 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using UnityEngine.InputSystem;
|
||||
using UnityEngine.XR.OpenXR;
|
||||
using UnityEngine.XR.OpenXR.Features.Interactions;
|
||||
using UnityEngine.XR.OpenXR.Tests;
|
||||
using Assert = UnityEngine.Assertions.Assert;
|
||||
|
||||
namespace UnityEditor.XR.OpenXR.Tests
|
||||
{
|
||||
internal class OpenXRInputEditorTests : OpenXRInputTestsBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Tests whether or not the device layout for an interaction feature is registered/unregistered
|
||||
/// when the feature is enabled/disabled
|
||||
/// </summary>
|
||||
#if !INPUT_SYSTEM_BINDING_VALIDATOR
|
||||
[Test]
|
||||
public void DeviceLayoutRegistration([ValueSource(nameof(s_InteractionFeatureLayouts))] (Type featureType, Type layoutType, string layoutNameOverride) interactionFeature)
|
||||
{
|
||||
var layoutName = interactionFeature.layoutNameOverride ?? interactionFeature.layoutType.Name;
|
||||
|
||||
// Make sure the layout is not registered as it would give the test a false positive
|
||||
InputSystem.RemoveLayout(layoutName);
|
||||
Assert.IsFalse(IsLayoutRegistered(layoutName), "Layout is still registered, test will give a false positive");
|
||||
|
||||
// Enabling the feature should register the layout
|
||||
EnableFeature(interactionFeature.featureType);
|
||||
Assert.IsTrue(IsLayoutRegistered(layoutName), "Layout was not registered by enabling the feature");
|
||||
|
||||
// When an interaction feature is disabled its layout should be disable as well
|
||||
EnableFeature(interactionFeature.featureType, false);
|
||||
Assert.IsFalse(IsLayoutRegistered(layoutName), "Layout was not unregistered by the interaction feature");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests that interaction features enabled in multiple build targets properly registers and unregisters
|
||||
/// the device layout depending on whether the feature is enabled in at least one build target.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void InteractionFeatureLayoutRegistration()
|
||||
{
|
||||
var packageSettings = OpenXRSettings.GetPackageSettings();
|
||||
Assert.IsNotNull(packageSettings);
|
||||
|
||||
// Ignore the test if there is not more than 1 build target.
|
||||
var features = packageSettings.GetFeatures<OculusTouchControllerProfile>().Select(f => f.feature).ToArray();
|
||||
if (features.Length < 2)
|
||||
return;
|
||||
|
||||
// Disable all of the oculus interaction features
|
||||
foreach (var feature in features)
|
||||
{
|
||||
feature.enabled = false;
|
||||
}
|
||||
|
||||
// Make sure the oculus device layout is not registered
|
||||
NUnit.Framework.Assert.Throws(typeof(ArgumentException), () => UnityEngine.InputSystem.InputSystem.LoadLayout<OculusTouchControllerProfile.OculusTouchController>());
|
||||
|
||||
// Enable one of the features and make sure the layout is registered
|
||||
features[0].enabled = true;
|
||||
NUnit.Framework.Assert.DoesNotThrow(() => UnityEngine.InputSystem.InputSystem.LoadLayout<OculusTouchControllerProfile.OculusTouchController>());
|
||||
NUnit.Framework.Assert.DoesNotThrow(() => UnityEngine.InputSystem.InputSystem.LoadLayout<OculusTouchControllerProfile.OculusTouchController>());
|
||||
|
||||
// Enable a second feature and make sure the layout is still enabled
|
||||
features[1].enabled = true;
|
||||
NUnit.Framework.Assert.DoesNotThrow(() => UnityEngine.InputSystem.InputSystem.LoadLayout<OculusTouchControllerProfile.OculusTouchController>());
|
||||
|
||||
// Disable the first feature and make sure the layout is still enabled
|
||||
features[0].enabled = false;
|
||||
NUnit.Framework.Assert.DoesNotThrow(() => UnityEngine.InputSystem.InputSystem.LoadLayout<OculusTouchControllerProfile.OculusTouchController>());
|
||||
|
||||
// Disable the second feature and make sure the layout is no longer
|
||||
features[1].enabled = false;
|
||||
NUnit.Framework.Assert.Throws(typeof(ArgumentException), () => UnityEngine.InputSystem.InputSystem.LoadLayout<OculusTouchControllerProfile.OculusTouchController>());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ee1a4eb1841c4a40a7e4a76be579ca3e
|
||||
timeCreated: 1622132012
|
||||
@@ -0,0 +1,142 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using UnityEngine;
|
||||
using UnityEngine.InputSystem;
|
||||
using UnityEngine.XR.OpenXR;
|
||||
using UnityEngine.XR.OpenXR.Features.Interactions;
|
||||
using UnityEngine.XR.OpenXR.Tests;
|
||||
using Assert = UnityEngine.Assertions.Assert;
|
||||
|
||||
namespace UnityEditor.XR.OpenXR.Tests
|
||||
{
|
||||
internal class OpenXRRuntimeSelectorTests : OpenXRInputTestsBase
|
||||
{
|
||||
[Test]
|
||||
public void NoAvailableRuntimesTest()
|
||||
{
|
||||
List<OpenXRRuntimeSelector.RuntimeDetector> detectorList = OpenXRRuntimeSelector.GenerateRuntimeDetectorList();
|
||||
Assert.IsTrue(detectorList.Count > 0);
|
||||
Assert.IsTrue(detectorList[0] is OpenXRRuntimeSelector.SystemDefault, "First choice should always be SystemDefault");
|
||||
Assert.IsTrue(detectorList[detectorList.Count - 1] is OpenXRRuntimeSelector.OtherRuntime, "Last choice should always be Other");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void BuiltInRuntimesAsAvailableRuntimesTest()
|
||||
{
|
||||
// Simulate what happens if the AvailableRuntimes registry key is empty.
|
||||
// WindowsMR, SteamVR, and Oculus should all be added to the list.
|
||||
Dictionary<string, int> runtimePathToValue = new Dictionary<string, int>();
|
||||
List<OpenXRRuntimeSelector.RuntimeDetector> detectorList = OpenXRRuntimeSelector.GenerateRuntimeDetectorList(runtimePathToValue);
|
||||
Assert.IsTrue(detectorList.Count > 0);
|
||||
Assert.IsTrue(detectorList[0] is OpenXRRuntimeSelector.SystemDefault);
|
||||
Assert.IsTrue(detectorList[detectorList.Count - 1] is OpenXRRuntimeSelector.OtherRuntime);
|
||||
|
||||
bool foundWindowsMRDetector = false;
|
||||
bool foundSteamVRDetector = false;
|
||||
bool foundOculusDetector = false;
|
||||
foreach (var detector in detectorList)
|
||||
{
|
||||
foundWindowsMRDetector |= detector is OpenXRRuntimeSelector.WindowsMRDetector;
|
||||
foundSteamVRDetector |= detector is OpenXRRuntimeSelector.SteamVRDetector;
|
||||
foundOculusDetector |= detector is OpenXRRuntimeSelector.OculusDetector;
|
||||
}
|
||||
|
||||
Assert.IsTrue(foundWindowsMRDetector);
|
||||
Assert.IsTrue(foundSteamVRDetector);
|
||||
Assert.IsTrue(foundOculusDetector);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DiscoveredAvailableRuntimesTest()
|
||||
{
|
||||
OpenXRRuntimeSelector.WindowsMRDetector windowsMRDetector = new OpenXRRuntimeSelector.WindowsMRDetector();
|
||||
OpenXRRuntimeSelector.SteamVRDetector steamVRDetector = new OpenXRRuntimeSelector.SteamVRDetector();
|
||||
OpenXRRuntimeSelector.OculusDetector oculusDetector = new OpenXRRuntimeSelector.OculusDetector();
|
||||
string enabledRuntime = "enabledRuntime";
|
||||
string disabledRuntime = "disabledRuntime";
|
||||
|
||||
Dictionary<string, int> runtimePathToValue = new Dictionary<string, int>()
|
||||
{
|
||||
{ windowsMRDetector.jsonPath, 0},
|
||||
{ steamVRDetector.jsonPath, 1},
|
||||
{ enabledRuntime, 0 },
|
||||
{ disabledRuntime, 1 }
|
||||
};
|
||||
|
||||
List<OpenXRRuntimeSelector.RuntimeDetector> detectorList = OpenXRRuntimeSelector.GenerateRuntimeDetectorList(runtimePathToValue);
|
||||
Assert.IsTrue(detectorList.Count > 0);
|
||||
Assert.IsTrue(detectorList[0] is OpenXRRuntimeSelector.SystemDefault);
|
||||
Assert.IsTrue(detectorList[detectorList.Count - 1] is OpenXRRuntimeSelector.OtherRuntime);
|
||||
|
||||
HashSet<string> detectedJsons = new HashSet<string>();
|
||||
foreach (var detector in detectorList)
|
||||
{
|
||||
detectedJsons.Add(detector.jsonPath);
|
||||
}
|
||||
|
||||
Assert.IsTrue(detectedJsons.Contains(windowsMRDetector.jsonPath));
|
||||
Assert.IsFalse(detectedJsons.Contains(steamVRDetector.jsonPath));
|
||||
Assert.IsTrue(detectedJsons.Contains(oculusDetector.jsonPath));
|
||||
Assert.IsTrue(detectedJsons.Contains(enabledRuntime));
|
||||
Assert.IsFalse(detectedJsons.Contains(disabledRuntime));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestSelectOtherRuntime()
|
||||
{
|
||||
// Get the OtherRuntime choice (verify that it's the last detector)
|
||||
var runtimeDetector = OpenXRRuntimeSelector.RuntimeDetectors[OpenXRRuntimeSelector.RuntimeDetectors.Count - 1];
|
||||
OpenXRRuntimeSelector.OtherRuntime otherRuntime = runtimeDetector as OpenXRRuntimeSelector.OtherRuntime;
|
||||
Assert.IsTrue(otherRuntime != null);
|
||||
|
||||
// Save data that this test will modify to restore state after the test finishes.
|
||||
string previousOtherJsonPath = otherRuntime.jsonPath;
|
||||
string previousActiveRuntime = Environment.GetEnvironmentVariable(OpenXRRuntimeSelector.RuntimeDetector.k_RuntimeEnvKey);
|
||||
string previousSelectedRuntime = Environment.GetEnvironmentVariable(OpenXRRuntimeSelector.k_SelectedRuntimeEnvKey);
|
||||
|
||||
try
|
||||
{
|
||||
// Set the other runtime value and set this as the selected runtime.
|
||||
string jsonPath = "testJsonPath";
|
||||
otherRuntime.SetOtherRuntimeJsonPath(jsonPath);
|
||||
OpenXRRuntimeSelector.SetSelectedRuntime(jsonPath);
|
||||
|
||||
// Exit Edit Mode. Active runtime and Selected runtime should have the json path.
|
||||
OpenXRRuntimeSelector.ActivateOrDeactivateRuntime(PlayModeStateChange.ExitingEditMode);
|
||||
string activeRuntime = Environment.GetEnvironmentVariable(OpenXRRuntimeSelector.RuntimeDetector.k_RuntimeEnvKey);
|
||||
string selectedRuntime = Environment.GetEnvironmentVariable(OpenXRRuntimeSelector.k_SelectedRuntimeEnvKey);
|
||||
Assert.AreEqual(activeRuntime, jsonPath);
|
||||
Assert.AreEqual(selectedRuntime, jsonPath);
|
||||
|
||||
// Simulate a refresh of the runtime detectors when entering play mode.
|
||||
// The OtherRuntime should still have the jsonPath from before.
|
||||
OpenXRRuntimeSelector.RefreshRuntimeDetectorList();
|
||||
runtimeDetector = OpenXRRuntimeSelector.RuntimeDetectors[OpenXRRuntimeSelector.RuntimeDetectors.Count - 1];
|
||||
otherRuntime = runtimeDetector as OpenXRRuntimeSelector.OtherRuntime;
|
||||
Assert.AreEqual(otherRuntime.jsonPath, jsonPath);
|
||||
|
||||
// Enter Edit Mode. Active runtime should be cleared, selected runtime should still have the json path.
|
||||
OpenXRRuntimeSelector.ActivateOrDeactivateRuntime(PlayModeStateChange.EnteredEditMode);
|
||||
activeRuntime = Environment.GetEnvironmentVariable(OpenXRRuntimeSelector.RuntimeDetector.k_RuntimeEnvKey);
|
||||
selectedRuntime = Environment.GetEnvironmentVariable(OpenXRRuntimeSelector.k_SelectedRuntimeEnvKey);
|
||||
Assert.AreNotEqual(activeRuntime, jsonPath);
|
||||
Assert.AreEqual(selectedRuntime, jsonPath);
|
||||
|
||||
// Refresh again. OtherRuntime and selected runtime should still have the json path.
|
||||
OpenXRRuntimeSelector.RefreshRuntimeDetectorList();
|
||||
runtimeDetector = OpenXRRuntimeSelector.RuntimeDetectors[OpenXRRuntimeSelector.RuntimeDetectors.Count - 1];
|
||||
otherRuntime = runtimeDetector as OpenXRRuntimeSelector.OtherRuntime;
|
||||
Assert.AreEqual(otherRuntime.jsonPath, jsonPath);
|
||||
}
|
||||
finally
|
||||
{
|
||||
otherRuntime.SetOtherRuntimeJsonPath(previousOtherJsonPath);
|
||||
Environment.SetEnvironmentVariable(OpenXRRuntimeSelector.RuntimeDetector.k_RuntimeEnvKey, previousActiveRuntime);
|
||||
Environment.SetEnvironmentVariable(OpenXRRuntimeSelector.k_SelectedRuntimeEnvKey, previousSelectedRuntime);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 14582cceaceaa6949852ce064e85a84a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,75 @@
|
||||
using System;
|
||||
using NUnit.Framework;
|
||||
using System.Reflection;
|
||||
using System.Text.RegularExpressions;
|
||||
using UnityEngine;
|
||||
using UnityEngine.XR.OpenXR.Features;
|
||||
using UnityEngine.XR.OpenXR.Features.Interactions;
|
||||
using UnityEngine.XR.OpenXR;
|
||||
using Unity.XR.CoreUtils.Editor;
|
||||
|
||||
namespace UnityEditor.XR.OpenXR.Tests
|
||||
{
|
||||
internal class OpenXRValidationTests
|
||||
{
|
||||
internal class FakeFeature : OpenXRFeature
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test that IsRuleEnabled will be true at the correct time for a BuildValidationRule.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void IsRuleEnabledTest()
|
||||
{
|
||||
// Create a validation rule that is enabled when FakeFeature is active
|
||||
OpenXRFeature.ValidationRule testRule = new OpenXRFeature.ValidationRule(ScriptableObject.CreateInstance<FakeFeature>())
|
||||
{
|
||||
message = "Fake feature message.",
|
||||
checkPredicate = () => true,
|
||||
fixIt = () => { },
|
||||
error = false,
|
||||
errorEnteringPlaymode = false
|
||||
};
|
||||
|
||||
// Create the build validation rule for Standalone (arbitrarily picked)
|
||||
BuildValidationRule buildValidationRule = OpenXRProjectValidationRulesSetup.ConvertRuleToBuildValidationRule(testRule, BuildTargetGroup.Standalone);
|
||||
|
||||
// Since the feature isn't in the active Standalone settings, the rule should not be enabled.
|
||||
Assert.IsFalse(buildValidationRule.IsRuleEnabled());
|
||||
|
||||
// Temporarily add an enabled FakeFeature to the Standalone settings, and then restore the settings when the test is done.
|
||||
// The build validation rule should be enabled when we add the feature to the Standalone settings.
|
||||
OpenXRSettings standaloneSettings = OpenXRSettings.GetSettingsForBuildTargetGroup(BuildTargetGroup.Standalone);
|
||||
OpenXRFeature firstStandaloneSetting = standaloneSettings.features[0];
|
||||
try
|
||||
{
|
||||
FakeFeature fakeFeature = ScriptableObject.CreateInstance<FakeFeature>();
|
||||
fakeFeature.enabled = true;
|
||||
standaloneSettings.features[0] = fakeFeature;
|
||||
Assert.IsTrue(buildValidationRule.IsRuleEnabled());
|
||||
}
|
||||
finally
|
||||
{
|
||||
standaloneSettings.features[0] = firstStandaloneSetting;
|
||||
}
|
||||
|
||||
// Create another build validation rule for something else other than Standalone.
|
||||
// The build validation rule should not be enabled when we add the feature to the Standalone group.
|
||||
buildValidationRule = OpenXRProjectValidationRulesSetup.ConvertRuleToBuildValidationRule(testRule, BuildTargetGroup.WSA);
|
||||
standaloneSettings = OpenXRSettings.GetSettingsForBuildTargetGroup(BuildTargetGroup.Standalone);
|
||||
firstStandaloneSetting = standaloneSettings.features[0];
|
||||
try
|
||||
{
|
||||
FakeFeature fakeFeature = ScriptableObject.CreateInstance<FakeFeature>();
|
||||
fakeFeature.enabled = true;
|
||||
standaloneSettings.features[0] = fakeFeature;
|
||||
Assert.IsFalse(buildValidationRule.IsRuleEnabled());
|
||||
}
|
||||
finally
|
||||
{
|
||||
standaloneSettings.features[0] = firstStandaloneSetting;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8329db5b1413bf24f849af3e63602cd9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"name": "Unity.XR.OpenXR.Tests.Editor",
|
||||
"rootNamespace": "",
|
||||
"references": [
|
||||
"GUID:27619889b8ba8c24980f49ee34dbb44a",
|
||||
"GUID:0acc523941302664db1f4e527237feb3",
|
||||
"GUID:4847341ff46394e83bb78fbd0652937e",
|
||||
"GUID:96aa6ba065960476598f8f643e7252b6",
|
||||
"GUID:e40ba710768534012815d3193fa296cb",
|
||||
"GUID:2023fa18bf9504e98b11fe2174802802",
|
||||
"GUID:6150739e4dc7bff4d833306fd9d5a4f0",
|
||||
"GUID:75469ad4d38634e559750d17036d5f7c",
|
||||
"GUID:4ddd23ea56a3a40f0aa0036d1624a53e"
|
||||
],
|
||||
"includePlatforms": [
|
||||
"Editor"
|
||||
],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false,
|
||||
"overrideReferences": true,
|
||||
"precompiledReferences": [
|
||||
"nunit.framework.dll"
|
||||
],
|
||||
"autoReferenced": false,
|
||||
"defineConstraints": [
|
||||
"UNITY_INCLUDE_TESTS"
|
||||
],
|
||||
"versionDefines": [
|
||||
{
|
||||
"name": "com.unity.inputsystem",
|
||||
"expression": "1.6.3",
|
||||
"define": "INPUT_SYSTEM_BINDING_VALIDATOR"
|
||||
}
|
||||
],
|
||||
"noEngineReferences": false
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 666653aa92bac494b9db60410f5b51f0
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,158 @@
|
||||
using NUnit.Framework;
|
||||
using UnityEngine;
|
||||
using UnityEngine.TestTools;
|
||||
using UnityEngine.XR.OpenXR;
|
||||
using UnityEngine.XR.OpenXR.Features;
|
||||
using UnityEngine.XR.OpenXR.Features.Mock;
|
||||
using UnityEngine.XR.OpenXR.Tests;
|
||||
using Assert = UnityEngine.Assertions.Assert;
|
||||
|
||||
[assembly: UnityPlatform(RuntimePlatform.WindowsEditor, RuntimePlatform.OSXEditor)]
|
||||
|
||||
namespace UnityEditor.XR.OpenXR.Tests
|
||||
{
|
||||
internal class XRLoaderLifecycleTests : OpenXRLoaderSetup
|
||||
{
|
||||
[Test]
|
||||
public void FullLifecycleOrder()
|
||||
{
|
||||
bool subsystemCreate = false;
|
||||
bool subsystemStart = false;
|
||||
bool hookInstanceProc = false;
|
||||
MockRuntime.Instance.TestCallback = (methodName, param) =>
|
||||
{
|
||||
switch (methodName)
|
||||
{
|
||||
case nameof(OpenXRFeature.HookGetInstanceProcAddr):
|
||||
Assert.IsFalse(subsystemCreate);
|
||||
Assert.IsFalse(subsystemStart);
|
||||
hookInstanceProc = true;
|
||||
break;
|
||||
|
||||
case nameof(OpenXRFeature.OnSubsystemCreate):
|
||||
Assert.IsTrue(hookInstanceProc);
|
||||
Assert.IsFalse(subsystemStart);
|
||||
subsystemCreate = true;
|
||||
break;
|
||||
|
||||
case nameof(OpenXRFeature.OnSubsystemStart):
|
||||
Assert.IsTrue(hookInstanceProc);
|
||||
Assert.IsTrue(subsystemCreate);
|
||||
subsystemStart = true;
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
base.InitializeAndStart();
|
||||
|
||||
ProcessOpenXRMessageLoop();
|
||||
|
||||
Assert.IsTrue(hookInstanceProc);
|
||||
Assert.IsTrue(subsystemCreate);
|
||||
Assert.IsTrue(subsystemStart);
|
||||
|
||||
bool subsystemStop = false;
|
||||
bool subsystemDestroy = false;
|
||||
MockRuntime.Instance.TestCallback = (methodName, param) =>
|
||||
{
|
||||
switch (methodName)
|
||||
{
|
||||
case nameof(OpenXRFeature.OnSubsystemStop):
|
||||
Assert.IsFalse(subsystemDestroy);
|
||||
subsystemStop = true;
|
||||
break;
|
||||
|
||||
case nameof(OpenXRFeature.OnSubsystemDestroy):
|
||||
Assert.IsTrue(subsystemStop);
|
||||
subsystemDestroy = true;
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
base.StopAndShutdown();
|
||||
|
||||
Assert.IsTrue(subsystemStop);
|
||||
Assert.IsTrue(subsystemDestroy);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void InstanceCreate() => TestInstanceCreate(true, false);
|
||||
|
||||
[Test]
|
||||
public void InstanceCreateFail() => TestInstanceCreate(false, false);
|
||||
|
||||
[Test]
|
||||
public void InstanceCreateRequired() => TestInstanceCreate(true, true);
|
||||
|
||||
[Test]
|
||||
public void InstanceCreateFailRequired() => TestInstanceCreate(false, true);
|
||||
|
||||
public void TestInstanceCreate(bool result, bool required)
|
||||
{
|
||||
Loader.DisableValidationChecksOnEnteringPlaymode = true;
|
||||
bool instanceCreated = false;
|
||||
bool hookInstanceProcAddr = false;
|
||||
bool other = false;
|
||||
MockRuntime.Instance.TestCallback = (methodName, param) =>
|
||||
{
|
||||
switch (methodName)
|
||||
{
|
||||
case nameof(OpenXRFeature.OnInstanceCreate):
|
||||
instanceCreated = true;
|
||||
return result;
|
||||
|
||||
case nameof(OpenXRFeature.HookGetInstanceProcAddr):
|
||||
hookInstanceProcAddr = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
other = true;
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
MockRuntime.Instance.required = required;
|
||||
base.InitializeAndStart();
|
||||
|
||||
Assert.IsTrue(instanceCreated);
|
||||
Assert.IsTrue(hookInstanceProcAddr);
|
||||
|
||||
if (required && !result)
|
||||
Assert.IsNull(OpenXRLoaderBase.Instance);
|
||||
else
|
||||
Assert.IsNotNull(OpenXRLoaderBase.Instance);
|
||||
|
||||
// A feature that fails that is not required should be disabled
|
||||
if (!result && !required)
|
||||
Assert.IsFalse(MockRuntime.Instance.enabled);
|
||||
|
||||
base.StopAndShutdown();
|
||||
|
||||
if (result)
|
||||
Assert.IsTrue(other);
|
||||
else
|
||||
// A feature that fails initialize should have no further callbacks
|
||||
Assert.IsFalse(other);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DeinitWithoutInit()
|
||||
{
|
||||
bool callback = false;
|
||||
MockRuntime.Instance.TestCallback = (methodName, param) =>
|
||||
{
|
||||
callback = true;
|
||||
return true;
|
||||
};
|
||||
|
||||
base.StopAndShutdown();
|
||||
|
||||
Assert.IsFalse(callback);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dd9093c836e64270bb8a8c79d98e0055
|
||||
timeCreated: 1586981136
|
||||
@@ -0,0 +1,45 @@
|
||||
using System.IO;
|
||||
using NUnit.Framework;
|
||||
using UnityEngine;
|
||||
using UnityEngine.TestTools;
|
||||
using UnityEditor.SceneManagement;
|
||||
|
||||
[assembly: UnityPlatform(RuntimePlatform.WindowsEditor, RuntimePlatform.OSXEditor)]
|
||||
|
||||
namespace UnityEditor.XR.OpenXR.Tests
|
||||
{
|
||||
internal class aAssetBundleTests
|
||||
{
|
||||
[Test]
|
||||
public void BuildAssetBundle()
|
||||
{
|
||||
#if UNITY_EDITOR_WIN
|
||||
var target = BuildTarget.StandaloneWindows64;
|
||||
#elif UNITY_EDITOR_OSX
|
||||
var target = BuildTarget.StandaloneOSX;
|
||||
#else
|
||||
var target = BuildTarget.NoTarget;
|
||||
#endif
|
||||
|
||||
// Test is only valid if we have a valid build target and that build target is available
|
||||
if (target == BuildTarget.NoTarget || !BuildPipeline.IsBuildTargetSupported(BuildPipeline.GetBuildTargetGroup(target), target))
|
||||
return;
|
||||
|
||||
// Create an asset and add it to an asset bundle
|
||||
var scene = EditorSceneManager.NewScene(NewSceneSetup.DefaultGameObjects);
|
||||
EditorSceneManager.SaveScene(scene, "Assets/abtest.unity");
|
||||
AssetDatabase.Refresh();
|
||||
var importer = AssetImporter.GetAtPath("Assets/abtest.unity");
|
||||
importer.assetBundleName = "mocktest";
|
||||
|
||||
if (!Directory.Exists("mocktest/ab"))
|
||||
Directory.CreateDirectory("mocktest/ab");
|
||||
|
||||
// Build the asset bundle
|
||||
BuildPipeline.BuildAssetBundles("mocktest/ab", BuildAssetBundleOptions.ForceRebuildAssetBundle, target);
|
||||
|
||||
// Cleanup
|
||||
AssetDatabase.DeleteAsset("Assets/abtest.unity");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3bfe33a5f8e944b0be093f643d913262
|
||||
timeCreated: 1625861611
|
||||
310
Packages/com.unity.xr.openxr/Tests/Editor/zBuildHookTests.cs
Normal file
310
Packages/com.unity.xr.openxr/Tests/Editor/zBuildHookTests.cs
Normal file
@@ -0,0 +1,310 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using NUnit.Framework;
|
||||
using UnityEditor.XR.OpenXR.Features;
|
||||
using UnityEngine.XR.OpenXR.Features.Mock;
|
||||
using UnityEditor.Build.Reporting;
|
||||
using UnityEngine;
|
||||
using UnityEngine.XR.Management;
|
||||
using UnityEngine.XR.OpenXR.Tests;
|
||||
using Assert = UnityEngine.Assertions.Assert;
|
||||
|
||||
namespace UnityEditor.XR.OpenXR.Tests
|
||||
{
|
||||
// If you change this file, be sure to run the "no players" tests on yamato.
|
||||
// APV jobs don't include "players" such as standalone, so `BuildMockPlayer()`
|
||||
// will fail during APV. The "no players" job on yamato will catch this.
|
||||
internal class zBuildHookTests : OpenXRLoaderSetup
|
||||
{
|
||||
internal static BuildReport BuildMockPlayer()
|
||||
{
|
||||
BuildPlayerOptions opts = new BuildPlayerOptions();
|
||||
#if UNITY_EDITOR_WIN
|
||||
opts.target = BuildTarget.StandaloneWindows64;
|
||||
#elif UNITY_EDITOR_OSX
|
||||
opts.target = BuildTarget.StandaloneOSX;
|
||||
#endif
|
||||
if (File.Exists("Assets/main.unity"))
|
||||
opts.scenes = new string[] { "Assets/main.unity" };
|
||||
opts.targetGroup = BuildTargetGroup.Standalone;
|
||||
opts.locationPathName = "mocktest/mocktest.exe";
|
||||
|
||||
UnityEngine.TestTools.LogAssert.ignoreFailingMessages = true;
|
||||
EditorUserBuildSettings.SwitchActiveBuildTarget(BuildTargetGroup.Standalone, opts.target);
|
||||
var report = BuildPipeline.BuildPlayer(opts);
|
||||
UnityEngine.TestTools.LogAssert.ignoreFailingMessages = false;
|
||||
return report;
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PrePostCallbacksAreReceived()
|
||||
{
|
||||
bool preprocessCalled = false;
|
||||
bool postprocessCalled = false;
|
||||
|
||||
BuildCallbacks.TestCallback = (methodName, param) =>
|
||||
{
|
||||
if (methodName == "OnPreprocessBuildExt")
|
||||
{
|
||||
preprocessCalled = true;
|
||||
}
|
||||
|
||||
if (methodName == "OnPostprocessBuildExt")
|
||||
{
|
||||
postprocessCalled = true;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
var result = BuildMockPlayer();
|
||||
|
||||
if (Environment.GetEnvironmentVariable("UNITY_OPENXR_YAMATO") == "1")
|
||||
Assert.IsTrue(result.summary.result == BuildResult.Succeeded);
|
||||
else if (result.summary.result != BuildResult.Succeeded)
|
||||
return;
|
||||
|
||||
Assert.IsTrue(preprocessCalled);
|
||||
Assert.IsTrue(postprocessCalled);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void NoBuildCallbacksFeatureDisabled()
|
||||
{
|
||||
bool preprocessCalled = false;
|
||||
bool postprocessCalled = false;
|
||||
|
||||
BuildCallbacks.TestCallback = (methodName, param) =>
|
||||
{
|
||||
if (methodName == "OnPreprocessBuildExt")
|
||||
{
|
||||
preprocessCalled = true;
|
||||
}
|
||||
|
||||
if (methodName == "OnPostprocessBuildExt")
|
||||
{
|
||||
postprocessCalled = true;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
// Disable mock runtime, no callbacks should occur during build
|
||||
EnableFeature<MockRuntime>(false);
|
||||
BuildMockPlayer();
|
||||
Assert.IsFalse(preprocessCalled);
|
||||
Assert.IsFalse(postprocessCalled);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void NoBuildCallbacksOpenXRDisabled()
|
||||
{
|
||||
bool preprocessCalled = false;
|
||||
bool postprocessCalled = false;
|
||||
|
||||
BuildCallbacks.TestCallback = (methodName, param) =>
|
||||
{
|
||||
if (methodName == "OnPreprocessBuildExt")
|
||||
{
|
||||
preprocessCalled = true;
|
||||
}
|
||||
|
||||
if (methodName == "OnPostprocessBuildExt")
|
||||
{
|
||||
postprocessCalled = true;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
// Remove OpenXR Loader, no callbacks should occur during build
|
||||
var loaders = XRGeneralSettings.Instance.Manager.activeLoaders;
|
||||
XRGeneralSettings.Instance.Manager.TrySetLoaders(new List<XRLoader>());
|
||||
BuildMockPlayer();
|
||||
XRGeneralSettings.Instance.Manager.TrySetLoaders(new List<XRLoader>(loaders));
|
||||
Assert.IsFalse(preprocessCalled);
|
||||
Assert.IsFalse(postprocessCalled);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void VerifyBootConfigWrite()
|
||||
{
|
||||
bool preprocessCalled = false;
|
||||
bool postprocessCalled = false;
|
||||
|
||||
BootConfigTests.TestCallback = (methodName, param) =>
|
||||
{
|
||||
if (methodName == "OnPreprocessBuildExt")
|
||||
{
|
||||
preprocessCalled = true;
|
||||
}
|
||||
|
||||
if (methodName == "OnPostprocessBuildExt")
|
||||
{
|
||||
postprocessCalled = true;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
var result = BuildMockPlayer();
|
||||
BuildMockPlayer();
|
||||
Assert.IsFalse(preprocessCalled);
|
||||
Assert.IsFalse(postprocessCalled);
|
||||
|
||||
BootConfigTests.EnsureCleanupFromLastRun();
|
||||
}
|
||||
|
||||
private bool HasOpenXRLibraries(BuildReport report)
|
||||
{
|
||||
var path = Path.GetDirectoryName(report.summary.outputPath);
|
||||
var dir = new DirectoryInfo(path);
|
||||
|
||||
var ext = "dll";
|
||||
if (Application.platform == RuntimePlatform.OSXEditor)
|
||||
ext = "dylib";
|
||||
|
||||
var dlls = dir.EnumerateFiles($"*.{ext}", SearchOption.AllDirectories).Select(s => s.Name).ToList();
|
||||
return dlls.Contains($"openxr_loader.{ext}") || dlls.Contains($"UnityOpenXR.{ext}");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void VerifyBuildOutputLibraries()
|
||||
{
|
||||
var resultWithOpenXR = BuildMockPlayer();
|
||||
|
||||
// Disable this test if we're not running our openxr yamato infrastructure
|
||||
if (resultWithOpenXR.summary.result != BuildResult.Succeeded && Environment.GetEnvironmentVariable("UNITY_OPENXR_YAMATO") != "1")
|
||||
return;
|
||||
|
||||
Assert.IsTrue(HasOpenXRLibraries(resultWithOpenXR));
|
||||
|
||||
// Remove OpenXR Loader
|
||||
XRGeneralSettings.Instance.Manager.TrySetLoaders(new List<XRLoader>());
|
||||
var resultWithoutOpenXR = BuildMockPlayer();
|
||||
Assert.IsFalse(HasOpenXRLibraries(resultWithoutOpenXR));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void VerifyBuildWithoutAnalytics()
|
||||
{
|
||||
var packageName = "com.unity.analytics";
|
||||
UnityEditor.PackageManager.Client.Remove(packageName);
|
||||
|
||||
var result = BuildMockPlayer();
|
||||
|
||||
if (Environment.GetEnvironmentVariable("UNITY_OPENXR_YAMATO") == "1")
|
||||
Assert.IsTrue(result.summary.result == BuildResult.Succeeded);
|
||||
else if (result.summary.result != BuildResult.Succeeded)
|
||||
return;
|
||||
}
|
||||
|
||||
internal class BuildCallbacks : OpenXRFeatureBuildHooks
|
||||
{
|
||||
[NonSerialized] internal static Func<string, object, bool> TestCallback = (methodName, param) => true;
|
||||
|
||||
public override int callbackOrder => 1;
|
||||
public override Type featureType => typeof(MockRuntime);
|
||||
|
||||
protected override void OnPreprocessBuildExt(BuildReport report)
|
||||
{
|
||||
TestCallback(MethodBase.GetCurrentMethod().Name, report);
|
||||
}
|
||||
|
||||
protected override void OnPostGenerateGradleAndroidProjectExt(string path)
|
||||
{
|
||||
TestCallback(MethodBase.GetCurrentMethod().Name, path);
|
||||
}
|
||||
|
||||
protected override void OnPostprocessBuildExt(BuildReport report)
|
||||
{
|
||||
TestCallback(MethodBase.GetCurrentMethod().Name, report);
|
||||
}
|
||||
|
||||
protected override void OnProcessBootConfigExt(BuildReport report, BootConfigBuilder builder)
|
||||
{
|
||||
TestCallback(MethodBase.GetCurrentMethod().Name, report);
|
||||
}
|
||||
}
|
||||
|
||||
internal class BootConfigTests : OpenXRFeatureBuildHooks
|
||||
{
|
||||
[NonSerialized] internal static Func<string, object, bool> TestCallback = (methodName, param) => true;
|
||||
|
||||
public override int callbackOrder => 1;
|
||||
public override Type featureType => typeof(MockRuntime);
|
||||
|
||||
// For this test, we want to ensure that the last run actually cleans up any settings that we've
|
||||
// stored into the boot settings of the EditorUserBuildSettings. We need the last run BuildReport
|
||||
// in order to check the EditorUserBuildSettings.
|
||||
private static BuildReport s_lastRunBuildReport;
|
||||
|
||||
protected override void OnPreprocessBuildExt(BuildReport report)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void OnPostGenerateGradleAndroidProjectExt(string path)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void OnPostprocessBuildExt(BuildReport report)
|
||||
{
|
||||
// check to see if we've got the boot config written into the UserSettings
|
||||
var bootConfig = new BootConfig(report);
|
||||
bootConfig.ReadBootConfig();
|
||||
Assert.IsTrue(bootConfig.TryGetValue("key-01", out var key01Value));
|
||||
Assert.AreEqual(key01Value, "primary test value");
|
||||
Assert.IsTrue(bootConfig.TryGetValue("key-02", out var key02Value));
|
||||
Assert.AreEqual(key02Value, "secondary test value");
|
||||
Assert.IsTrue(bootConfig.TryGetValue("key-03", out var key03Value));
|
||||
Assert.AreEqual(key03Value, "1");
|
||||
Assert.IsTrue(bootConfig.TryGetValue("key-04", out var key04Value));
|
||||
Assert.AreEqual(key04Value, "0");
|
||||
|
||||
s_lastRunBuildReport = report;
|
||||
}
|
||||
|
||||
protected override void OnProcessBootConfigExt(BuildReport report, BootConfigBuilder builder)
|
||||
{
|
||||
// Now we set some boot config values and check to make sure they're there.
|
||||
builder.SetBootConfigValue("key-01", "primary test value");
|
||||
builder.SetBootConfigValue("key-02", "secondary test value");
|
||||
builder.SetBootConfigBoolean("key-03", true);
|
||||
builder.SetBootConfigBoolean("key-04", false);
|
||||
Assert.IsTrue(builder.TryGetBootConfigValue("key-01", out var result01));
|
||||
Assert.AreEqual(result01, "primary test value");
|
||||
Assert.IsTrue(builder.TryGetBootConfigValue("key-02", out var result02));
|
||||
Assert.AreEqual(result02, "secondary test value");
|
||||
Assert.IsTrue(builder.TryGetBootConfigBoolean("key-03", out var result03));
|
||||
Assert.IsTrue(result03);
|
||||
Assert.IsTrue(builder.TryGetBootConfigBoolean("key-04", out var result04));
|
||||
Assert.IsFalse(result04);
|
||||
builder.SetBootConfigValue("key-05", "remove-me");
|
||||
Assert.IsTrue(builder.TryRemoveBootConfigEntry("key-05"));
|
||||
Assert.IsFalse(builder.TryGetBootConfigValue("key-05", out var result05));
|
||||
Assert.IsFalse(builder.TryGetBootConfigBoolean("key-999", out var result06));
|
||||
Assert.IsFalse(result06);
|
||||
}
|
||||
|
||||
public static void EnsureCleanupFromLastRun()
|
||||
{
|
||||
// make sure that the UserSettings doesn't hold any additional configs we've previously written
|
||||
if (s_lastRunBuildReport == null)
|
||||
return;
|
||||
|
||||
var bootConfig = new BootConfig(s_lastRunBuildReport);
|
||||
bootConfig.ReadBootConfig();
|
||||
Assert.IsFalse(bootConfig.TryGetValue("key-01", out var key01Value));
|
||||
Assert.IsFalse(bootConfig.TryGetValue("key-02", out var key02Value));
|
||||
Assert.IsFalse(bootConfig.TryGetValue("key-03", out var key03Value));
|
||||
Assert.IsFalse(bootConfig.TryGetValue("key-04", out var key04Value));
|
||||
Assert.IsFalse(bootConfig.TryGetValue("key-05", out var key05Value));
|
||||
|
||||
s_lastRunBuildReport = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4a0dc36717c904db4ab68f42baa559da
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,372 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
using UnityEditor;
|
||||
using UnityEditor.Build;
|
||||
using UnityEditor.Build.Reporting;
|
||||
using UnityEditor.XR.OpenXR.Features;
|
||||
|
||||
using UnityEngine;
|
||||
using UnityEngine.Assertions;
|
||||
using UnityEngine.Rendering;
|
||||
using UnityEngine.XR.OpenXR;
|
||||
using UnityEngine.XR.OpenXR.Features;
|
||||
using UnityEngine.XR.OpenXR.Features.Interactions;
|
||||
|
||||
class zBuildSamplesYamatoOnly
|
||||
{
|
||||
struct SampleBuildTargetSetup
|
||||
{
|
||||
public BuildTarget buildTarget;
|
||||
public BuildTargetGroup targetGroup;
|
||||
public Action<string, string> setupPlayerSettings;
|
||||
public string outputPostfix;
|
||||
public Regex sampleRegex;
|
||||
}
|
||||
|
||||
static void WriteAndroidInstallerScripts(string outputFile, string identifier)
|
||||
{
|
||||
var dir = Path.GetDirectoryName(outputFile);
|
||||
if (dir == null) return;
|
||||
Directory.CreateDirectory(dir);
|
||||
|
||||
var scripts = new string[] { "install.command", "install.bat" };
|
||||
foreach (var script in scripts)
|
||||
{
|
||||
var scriptPath = Path.Combine(dir, script);
|
||||
|
||||
var scriptContents = $"adb uninstall {identifier}\n" +
|
||||
$"adb install \"{Path.GetFileName(outputFile)}\"\n\n";
|
||||
|
||||
File.AppendAllText(scriptPath, scriptContents);
|
||||
}
|
||||
}
|
||||
|
||||
static void EnableQuestFeature()
|
||||
{
|
||||
foreach (var feature in OpenXRSettings.ActiveBuildTargetInstance.features)
|
||||
{
|
||||
if (String.Compare(feature.featureIdInternal, "com.unity.openxr.feature.metaquest", true) == 0)
|
||||
{
|
||||
Console.WriteLine($"Enable: {feature.nameUi}");
|
||||
feature.enabled = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
Assert.IsTrue(false, "Could not enable meta quest extension - if you're not on build machine you must copy dir MetaQuest to your project.");
|
||||
}
|
||||
|
||||
static void EnableMSFTObserverFeature()
|
||||
{
|
||||
foreach (var feature in OpenXRSettings.ActiveBuildTargetInstance.features)
|
||||
{
|
||||
if (String.Compare(feature.featureIdInternal, "com.unity.openxr.feature.example.msftobserver", true) == 0)
|
||||
{
|
||||
Console.WriteLine($"Enable: {feature.nameUi}");
|
||||
feature.enabled = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void EnableFeature<TFeatureType>() where TFeatureType : OpenXRFeature
|
||||
{
|
||||
foreach (var feature in OpenXRSettings.ActiveBuildTargetInstance.features)
|
||||
{
|
||||
if (feature is TFeatureType)
|
||||
{
|
||||
Console.WriteLine($"Enable: {feature.nameUi}");
|
||||
feature.enabled = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void EnableSampleFeatures()
|
||||
{
|
||||
foreach (var feature in OpenXRSettings.ActiveBuildTargetInstance.features)
|
||||
{
|
||||
if (feature.GetType().Namespace == null)
|
||||
{
|
||||
throw new Exception("All code in the OpenXR Package must be in a namespace.");
|
||||
}
|
||||
|
||||
if (feature.GetType().Namespace.StartsWith("UnityEngine.XR.OpenXR.Samples"))
|
||||
{
|
||||
Console.WriteLine($"Enable: {feature.nameUi}");
|
||||
feature.enabled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void EnableStandaloneProfiles()
|
||||
{
|
||||
EnableFeature<MicrosoftHandInteraction>();
|
||||
EnableFeature<MicrosoftMotionControllerProfile>();
|
||||
EnableFeature<HTCViveControllerProfile>();
|
||||
EnableFeature<ValveIndexControllerProfile>();
|
||||
EnableFeature<OculusTouchControllerProfile>();
|
||||
EnableFeature<MetaQuestTouchProControllerProfile>();
|
||||
EnableFeature<HandInteractionProfile>();
|
||||
EnableFeature<PalmPoseInteraction>();
|
||||
EnableFeature<DPadInteraction>();
|
||||
EnableFeature<HandCommonPosesInteraction>();
|
||||
}
|
||||
|
||||
static void EnableWSAProfiles()
|
||||
{
|
||||
EnableFeature<MicrosoftHandInteraction>();
|
||||
EnableFeature<EyeGazeInteraction>();
|
||||
EnableFeature<MicrosoftMotionControllerProfile>();
|
||||
EnableFeature<HandInteractionProfile>();
|
||||
EnableFeature<PalmPoseInteraction>();
|
||||
EnableFeature<HandCommonPosesInteraction>();
|
||||
}
|
||||
|
||||
static void EnableAndroidProfiles()
|
||||
{
|
||||
EnableFeature<OculusTouchControllerProfile>();
|
||||
EnableFeature<MetaQuestTouchProControllerProfile>();
|
||||
}
|
||||
|
||||
static SampleBuildTargetSetup[] buildTargetSetup =
|
||||
{
|
||||
#if UNITY_EDITOR_WIN
|
||||
new SampleBuildTargetSetup
|
||||
{
|
||||
buildTarget = BuildTarget.StandaloneWindows64,
|
||||
targetGroup = BuildTargetGroup.Standalone,
|
||||
setupPlayerSettings = (outputFile, identifier) =>
|
||||
{
|
||||
EnableSampleFeatures();
|
||||
EnableStandaloneProfiles();
|
||||
PlayerSettings.SetGraphicsAPIs(BuildTarget.StandaloneWindows64, new[] { GraphicsDeviceType.Direct3D11, GraphicsDeviceType.Vulkan });
|
||||
OpenXRSettings.ActiveBuildTargetInstance.depthSubmissionMode = OpenXRSettings.DepthSubmissionMode.Depth24Bit;
|
||||
},
|
||||
outputPostfix = "dx11",
|
||||
},
|
||||
new SampleBuildTargetSetup
|
||||
{
|
||||
sampleRegex = new Regex(".*Render.*"), // Only build dx12 variant for Render Samples
|
||||
buildTarget = BuildTarget.StandaloneWindows64,
|
||||
targetGroup = BuildTargetGroup.Standalone,
|
||||
setupPlayerSettings = (outputFile, identifier) =>
|
||||
{
|
||||
EnableSampleFeatures();
|
||||
PlayerSettings.SetGraphicsAPIs(BuildTarget.StandaloneWindows64, new[] { GraphicsDeviceType.Direct3D12, GraphicsDeviceType.Direct3D11 });
|
||||
QualitySettings.SetQualityLevel(5);
|
||||
QualitySettings.antiAliasing = 4;
|
||||
},
|
||||
outputPostfix = "dx12",
|
||||
},
|
||||
new SampleBuildTargetSetup
|
||||
{
|
||||
sampleRegex = new Regex(".*Render.*"), // Only build vulkan variant for Render Samples
|
||||
buildTarget = BuildTarget.StandaloneWindows64,
|
||||
targetGroup = BuildTargetGroup.Standalone,
|
||||
setupPlayerSettings = (outputFile, identifier) =>
|
||||
{
|
||||
EnableSampleFeatures();
|
||||
PlayerSettings.SetGraphicsAPIs(BuildTarget.StandaloneWindows64, new[] { GraphicsDeviceType.Vulkan, GraphicsDeviceType.Direct3D11 });
|
||||
OpenXRSettings.ActiveBuildTargetInstance.depthSubmissionMode = OpenXRSettings.DepthSubmissionMode.Depth24Bit;
|
||||
},
|
||||
outputPostfix = "vk",
|
||||
},
|
||||
new SampleBuildTargetSetup
|
||||
{
|
||||
buildTarget = BuildTarget.WSAPlayer,
|
||||
targetGroup = BuildTargetGroup.WSA,
|
||||
setupPlayerSettings = (outputFile, identifier) =>
|
||||
{
|
||||
EnableSampleFeatures();
|
||||
EnableMSFTObserverFeature();
|
||||
EnableFeature<EyeGazeInteraction>();
|
||||
EnableFeature<MicrosoftHandInteraction>();
|
||||
EnableWSAProfiles();
|
||||
PlayerSettings.SetGraphicsAPIs(BuildTarget.WSAPlayer, new[] { GraphicsDeviceType.Direct3D11 });
|
||||
PlayerSettings.WSA.SetCapability(PlayerSettings.WSACapability.GazeInput, true);
|
||||
#if UNITY_2021_3_OR_NEWER
|
||||
PlayerSettings.WSA.packageName = PlayerSettings.GetApplicationIdentifier(NamedBuildTarget.WindowsStoreApps);
|
||||
#else
|
||||
PlayerSettings.WSA.packageName = PlayerSettings.GetApplicationIdentifier(BuildTargetGroup.WSA);
|
||||
#endif
|
||||
OpenXRSettings.ActiveBuildTargetInstance.renderMode = OpenXRSettings.RenderMode.SinglePassInstanced;
|
||||
OpenXRSettings.ActiveBuildTargetInstance.depthSubmissionMode = OpenXRSettings.DepthSubmissionMode.Depth16Bit;
|
||||
},
|
||||
outputPostfix = "dx11",
|
||||
},
|
||||
new SampleBuildTargetSetup
|
||||
{
|
||||
sampleRegex = new Regex(".*Render.*"), // Only build dx12 variant for Render Samples
|
||||
buildTarget = BuildTarget.WSAPlayer,
|
||||
targetGroup = BuildTargetGroup.WSA,
|
||||
setupPlayerSettings = (outputFile, identifier) =>
|
||||
{
|
||||
EnableSampleFeatures();
|
||||
EnableMSFTObserverFeature();
|
||||
EnableFeature<EyeGazeInteraction>();
|
||||
EnableFeature<MicrosoftHandInteraction>();
|
||||
PlayerSettings.SetGraphicsAPIs(BuildTarget.WSAPlayer, new[] { GraphicsDeviceType.Direct3D12 });
|
||||
QualitySettings.SetQualityLevel(5);
|
||||
QualitySettings.antiAliasing = 4;
|
||||
#if UNITY_2021_3_OR_NEWER
|
||||
PlayerSettings.WSA.packageName = PlayerSettings.GetApplicationIdentifier(NamedBuildTarget.WindowsStoreApps);
|
||||
#else
|
||||
PlayerSettings.WSA.packageName = PlayerSettings.GetApplicationIdentifier(BuildTargetGroup.WSA);
|
||||
#endif
|
||||
PlayerSettings.WSA.SetCapability(PlayerSettings.WSACapability.GazeInput, true);
|
||||
},
|
||||
outputPostfix = "dx12",
|
||||
},
|
||||
#endif
|
||||
new SampleBuildTargetSetup
|
||||
{
|
||||
sampleRegex = new Regex(".*Render.*|.*MetaSample.*"), // Only build vulkan variant for Render Samples
|
||||
buildTarget = BuildTarget.Android,
|
||||
targetGroup = BuildTargetGroup.Android,
|
||||
setupPlayerSettings = (outputFile, identifier) =>
|
||||
{
|
||||
EnableSampleFeatures();
|
||||
EnableQuestFeature();
|
||||
EnableAndroidProfiles();
|
||||
PlayerSettings.SetGraphicsAPIs(BuildTarget.Android, new[] { GraphicsDeviceType.Vulkan, GraphicsDeviceType.OpenGLES3 });
|
||||
PlayerSettings.Android.minSdkVersion = AndroidSdkVersions.AndroidApiLevel25;
|
||||
PlayerSettings.Android.targetArchitectures = AndroidArchitecture.ARM64;
|
||||
#if UNITY_2021_3_OR_NEWER
|
||||
PlayerSettings.SetScriptingBackend(NamedBuildTarget.Android, ScriptingImplementation.IL2CPP);
|
||||
#else
|
||||
PlayerSettings.SetScriptingBackend(BuildTargetGroup.Android, ScriptingImplementation.IL2CPP);
|
||||
#endif
|
||||
WriteAndroidInstallerScripts(outputFile, identifier);
|
||||
OpenXRSettings.ActiveBuildTargetInstance.depthSubmissionMode = OpenXRSettings.DepthSubmissionMode.Depth16Bit;
|
||||
},
|
||||
outputPostfix = "arm64_vk",
|
||||
},
|
||||
new SampleBuildTargetSetup
|
||||
{
|
||||
sampleRegex = new Regex("^(?!.*MetaSample).*$"), // Don't build the Meta Sample for GLES
|
||||
buildTarget = BuildTarget.Android,
|
||||
targetGroup = BuildTargetGroup.Android,
|
||||
setupPlayerSettings = (outputFile, identifier) =>
|
||||
{
|
||||
EnableSampleFeatures();
|
||||
EnableQuestFeature();
|
||||
EnableAndroidProfiles();
|
||||
PlayerSettings.SetUseDefaultGraphicsAPIs(BuildTarget.Android, false);
|
||||
PlayerSettings.SetGraphicsAPIs(BuildTarget.Android, new[] { GraphicsDeviceType.OpenGLES3, GraphicsDeviceType.Vulkan });
|
||||
PlayerSettings.Android.minSdkVersion = AndroidSdkVersions.AndroidApiLevel25;
|
||||
PlayerSettings.Android.targetArchitectures = AndroidArchitecture.ARM64;
|
||||
#if UNITY_2021_3_OR_NEWER
|
||||
PlayerSettings.SetScriptingBackend(NamedBuildTarget.Android, ScriptingImplementation.IL2CPP);
|
||||
#else
|
||||
PlayerSettings.SetScriptingBackend(BuildTargetGroup.Android, ScriptingImplementation.IL2CPP);
|
||||
#endif
|
||||
WriteAndroidInstallerScripts(outputFile, identifier);
|
||||
OpenXRSettings.ActiveBuildTargetInstance.depthSubmissionMode = OpenXRSettings.DepthSubmissionMode.Depth16Bit;
|
||||
},
|
||||
outputPostfix = "arm64_gles3",
|
||||
},
|
||||
};
|
||||
|
||||
static string GetBuildFileExt(BuildTarget target)
|
||||
{
|
||||
switch (target)
|
||||
{
|
||||
case BuildTarget.Android:
|
||||
return ".apk";
|
||||
case BuildTarget.StandaloneWindows:
|
||||
case BuildTarget.StandaloneWindows64:
|
||||
return ".exe";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
static string GetResultDir()
|
||||
{
|
||||
bool next = false;
|
||||
foreach (var arg in System.Environment.GetCommandLineArgs())
|
||||
{
|
||||
if (next)
|
||||
return arg;
|
||||
|
||||
if (arg == "-resultDir")
|
||||
next = true;
|
||||
}
|
||||
return "OpenXR Samples";
|
||||
}
|
||||
|
||||
static void BuildSamples()
|
||||
{
|
||||
string resultDir = GetResultDir();
|
||||
|
||||
Console.WriteLine("Result Dir: " + resultDir);
|
||||
|
||||
var sampleName = "Unknown Sample";
|
||||
var projSamplesDir = new DirectoryInfo("Assets/Sample");
|
||||
if (projSamplesDir.Exists)
|
||||
{
|
||||
// Use the directory name in the samples directory, if it exists
|
||||
sampleName = projSamplesDir.GetDirectories()[0].Name;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Otherwise use the current folder as the project name
|
||||
projSamplesDir = new DirectoryInfo("Assets");
|
||||
sampleName = new DirectoryInfo(".").Name;
|
||||
}
|
||||
|
||||
PlayerSettings.colorSpace = ColorSpace.Linear;
|
||||
FeatureHelpers.RefreshFeatures(EditorUserBuildSettings.selectedBuildTargetGroup);
|
||||
|
||||
foreach (var setup in buildTargetSetup)
|
||||
{
|
||||
if (setup.sampleRegex != null && !setup.sampleRegex.Match(sampleName).Success)
|
||||
continue;
|
||||
|
||||
if (EditorUserBuildSettings.activeBuildTarget != setup.buildTarget)
|
||||
continue;
|
||||
|
||||
string outputDir = Path.Combine(resultDir, setup.buildTarget.ToString());
|
||||
|
||||
string identifier = "com.openxr." + sampleName + "." + setup.outputPostfix;
|
||||
#if UNITY_2021_3_OR_NEWER
|
||||
PlayerSettings.SetApplicationIdentifier(NamedBuildTarget.FromBuildTargetGroup(setup.targetGroup), identifier);
|
||||
#else
|
||||
PlayerSettings.SetApplicationIdentifier(setup.targetGroup, identifier);
|
||||
#endif
|
||||
PlayerSettings.productName = "OpenXR " + sampleName + " " + setup.outputPostfix;
|
||||
Console.WriteLine("=========== Setting up player settings (changing graphics apis)");
|
||||
string outputFile = Path.Combine(outputDir,
|
||||
PlayerSettings.productName + GetBuildFileExt(setup.buildTarget));
|
||||
setup.setupPlayerSettings(outputFile, identifier);
|
||||
|
||||
// Get the list of scenes set in build settings in the project
|
||||
var scenes = EditorBuildSettings.scenes.Where(s => s.enabled).Select(s => s.path).ToArray();
|
||||
|
||||
// If there aren't any, just build all of the scenes found in the sample
|
||||
if (scenes.Length == 0)
|
||||
{
|
||||
scenes = Directory.GetFiles(projSamplesDir.FullName, "*.unity", SearchOption.AllDirectories);
|
||||
}
|
||||
|
||||
BuildPlayerOptions buildOptions = new BuildPlayerOptions
|
||||
{
|
||||
scenes = scenes,
|
||||
target = setup.buildTarget,
|
||||
targetGroup = setup.targetGroup,
|
||||
locationPathName = outputFile,
|
||||
};
|
||||
Console.WriteLine($"=========== Building {sampleName} {setup.buildTarget}_{setup.outputPostfix}");
|
||||
var report = BuildPipeline.BuildPlayer(buildOptions);
|
||||
Console.WriteLine($"=========== Build Result {sampleName} {setup.buildTarget}_{setup.outputPostfix} {report.summary.result}");
|
||||
|
||||
if (report.summary.result == BuildResult.Failed)
|
||||
{
|
||||
EditorApplication.Exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b539520d5a342436b8ee4abb353fd33b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Packages/com.unity.xr.openxr/Tests/Runtime.meta
Normal file
8
Packages/com.unity.xr.openxr/Tests/Runtime.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 95a45e2eff66745b1ba7ca6684e8a4b0
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,222 @@
|
||||
using System;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace UnityEngine.XR.OpenXR.Tests
|
||||
{
|
||||
internal class DiagnosticReportTests
|
||||
{
|
||||
const string k_SectionOneTitle = "Section One";
|
||||
const string k_SectionTwoTitle = "Section Two";
|
||||
|
||||
[SetUp]
|
||||
public void SetUp()
|
||||
{
|
||||
DiagnosticReport.StartReport();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GettingSectionReturnsValidHandle()
|
||||
{
|
||||
var sectionOneHandle = DiagnosticReport.GetSection(k_SectionOneTitle);
|
||||
Assert.AreNotEqual(DiagnosticReport.k_NullSection, sectionOneHandle);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SameSectionTitleGivesSameSectionHandle()
|
||||
{
|
||||
var sectionOneHandle = DiagnosticReport.GetSection(k_SectionOneTitle);
|
||||
var sectionOneHandleTwo = DiagnosticReport.GetSection(k_SectionOneTitle);
|
||||
Assert.AreEqual(sectionOneHandle, sectionOneHandleTwo);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DifferentSectionTitlesGiveDifferentSectionHandles()
|
||||
{
|
||||
var sectionOneHandle = DiagnosticReport.GetSection(k_SectionOneTitle);
|
||||
var sectionTwoHandle = DiagnosticReport.GetSection(k_SectionTwoTitle);
|
||||
Assert.AreNotEqual(sectionOneHandle, sectionTwoHandle);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CheckSimpleReportGenerationIsCorrect()
|
||||
{
|
||||
const string k_ExpectedOutput = "==== Section One ====\n\n==== Last 20 Events ====\n";
|
||||
|
||||
var sectionOneHandle = DiagnosticReport.GetSection(k_SectionOneTitle);
|
||||
var report = DiagnosticReport.GenerateReport();
|
||||
Assert.IsFalse(String.IsNullOrEmpty(report));
|
||||
Assert.AreEqual(k_ExpectedOutput, report);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CheckGenerateReportWithEntries()
|
||||
{
|
||||
const string k_ExpectedOutput = @"==== Section One ====
|
||||
Entry Header: Entry Body
|
||||
Entry Header 2: Entry Body 2
|
||||
Entry Header 3: Entry Body 3
|
||||
|
||||
==== Last 20 Events ====
|
||||
";
|
||||
|
||||
var sectionOneHandle = DiagnosticReport.GetSection(k_SectionOneTitle);
|
||||
DiagnosticReport.AddSectionEntry(sectionOneHandle, "Entry Header", "Entry Body");
|
||||
DiagnosticReport.AddSectionEntry(sectionOneHandle, "Entry Header 2", "Entry Body 2");
|
||||
DiagnosticReport.AddSectionEntry(sectionOneHandle, "Entry Header 3", "Entry Body 3");
|
||||
|
||||
var report = DiagnosticReport.GenerateReport();
|
||||
Assert.AreEqual(k_ExpectedOutput, report);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CheckGenerateReportWithMultipleSectionsAndEntries()
|
||||
{
|
||||
const string k_ExpectedOutput = @"==== Section One ====
|
||||
Entry Header: Entry Body
|
||||
Entry Header 2: Entry Body 2
|
||||
Entry Header 3: Entry Body 3
|
||||
|
||||
==== Section Two ====
|
||||
Entry Header 4: Entry Body 4
|
||||
Entry Header 5: Entry Body 5
|
||||
Entry Header 6: Entry Body 6
|
||||
|
||||
==== Last 20 Events ====
|
||||
";
|
||||
|
||||
var sectionOneHandle = DiagnosticReport.GetSection(k_SectionOneTitle);
|
||||
DiagnosticReport.AddSectionEntry(sectionOneHandle, "Entry Header", "Entry Body");
|
||||
DiagnosticReport.AddSectionEntry(sectionOneHandle, "Entry Header 2", "Entry Body 2");
|
||||
DiagnosticReport.AddSectionEntry(sectionOneHandle, "Entry Header 3", "Entry Body 3");
|
||||
|
||||
var sectionTwoHandle = DiagnosticReport.GetSection(k_SectionTwoTitle);
|
||||
DiagnosticReport.AddSectionEntry(sectionTwoHandle, "Entry Header 4", "Entry Body 4");
|
||||
DiagnosticReport.AddSectionEntry(sectionTwoHandle, "Entry Header 5", "Entry Body 5");
|
||||
DiagnosticReport.AddSectionEntry(sectionTwoHandle, "Entry Header 6", "Entry Body 6");
|
||||
|
||||
var report = DiagnosticReport.GenerateReport();
|
||||
Assert.AreEqual(k_ExpectedOutput, report);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CheckGeneratedEventsAreReported()
|
||||
{
|
||||
const string k_ExpectedOutput = @"==== Last 20 Events ====
|
||||
Event One: Event Body One
|
||||
";
|
||||
|
||||
DiagnosticReport.AddEventEntry("Event One", "Event Body One");
|
||||
|
||||
var report = DiagnosticReport.GenerateReport();
|
||||
Assert.AreEqual(k_ExpectedOutput, report);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CheckGeneratedEventsOverTwentyAreReported()
|
||||
{
|
||||
const string k_ExpectedOutput = @"==== Last 20 Events ====
|
||||
Event 11: Event Body 11
|
||||
Event 12: Event Body 12
|
||||
Event 13: Event Body 13
|
||||
Event 14: Event Body 14
|
||||
Event 15: Event Body 15
|
||||
Event 16: Event Body 16
|
||||
Event 17: Event Body 17
|
||||
Event 18: Event Body 18
|
||||
Event 19: Event Body 19
|
||||
Event 20: Event Body 20
|
||||
Event 21: Event Body 21
|
||||
Event 22: Event Body 22
|
||||
Event 23: Event Body 23
|
||||
Event 24: Event Body 24
|
||||
Event 25: Event Body 25
|
||||
Event 26: Event Body 26
|
||||
Event 27: Event Body 27
|
||||
Event 28: Event Body 28
|
||||
Event 29: Event Body 29
|
||||
Event 30: Event Body 30
|
||||
";
|
||||
|
||||
for (int i = 0; i <= 30; i++)
|
||||
{
|
||||
DiagnosticReport.AddEventEntry($"Event {i}", $"Event Body {i}");
|
||||
}
|
||||
|
||||
|
||||
var report = DiagnosticReport.GenerateReport();
|
||||
Assert.AreEqual(k_ExpectedOutput, report);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CheckFullReport()
|
||||
{
|
||||
const string k_ExpectedOutput = @"==== Section One ====
|
||||
Section One Entry One: Simple
|
||||
|
||||
==== Section Two ====
|
||||
Section Two Entry One: Simple
|
||||
|
||||
Section Two Entry Two: (2)
|
||||
FOO=BAR
|
||||
BAZ=100
|
||||
|
||||
==== Last 20 Events ====
|
||||
Event 11: Event Body 11
|
||||
Event 12: Event Body 12
|
||||
Event 13: Event Body 13
|
||||
Event 14: Event Body 14
|
||||
Event 15: Event Body 15
|
||||
Event 16: Event Body 16
|
||||
Event 17: Event Body 17
|
||||
Event 18: Event Body 18
|
||||
Event 19: Event Body 19
|
||||
Event 20: Event Body 20
|
||||
Event 21: Event Body 21
|
||||
Event 22: Event Body 22
|
||||
Event 23: Event Body 23
|
||||
Event 24: Event Body 24
|
||||
Event 25: Event Body 25
|
||||
Event 26: Event Body 26
|
||||
Event 27: Event Body 27
|
||||
Event 28: Event Body 28
|
||||
Event 29: Event Body 29
|
||||
Event 30: Event Body 30
|
||||
";
|
||||
|
||||
var sectionOneHandle = DiagnosticReport.GetSection(k_SectionOneTitle);
|
||||
DiagnosticReport.AddSectionEntry(sectionOneHandle, "Section One Entry One", "Simple");
|
||||
|
||||
for (int i = 0; i <= 30; i++)
|
||||
{
|
||||
DiagnosticReport.AddEventEntry($"Event {i}", $"Event Body {i}");
|
||||
}
|
||||
|
||||
var sectionTwoHandle = DiagnosticReport.GetSection(k_SectionTwoTitle);
|
||||
DiagnosticReport.AddSectionEntry(sectionTwoHandle, "Section Two Entry One", "Simple");
|
||||
DiagnosticReport.AddSectionBreak(sectionTwoHandle);
|
||||
DiagnosticReport.AddSectionEntry(sectionTwoHandle, "Section Two Entry Two", @"(2)
|
||||
FOO=BAR
|
||||
BAZ=100
|
||||
");
|
||||
|
||||
var report = DiagnosticReport.GenerateReport();
|
||||
Debug.Log(report);
|
||||
Assert.AreEqual(k_ExpectedOutput, report);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SectionReportsStayInCreatedOrder()
|
||||
{
|
||||
var sectionOneHandle = DiagnosticReport.GetSection(k_SectionOneTitle);
|
||||
var sectionTwoHandle = DiagnosticReport.GetSection(k_SectionTwoTitle);
|
||||
var reportOne = DiagnosticReport.GenerateReport();
|
||||
|
||||
DiagnosticReport.StartReport();
|
||||
sectionTwoHandle = DiagnosticReport.GetSection(k_SectionTwoTitle);
|
||||
sectionOneHandle = DiagnosticReport.GetSection(k_SectionOneTitle);
|
||||
var reportTwo = DiagnosticReport.GenerateReport();
|
||||
|
||||
Assert.AreNotEqual(reportOne, reportTwo);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6bf97c878af61544d8e27f6a50a1d9ef
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
192
Packages/com.unity.xr.openxr/Tests/Runtime/LoaderTestSetup.cs
Normal file
192
Packages/com.unity.xr.openxr/Tests/Runtime/LoaderTestSetup.cs
Normal file
@@ -0,0 +1,192 @@
|
||||
using System.IO;
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
#endif
|
||||
|
||||
using UnityEngine.TestTools;
|
||||
using UnityEngine.XR.Management;
|
||||
|
||||
|
||||
namespace UnityEngine.XR.TestTooling
|
||||
{
|
||||
internal abstract class LoaderTestSetup<L, S> : ManagementTestSetup, IPrebuildSetup, IPostBuildCleanup
|
||||
where L : XRLoader
|
||||
where S : ScriptableObject
|
||||
{
|
||||
protected abstract string settingsKey { get; }
|
||||
|
||||
protected L loader = null;
|
||||
protected S settings = null;
|
||||
|
||||
public bool IsRunning<T>()
|
||||
where T : class, ISubsystem
|
||||
{
|
||||
return XRGeneralSettings.Instance?.Manager?.activeLoader?.GetLoadedSubsystem<T>()?.running ?? false;
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
T GetOrCreateAsset<T>(string path) where T : UnityEngine.ScriptableObject
|
||||
{
|
||||
T asset = default(T);
|
||||
|
||||
if (!File.Exists(path))
|
||||
{
|
||||
asset = ScriptableObject.CreateInstance<T>();
|
||||
AssetDatabase.CreateAsset(asset, path);
|
||||
AssetDatabase.SaveAssets();
|
||||
}
|
||||
else
|
||||
{
|
||||
asset = AssetDatabase.LoadAssetAtPath<T>(path);
|
||||
}
|
||||
|
||||
return asset as T;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
protected void DestroyLoaderAndSettings()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
|
||||
var path = GetAssetPathForComponents(s_TempSettingsPath);
|
||||
var settingsPath = Path.Combine(path, $"Test_{typeof(S).Name}.asset");
|
||||
AssetDatabase.DeleteAsset(settingsPath);
|
||||
|
||||
var loaderPath = Path.Combine(path, $"Test_{typeof(L).Name}.asset");
|
||||
AssetDatabase.DeleteAsset(loaderPath);
|
||||
#endif
|
||||
}
|
||||
|
||||
protected void SetupLoaderAndSettings()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
// Setup Loader
|
||||
var path = GetAssetPathForComponents(s_TempSettingsPath);
|
||||
var loaderPath = Path.Combine(path, $"Test_{typeof(L).Name}.asset");
|
||||
loader = GetOrCreateAsset<L>(loaderPath);
|
||||
|
||||
if (xrGeneralSettings == null)
|
||||
{
|
||||
xrGeneralSettings = XRGeneralSettings.Instance;
|
||||
testManager = xrGeneralSettings?.Manager ?? null;
|
||||
}
|
||||
|
||||
#pragma warning disable CS0618
|
||||
xrGeneralSettings?.Manager.loaders.Clear();
|
||||
xrGeneralSettings?.Manager.loaders.Add(loader);
|
||||
#pragma warning restore CS0618
|
||||
|
||||
// Setup Settings
|
||||
var settingsPath = Path.Combine(path, $"Test_{typeof(S).Name}.asset");
|
||||
settings = GetOrCreateAsset<S>(settingsPath);
|
||||
|
||||
EditorBuildSettings.AddConfigObject(settingsKey, settings, true);
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
public override void SetupTest()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
base.SetupTest();
|
||||
SetupLoaderAndSettings();
|
||||
#endif
|
||||
}
|
||||
|
||||
protected void RemoveLoaderAndSettings()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
StopAndShutdown();
|
||||
|
||||
if (loader != null)
|
||||
{
|
||||
#pragma warning disable CS0618
|
||||
xrGeneralSettings.Manager.loaders.Remove(loader);
|
||||
#pragma warning restore CS0618
|
||||
}
|
||||
|
||||
EditorBuildSettings.RemoveConfigObject(settingsKey);
|
||||
|
||||
loader = null;
|
||||
#endif
|
||||
}
|
||||
|
||||
public override void TearDownTest()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
RemoveLoaderAndSettings();
|
||||
base.TearDownTest();
|
||||
#endif
|
||||
}
|
||||
|
||||
protected void Initialize()
|
||||
{
|
||||
var manager = XRGeneralSettings.Instance?.Manager;
|
||||
manager?.InitializeLoaderSync();
|
||||
}
|
||||
|
||||
protected void Start()
|
||||
{
|
||||
var manager = XRGeneralSettings.Instance?.Manager;
|
||||
if ((manager?.activeLoader ?? null) != null)
|
||||
{
|
||||
manager.StartSubsystems();
|
||||
}
|
||||
}
|
||||
|
||||
protected void InitializeAndStart()
|
||||
{
|
||||
Initialize();
|
||||
Start();
|
||||
}
|
||||
|
||||
protected void Stop()
|
||||
{
|
||||
var manager = XRGeneralSettings.Instance?.Manager;
|
||||
if (manager != null && manager.activeLoader != null)
|
||||
manager?.StopSubsystems();
|
||||
else if (loader != null)
|
||||
loader.Stop();
|
||||
}
|
||||
|
||||
protected void Shutdown()
|
||||
{
|
||||
var manager = XRGeneralSettings.Instance?.Manager;
|
||||
if (manager != null && manager.activeLoader != null)
|
||||
manager?.DeinitializeLoader();
|
||||
else if (loader != null)
|
||||
loader.Deinitialize();
|
||||
}
|
||||
|
||||
protected void StopAndShutdown()
|
||||
{
|
||||
Stop();
|
||||
Shutdown();
|
||||
}
|
||||
|
||||
protected void RestartProvider()
|
||||
{
|
||||
StopAndShutdown();
|
||||
InitializeAndStart();
|
||||
}
|
||||
|
||||
// IPrebuildSetup - Build time setup
|
||||
public virtual void Setup()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
if (XRGeneralSettings.Instance != null)
|
||||
XRGeneralSettings.Instance.InitManagerOnStart = false;
|
||||
#endif
|
||||
}
|
||||
|
||||
// IPostBuildCleanup - Build time cleanup
|
||||
public virtual void Cleanup()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
if (XRGeneralSettings.Instance != null)
|
||||
XRGeneralSettings.Instance.InitManagerOnStart = true;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c3fab20e4f2e451dbbc1a5856ce147be
|
||||
timeCreated: 1586815402
|
||||
@@ -0,0 +1,130 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
using UnityEditor.XR.Management;
|
||||
#endif
|
||||
using UnityEngine.XR.Management;
|
||||
|
||||
namespace UnityEngine.XR.TestTooling
|
||||
{
|
||||
// Mostly borrowed from XRManagement - this should probably live in that package.
|
||||
internal abstract class ManagementTestSetup
|
||||
{
|
||||
protected static readonly string[] s_TestGeneralSettings = { "Temp", "Test" };
|
||||
protected static readonly string[] s_TempSettingsPath = { "Temp", "Test", "Settings" };
|
||||
|
||||
/// <summary>
|
||||
/// When true, AssetDatabase.AddObjectToAsset will not be called to add XRManagerSettings to XRGeneralSettings.
|
||||
/// </summary>
|
||||
protected virtual bool TestManagerUpgradePath => false;
|
||||
|
||||
protected string testPathToGeneralSettings;
|
||||
protected string testPathToSettings;
|
||||
|
||||
#pragma warning disable 0414 // CS0414: The field 'ManagementTestSetup.currentSettings' is assigned but its value is never used
|
||||
private UnityEngine.Object currentSettings = null;
|
||||
#pragma warning restore 0414
|
||||
|
||||
protected XRManagerSettings testManager = null;
|
||||
protected XRGeneralSettings xrGeneralSettings = null;
|
||||
#if UNITY_EDITOR
|
||||
protected XRGeneralSettingsPerBuildTarget buildTargetSettings = null;
|
||||
#endif
|
||||
|
||||
public virtual void SetupTest()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
var windowTypes = TypeCache.GetTypesDerivedFrom(typeof(EditorWindow));
|
||||
foreach (var wt in windowTypes)
|
||||
{
|
||||
if (wt.Name.Contains("ProjectSettingsWindow"))
|
||||
{
|
||||
var projectSettingsWindow = EditorWindow.GetWindow(wt);
|
||||
if (projectSettingsWindow != null)
|
||||
{
|
||||
projectSettingsWindow.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
testPathToGeneralSettings = GetAssetPathForComponents(s_TestGeneralSettings);
|
||||
testPathToGeneralSettings = Path.Combine(testPathToGeneralSettings, "Test_XRGeneralSettingsPerBuildTarget.asset");
|
||||
if (File.Exists(testPathToGeneralSettings))
|
||||
{
|
||||
AssetDatabase.DeleteAsset(testPathToGeneralSettings);
|
||||
}
|
||||
|
||||
buildTargetSettings = ScriptableObject.CreateInstance<XRGeneralSettingsPerBuildTarget>();
|
||||
AssetDatabase.CreateAsset(buildTargetSettings, testPathToGeneralSettings);
|
||||
|
||||
testPathToSettings = GetAssetPathForComponents(s_TempSettingsPath);
|
||||
testPathToSettings = Path.Combine(testPathToSettings, "Test_XRGeneralSettings.asset");
|
||||
|
||||
if (File.Exists(testPathToSettings))
|
||||
{
|
||||
AssetDatabase.DeleteAsset(testPathToSettings);
|
||||
}
|
||||
|
||||
testManager = ScriptableObject.CreateInstance<XRManagerSettings>();
|
||||
|
||||
xrGeneralSettings = ScriptableObject.CreateInstance<XRGeneralSettings>() as XRGeneralSettings;
|
||||
XRGeneralSettings.Instance = xrGeneralSettings;
|
||||
|
||||
xrGeneralSettings.Manager = testManager;
|
||||
buildTargetSettings.SetSettingsForBuildTarget(BuildTargetGroup.Standalone, xrGeneralSettings);
|
||||
buildTargetSettings.SetSettingsForBuildTarget(BuildPipeline.GetBuildTargetGroup(UnityEditor.EditorUserBuildSettings.activeBuildTarget), xrGeneralSettings);
|
||||
|
||||
AssetDatabase.CreateAsset(xrGeneralSettings, testPathToSettings);
|
||||
AssetDatabase.AddObjectToAsset(testManager, xrGeneralSettings);
|
||||
|
||||
AssetDatabase.SaveAssets();
|
||||
|
||||
EditorBuildSettings.AddConfigObject(XRGeneralSettings.k_SettingsKey, buildTargetSettings, true);
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
public virtual void TearDownTest()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
EditorBuildSettings.RemoveConfigObject(XRGeneralSettings.k_SettingsKey);
|
||||
buildTargetSettings = null;
|
||||
testManager = null;
|
||||
xrGeneralSettings = null;
|
||||
AssetDatabase.DeleteAsset(Path.Combine("Assets", "Temp"));
|
||||
#endif
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
public static string GetAssetPathForComponents(string[] pathComponents, string root = "Assets")
|
||||
{
|
||||
if (pathComponents.Length <= 0)
|
||||
return null;
|
||||
|
||||
string path = root;
|
||||
foreach (var pc in pathComponents)
|
||||
{
|
||||
string subFolder = Path.Combine(path, pc);
|
||||
bool shouldCreate = true;
|
||||
foreach (var f in AssetDatabase.GetSubFolders(path))
|
||||
{
|
||||
if (String.Compare(Path.GetFullPath(f), Path.GetFullPath(subFolder), true) == 0)
|
||||
{
|
||||
shouldCreate = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (shouldCreate)
|
||||
AssetDatabase.CreateFolder(path, pc);
|
||||
path = subFolder;
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 205f1850c21694e34b2b06a191e3b27b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,188 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using NUnit.Framework;
|
||||
using UnityEngine.TestTools;
|
||||
using UnityEngine.TestTools.Utils;
|
||||
using UnityEngine.XR.OpenXR.Features;
|
||||
using UnityEngine.XR.OpenXR.Features.Mock;
|
||||
using UnityEngine.XR.OpenXR;
|
||||
using UnityEngine.XR.OpenXR.NativeTypes;
|
||||
using ProviderXRStats = UnityEngine.XR.Provider.XRStats;
|
||||
|
||||
namespace UnityEngine.XR.OpenXR.Tests
|
||||
{
|
||||
internal class MetaPerformanceMetricsTests : OpenXRLoaderSetup
|
||||
{
|
||||
private static readonly string kAppCPUFrametimeStr = "/perfmetrics_meta/app/cpu_frametime";
|
||||
private static readonly string kAppGPUFrametimeStr = "/perfmetrics_meta/app/gpu_frametime";
|
||||
private static readonly string kAppMotionToPhotonLatencyStr =
|
||||
"/perfmetrics_meta/app/motion_to_photon_latency";
|
||||
private static readonly string kCompositorCPUFrametimeStr =
|
||||
"/perfmetrics_meta/compositor/cpu_frametime";
|
||||
private static readonly string kCompositorGPUFrametimeStr =
|
||||
"/perfmetrics_meta/compositor/gpu_frametime";
|
||||
private static readonly string kCompositorDroppedFrameCountStr =
|
||||
"/perfmetrics_meta/compositor/dropped_frame_count";
|
||||
private static readonly string kDeviceCPUUtilizationAvgStr =
|
||||
"/perfmetrics_meta/device/cpu_utilization_average";
|
||||
private static readonly string kDeviceCPUUtilizationWorstStr =
|
||||
"/perfmetrics_meta/device/cpu_utilization_worst";
|
||||
private static readonly string kDeviceGPUUtilizationStr =
|
||||
"/perfmetrics_meta/device/gpu_utilization";
|
||||
|
||||
private readonly List<string> xrPathStrings = new List<string>
|
||||
{
|
||||
kAppCPUFrametimeStr,
|
||||
kAppGPUFrametimeStr,
|
||||
kAppGPUFrametimeStr, // because we have 2 consumers
|
||||
kAppMotionToPhotonLatencyStr,
|
||||
kCompositorCPUFrametimeStr,
|
||||
kCompositorGPUFrametimeStr,
|
||||
kCompositorGPUFrametimeStr, // because we have 2 consumers
|
||||
kCompositorDroppedFrameCountStr,
|
||||
kCompositorDroppedFrameCountStr, // because we have 2 consumers
|
||||
kDeviceCPUUtilizationAvgStr,
|
||||
kDeviceCPUUtilizationWorstStr,
|
||||
kDeviceGPUUtilizationStr
|
||||
};
|
||||
|
||||
private static readonly string kUnityStatsAppCpuTime = "perfmetrics.appcputime";
|
||||
private static readonly string kUnityStatsAppGpuTime = "perfmetrics.appgputime";
|
||||
private static readonly string kUnityStatsAppGpuTimeFunc = "GPUAppLastFrameTime";
|
||||
private static readonly string kUnityStatsCompositorCpuTime =
|
||||
"perfmetrics.compositorcputime";
|
||||
private static readonly string kUnityStatsCompositorGpuTime =
|
||||
"perfmetrics.compositorgputime";
|
||||
private static readonly string kUnityStatsCompositorGpuTimeFunc =
|
||||
"GPUCompositorLastFrameTime";
|
||||
private static readonly string kUnityStatsGpuUtil = "perfmetrics.gpuutil";
|
||||
private static readonly string kUnityStatsCpuUtilAvg = "perfmetrics.cpuutilavg";
|
||||
private static readonly string kUnityStatsCpuUtilWorst = "perfmetrics.cpuutilworst";
|
||||
private static readonly string kUnityStatsDroppedFrameCount = "appstats.compositordroppedframes";
|
||||
private static readonly string kUnityStatsDroppedFrameCountFunc = "droppedFrameCount";
|
||||
private static readonly string kUnityStatsMotionToPhotonFunc = "motionToPhoton";
|
||||
|
||||
private readonly List<string> unityStatStrings = new List<string>
|
||||
{
|
||||
kUnityStatsAppCpuTime,
|
||||
kUnityStatsAppGpuTime,
|
||||
kUnityStatsAppGpuTimeFunc,
|
||||
kUnityStatsCompositorCpuTime,
|
||||
kUnityStatsCompositorGpuTime,
|
||||
kUnityStatsCompositorGpuTimeFunc,
|
||||
kUnityStatsGpuUtil,
|
||||
kUnityStatsCpuUtilAvg,
|
||||
kUnityStatsCpuUtilWorst,
|
||||
kUnityStatsDroppedFrameCount,
|
||||
kUnityStatsDroppedFrameCountFunc,
|
||||
kUnityStatsMotionToPhotonFunc
|
||||
};
|
||||
|
||||
private readonly Dictionary<string, XrPerformanceMetricsCounterUnitMETA> unitMap =
|
||||
new Dictionary<string, XrPerformanceMetricsCounterUnitMETA>()
|
||||
{
|
||||
{
|
||||
kAppCPUFrametimeStr,
|
||||
XrPerformanceMetricsCounterUnitMETA.XR_PERFORMANCE_METRICS_COUNTER_UNIT_MILLISECONDS_META
|
||||
},
|
||||
{
|
||||
kAppGPUFrametimeStr,
|
||||
XrPerformanceMetricsCounterUnitMETA.XR_PERFORMANCE_METRICS_COUNTER_UNIT_MILLISECONDS_META
|
||||
},
|
||||
{
|
||||
kAppMotionToPhotonLatencyStr,
|
||||
XrPerformanceMetricsCounterUnitMETA.XR_PERFORMANCE_METRICS_COUNTER_UNIT_MILLISECONDS_META
|
||||
},
|
||||
{
|
||||
kCompositorCPUFrametimeStr,
|
||||
XrPerformanceMetricsCounterUnitMETA.XR_PERFORMANCE_METRICS_COUNTER_UNIT_MILLISECONDS_META
|
||||
},
|
||||
{
|
||||
kCompositorGPUFrametimeStr,
|
||||
XrPerformanceMetricsCounterUnitMETA.XR_PERFORMANCE_METRICS_COUNTER_UNIT_MILLISECONDS_META
|
||||
},
|
||||
{
|
||||
kCompositorDroppedFrameCountStr,
|
||||
XrPerformanceMetricsCounterUnitMETA.XR_PERFORMANCE_METRICS_COUNTER_UNIT_GENERIC_META
|
||||
},
|
||||
{
|
||||
kDeviceCPUUtilizationAvgStr,
|
||||
XrPerformanceMetricsCounterUnitMETA.XR_PERFORMANCE_METRICS_COUNTER_UNIT_PERCENTAGE_META
|
||||
},
|
||||
{
|
||||
kDeviceCPUUtilizationWorstStr,
|
||||
XrPerformanceMetricsCounterUnitMETA.XR_PERFORMANCE_METRICS_COUNTER_UNIT_PERCENTAGE_META
|
||||
},
|
||||
{
|
||||
kDeviceGPUUtilizationStr,
|
||||
XrPerformanceMetricsCounterUnitMETA.XR_PERFORMANCE_METRICS_COUNTER_UNIT_PERCENTAGE_META
|
||||
}
|
||||
};
|
||||
|
||||
private enum XrPerformanceMetricsCounterUnitMETA
|
||||
{
|
||||
XR_PERFORMANCE_METRICS_COUNTER_UNIT_GENERIC_META = 0,
|
||||
XR_PERFORMANCE_METRICS_COUNTER_UNIT_PERCENTAGE_META = 1,
|
||||
XR_PERFORMANCE_METRICS_COUNTER_UNIT_MILLISECONDS_META = 2,
|
||||
XR_PERFORMANCE_METRICS_COUNTER_UNIT_BYTES_META = 3,
|
||||
XR_PERFORMANCE_METRICS_COUNTER_UNIT_HERTZ_META = 4,
|
||||
XR_PERFORMANCE_METRICS_COUNTER_UNIT_MAX_ENUM_META = 0x7FFFFFFF
|
||||
}
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator TestAllMetrics()
|
||||
{
|
||||
base.InitializeAndStart();
|
||||
yield return new WaitForXrFrame(2);
|
||||
|
||||
List<XRDisplaySubsystem> displays = new List<XRDisplaySubsystem>();
|
||||
SubsystemManager.GetSubsystems(displays);
|
||||
|
||||
Assert.That(displays.Count, Is.EqualTo(1));
|
||||
|
||||
// insert a -1 dummy value to ensure we're ready when we start querying
|
||||
for (int i = -1; i < 5; i++)
|
||||
{
|
||||
foreach (string xrPathString in xrPathStrings)
|
||||
{
|
||||
MockRuntime.MetaPerformanceMetrics_SeedCounterOnce_Float(
|
||||
xrPathString,
|
||||
(float)i,
|
||||
(uint)unitMap[xrPathString]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// wait for results to come available.
|
||||
bool didSync = false;
|
||||
for (int i = 0; i < 60; i++)
|
||||
{
|
||||
if (ProviderXRStats.TryGetStat(displays[0], kUnityStatsAppCpuTime, out var sync))
|
||||
{
|
||||
if (sync < 0)
|
||||
{
|
||||
didSync = true;
|
||||
break;
|
||||
}
|
||||
if (sync > 0)
|
||||
Assert.Fail("did not receive -1.f sync stat");
|
||||
}
|
||||
yield return new WaitForXrFrame();
|
||||
}
|
||||
Assert.True(didSync, "did not receive -1.f sync stat");
|
||||
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
yield return new WaitForXrFrame();
|
||||
foreach (string unityStatString in unityStatStrings)
|
||||
{
|
||||
Assert.True(
|
||||
ProviderXRStats.TryGetStat(displays[0], unityStatString, out var stat),
|
||||
"did not get stat for " + unityStatString
|
||||
);
|
||||
Assert.That(stat, Is.EqualTo((float)i).Within(0.001));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 30581af4aa5c63b4493cfae0e77f36d8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,226 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using UnityEngine.InputSystem.Controls;
|
||||
using UnityEngine.InputSystem.Layouts;
|
||||
using UnityEngine.InputSystem.XR;
|
||||
using UnityEngine.Scripting;
|
||||
using UnityEngine.XR.OpenXR.Features;
|
||||
using UnityEngine.XR.OpenXR.Features.Interactions;
|
||||
using UnityEngine.XR.OpenXR.Input;
|
||||
|
||||
#if USE_INPUT_SYSTEM_POSE_CONTROL
|
||||
using PoseControl = UnityEngine.InputSystem.XR.PoseControl;
|
||||
#else
|
||||
using PoseControl = UnityEngine.XR.OpenXR.Input.PoseControl;
|
||||
#endif
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
#endif
|
||||
|
||||
namespace UnityEngine.XR.OpenXR.Tests
|
||||
{
|
||||
/// <summary>
|
||||
/// Mock Interaction feature used in tests to test interaction features.
|
||||
/// </summary>
|
||||
#if UNITY_EDITOR
|
||||
[UnityEditor.XR.OpenXR.Features.OpenXRFeature(UiName = "Mock Interaction Feature",
|
||||
BuildTargetGroups = new[] { BuildTargetGroup.Standalone, BuildTargetGroup.Android },
|
||||
Company = "Unity",
|
||||
Desc = "Mock interaction feature used for testing interactions",
|
||||
DocumentationLink = Constants.k_DocumentationManualURL + "features/khrsimplecontrollerprofile.html",
|
||||
OpenxrExtensionStrings = "",
|
||||
Version = "0.0.1",
|
||||
Category = UnityEditor.XR.OpenXR.Features.FeatureCategory.Interaction,
|
||||
Hidden = true,
|
||||
Priority = int.MinValue,
|
||||
FeatureId = featureId)]
|
||||
#endif
|
||||
internal class MockInteractionFeature : OpenXRInteractionFeature
|
||||
{
|
||||
public const string featureId = "com.unity.openxr.feature.input.mockinteraction";
|
||||
|
||||
private const string kDeviceLocalizedName = "Mock Controller OpenXR";
|
||||
|
||||
/// <summary>
|
||||
/// Mock controller used in testing of interaction features
|
||||
/// </summary>
|
||||
[Preserve, InputControlLayout(displayName = kDeviceLocalizedName, commonUsages = new[] { "LeftHand", "RightHand" })]
|
||||
public class MockController : XRControllerWithRumble
|
||||
{
|
||||
[Preserve, InputControl]
|
||||
public AxisControl trigger { get; private set; }
|
||||
|
||||
[Preserve, InputControl]
|
||||
public ButtonControl triggerPressed { get; private set; }
|
||||
|
||||
[Preserve, InputControl]
|
||||
public Vector2Control thumbstick { get; private set; }
|
||||
|
||||
[Preserve, InputControl(offset = 0)]
|
||||
public PoseControl devicePose { get; private set; }
|
||||
|
||||
[Preserve, InputControl(offset = 2)]
|
||||
public new ButtonControl isTracked { get; private set; }
|
||||
|
||||
[Preserve, InputControl(offset = 4)]
|
||||
public new IntegerControl trackingState { get; private set; }
|
||||
|
||||
[Preserve, InputControl]
|
||||
public new Vector3Control devicePosition { get; private set; }
|
||||
|
||||
[Preserve, InputControl]
|
||||
public new QuaternionControl deviceRotation { get; private set; }
|
||||
|
||||
[Preserve, InputControl]
|
||||
public HapticControl haptic { get; private set; }
|
||||
|
||||
protected override void FinishSetup()
|
||||
{
|
||||
base.FinishSetup();
|
||||
|
||||
trigger = GetChildControl<AxisControl>("trigger");
|
||||
triggerPressed = GetChildControl<ButtonControl>("triggerPressed");
|
||||
thumbstick = GetChildControl<Vector2Control>("thumbstick");
|
||||
|
||||
devicePose = GetChildControl<PoseControl>("devicePose");
|
||||
isTracked = GetChildControl<ButtonControl>("isTracked");
|
||||
trackingState = GetChildControl<IntegerControl>("trackingState");
|
||||
devicePosition = GetChildControl<Vector3Control>("devicePosition");
|
||||
deviceRotation = GetChildControl<QuaternionControl>("deviceRotation");
|
||||
|
||||
haptic = GetChildControl<HapticControl>("haptic");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get/Set the action map config that should be used to register actions for this profile. If no
|
||||
/// config is specified the default action map config will be used.
|
||||
/// </summary>
|
||||
public ActionMapConfig actionMapConfig { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Registers the <see cref="MockController"/> layout with the Input System.
|
||||
/// </summary>
|
||||
protected override void RegisterDeviceLayout()
|
||||
{
|
||||
InputSystem.InputSystem.RegisterLayout(typeof(MockController),
|
||||
matches: new InputDeviceMatcher()
|
||||
.WithInterface(XRUtilities.InterfaceMatchAnyVersion)
|
||||
.WithProduct(kDeviceLocalizedName));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the <see cref="MockController"/> layout from the Input System.
|
||||
/// </summary>
|
||||
protected override void UnregisterDeviceLayout()
|
||||
{
|
||||
InputSystem.InputSystem.RemoveLayout(nameof(MockController));
|
||||
}
|
||||
|
||||
protected override string GetDeviceLayoutName()
|
||||
{
|
||||
return nameof(MockController);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a default action map config for the controller
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public ActionMapConfig CreateDefaultActionMapConfig() =>
|
||||
new ActionMapConfig()
|
||||
{
|
||||
name = "mockcontroller",
|
||||
localizedName = kDeviceLocalizedName,
|
||||
manufacturer = "Unity",
|
||||
serialNumber = "",
|
||||
desiredInteractionProfile = "/interaction_profiles/unity/mock_controller",
|
||||
deviceInfos = new List<DeviceConfig>
|
||||
{
|
||||
new DeviceConfig
|
||||
{
|
||||
userPath = UserPaths.leftHand,
|
||||
characteristics = InputDeviceCharacteristics.Left | InputDeviceCharacteristics.TrackedDevice | InputDeviceCharacteristics.HeldInHand | InputDeviceCharacteristics.Controller
|
||||
},
|
||||
new DeviceConfig
|
||||
{
|
||||
userPath = UserPaths.rightHand,
|
||||
characteristics = InputDeviceCharacteristics.Right | InputDeviceCharacteristics.TrackedDevice | InputDeviceCharacteristics.HeldInHand | InputDeviceCharacteristics.Controller
|
||||
}
|
||||
},
|
||||
actions = new List<ActionConfig>
|
||||
{
|
||||
new ActionConfig
|
||||
{
|
||||
name = nameof(MockController.triggerPressed),
|
||||
localizedName = "Trigger Pressed",
|
||||
type = ActionType.Binary,
|
||||
bindings = new List<ActionBinding>
|
||||
{
|
||||
new ActionBinding
|
||||
{
|
||||
interactionPath = "/input/trigger/click"
|
||||
}
|
||||
}
|
||||
},
|
||||
new ActionConfig
|
||||
{
|
||||
name = nameof(MockController.trigger),
|
||||
localizedName = "Trigger",
|
||||
type = ActionType.Axis1D,
|
||||
bindings = new List<ActionBinding>
|
||||
{
|
||||
new ActionBinding
|
||||
{
|
||||
interactionPath = "/input/trigger/value"
|
||||
}
|
||||
}
|
||||
},
|
||||
new ActionConfig
|
||||
{
|
||||
name = nameof(MockController.thumbstick),
|
||||
localizedName = "Thumbstick",
|
||||
type = ActionType.Axis2D,
|
||||
bindings = new List<ActionBinding>
|
||||
{
|
||||
new ActionBinding
|
||||
{
|
||||
interactionPath = "/input/thumbstick/value"
|
||||
}
|
||||
}
|
||||
},
|
||||
new ActionConfig
|
||||
{
|
||||
name = nameof(MockController.devicePose),
|
||||
localizedName = "Grip",
|
||||
type = ActionType.Pose,
|
||||
bindings = new List<ActionBinding>
|
||||
{
|
||||
new ActionBinding
|
||||
{
|
||||
interactionPath = "/input/grip/pose"
|
||||
}
|
||||
}
|
||||
},
|
||||
new ActionConfig
|
||||
{
|
||||
name = nameof(MockController.haptic),
|
||||
localizedName = "Haptic Output",
|
||||
type = ActionType.Vibrate,
|
||||
bindings = new List<ActionBinding>
|
||||
{
|
||||
new ActionBinding
|
||||
{
|
||||
interactionPath = "/output/haptic"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
protected override void RegisterActionMapsWithRuntime()
|
||||
{
|
||||
AddActionMap(actionMapConfig ?? CreateDefaultActionMapConfig());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 19db6adf5be3433989f34a1788172b2c
|
||||
timeCreated: 1621029060
|
||||
438
Packages/com.unity.xr.openxr/Tests/Runtime/MockRuntimeTests.cs
Normal file
438
Packages/com.unity.xr.openxr/Tests/Runtime/MockRuntimeTests.cs
Normal file
@@ -0,0 +1,438 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using NUnit.Framework;
|
||||
using UnityEngine.XR.OpenXR.Features;
|
||||
using UnityEngine.XR.OpenXR.Features.Mock;
|
||||
using UnityEngine.TestTools;
|
||||
using UnityEngine.TestTools.Utils;
|
||||
using UnityEngine.XR.OpenXR.NativeTypes;
|
||||
|
||||
namespace UnityEngine.XR.OpenXR.Tests
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines tests that validate the MockRuntime itself.
|
||||
/// </summary>
|
||||
internal class MockRuntimeTests : OpenXRLoaderSetup
|
||||
{
|
||||
[UnityTest]
|
||||
public IEnumerator TransitionToState()
|
||||
{
|
||||
InitializeAndStart();
|
||||
yield return new WaitForXrFrame();
|
||||
|
||||
Assert.AreEqual(MockRuntime.sessionState, XrSessionState.Focused, "MockRuntime must be in focused state for this test to work correctly");
|
||||
Assert.IsTrue(MockRuntime.TransitionToState(XrSessionState.Visible, false), "Failed to transition to visible state");
|
||||
|
||||
Assert.AreEqual(MockRuntime.sessionState, XrSessionState.Visible);
|
||||
}
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator TransitionToStateForced()
|
||||
{
|
||||
InitializeAndStart();
|
||||
yield return new WaitForXrFrame();
|
||||
|
||||
Assert.IsFalse(MockRuntime.TransitionToState(XrSessionState.Synchronized, false), "Synchronized state must be an invalid transition for this test to be valid");
|
||||
Assert.IsTrue(MockRuntime.TransitionToState(XrSessionState.Synchronized, true), "Force state transition should not return false");
|
||||
|
||||
yield return new WaitForXrFrame();
|
||||
|
||||
Assert.IsTrue(MockRuntime.sessionState == XrSessionState.Synchronized);
|
||||
}
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator CreateSessionFailure()
|
||||
{
|
||||
bool sawCreateSession = false;
|
||||
MockRuntime.Instance.TestCallback = (methodName, param) =>
|
||||
{
|
||||
if (methodName == nameof(OpenXRFeature.OnSessionCreate))
|
||||
{
|
||||
sawCreateSession = true;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
MockRuntime.SetFunctionCallback("xrCreateSession", (name) => XrResult.RuntimeFailure);
|
||||
|
||||
base.InitializeAndStart();
|
||||
|
||||
yield return null;
|
||||
|
||||
Assert.IsFalse(sawCreateSession);
|
||||
}
|
||||
|
||||
static XrResult[] beginSessionSuccessResults = new XrResult[]
|
||||
{
|
||||
XrResult.Success,
|
||||
XrResult.LossPending
|
||||
};
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator BeginSessionSuccessWithValues([ValueSource("beginSessionSuccessResults")]
|
||||
XrResult successResult)
|
||||
{
|
||||
var states = new List<XrSessionState>();
|
||||
MockRuntime.Instance.TestCallback = (methodName, param) =>
|
||||
{
|
||||
if (methodName == nameof(OpenXRFeature.OnSessionStateChange))
|
||||
{
|
||||
var newState = (XrSessionState)((MockRuntime.XrSessionStateChangedParams)param).NewState;
|
||||
states.Add(newState);
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
MockRuntime.SetFunctionCallback("xrBeginSession", (name) => successResult);
|
||||
|
||||
base.InitializeAndStart();
|
||||
|
||||
yield return null;
|
||||
|
||||
Assert.IsTrue(base.IsRunning<XRDisplaySubsystem>());
|
||||
|
||||
switch (successResult)
|
||||
{
|
||||
case XrResult.Success:
|
||||
Assert.IsTrue(states.Contains(XrSessionState.Ready));
|
||||
Assert.IsTrue(states.Contains(XrSessionState.Synchronized));
|
||||
Assert.IsTrue(states.Contains(XrSessionState.Visible));
|
||||
Assert.IsTrue(states.Contains(XrSessionState.Focused));
|
||||
break;
|
||||
|
||||
case XrResult.LossPending:
|
||||
Assert.IsTrue(states.Contains(XrSessionState.Ready));
|
||||
Assert.IsFalse(states.Contains(XrSessionState.Synchronized));
|
||||
Assert.IsFalse(states.Contains(XrSessionState.Visible));
|
||||
Assert.IsFalse(states.Contains(XrSessionState.Focused));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator BeginSessionFailure()
|
||||
{
|
||||
var states = new List<XrSessionState>();
|
||||
MockRuntime.Instance.TestCallback = (methodName, param) =>
|
||||
{
|
||||
if (methodName == nameof(OpenXRFeature.OnSessionStateChange))
|
||||
{
|
||||
var newState = (XrSessionState)((MockRuntime.XrSessionStateChangedParams)param).NewState;
|
||||
states.Add(newState);
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
MockRuntime.SetFunctionCallback("xrBeginSession", (name) => XrResult.RuntimeFailure);
|
||||
|
||||
InitializeAndStart();
|
||||
|
||||
yield return null;
|
||||
|
||||
Assert.IsTrue(base.IsRunning<XRDisplaySubsystem>());
|
||||
|
||||
Assert.IsTrue(states.Contains(XrSessionState.Ready));
|
||||
Assert.IsFalse(states.Contains(XrSessionState.Synchronized));
|
||||
Assert.IsFalse(states.Contains(XrSessionState.Visible));
|
||||
Assert.IsFalse(states.Contains(XrSessionState.Focused));
|
||||
}
|
||||
|
||||
#if false
|
||||
// TEST WAS UNSTABLE, DISABLING FOR RELEASE ONLY
|
||||
[UnityTest]
|
||||
public IEnumerator TestRequestExitShutsdownSubsystems()
|
||||
{
|
||||
bool sawSessionDestroy = false;
|
||||
MockRuntime.Instance.TestCallback = (methodName, param) =>
|
||||
{
|
||||
if (methodName == nameof(OpenXRFeature.OnSessionDestroy))
|
||||
{
|
||||
sawSessionDestroy = true;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
AddExtension(MockRuntime.XR_UNITY_mock_test);
|
||||
|
||||
base.InitializeAndStart();
|
||||
|
||||
yield return null;
|
||||
|
||||
Assert.IsTrue(base.IsRunning<XRDisplaySubsystem>());
|
||||
|
||||
var wait = new WaitForLoaderShutdown(Loader);
|
||||
MockDriver.RequestExitSession(MockRuntime.Instance.XrSession);
|
||||
|
||||
yield return wait;
|
||||
|
||||
Assert.IsTrue(sawSessionDestroy);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator RequestExitSession()
|
||||
{
|
||||
InitializeAndStart();
|
||||
|
||||
// Wait for a single XrFrame to make sure OpenXR is up and running
|
||||
yield return new WaitForXrFrame();
|
||||
|
||||
// Request the session exit which should shutdown OpenXR
|
||||
MockRuntime.RequestExitSession();
|
||||
|
||||
// Wait for OpenXR to shutdown
|
||||
yield return new WaitForLoaderShutdown();
|
||||
}
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator CauseInstanceLoss()
|
||||
{
|
||||
bool instanceLost = false;
|
||||
MockRuntime.Instance.TestCallback = (methodName, param) =>
|
||||
{
|
||||
if (methodName == nameof(OpenXRFeature.OnInstanceLossPending))
|
||||
{
|
||||
instanceLost = true;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
InitializeAndStart();
|
||||
|
||||
yield return null;
|
||||
|
||||
MockRuntime.CauseInstanceLoss();
|
||||
|
||||
yield return new WaitForLoaderShutdown();
|
||||
|
||||
Assert.IsTrue(instanceLost);
|
||||
}
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator DisplayTransparent()
|
||||
{
|
||||
AddExtension(MockRuntime.XR_UNITY_mock_test);
|
||||
|
||||
MockRuntime.Instance.TestCallback = (methodName, param) =>
|
||||
{
|
||||
if (methodName == nameof(OpenXRFeature.OnInstanceCreate))
|
||||
{
|
||||
MockRuntime.ChooseEnvironmentBlendMode(XrEnvironmentBlendMode.Additive);
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
base.InitializeAndStart();
|
||||
|
||||
yield return null;
|
||||
var displays = new List<XRDisplaySubsystem>();
|
||||
SubsystemManager.GetSubsystems(displays);
|
||||
Assert.AreEqual(false, displays[0].displayOpaque);
|
||||
}
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator DisplayOpaque()
|
||||
{
|
||||
base.InitializeAndStart();
|
||||
|
||||
yield return null;
|
||||
var displays = new List<XRDisplaySubsystem>();
|
||||
SubsystemManager.GetSubsystems(displays);
|
||||
Assert.AreEqual(true, displays[0].displayOpaque);
|
||||
}
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator MultipleEnvironmentBlendModes()
|
||||
{
|
||||
//Mock available environmentBlendModes are Opaque and Additive in mock_runtime.cpp EnumerateEnvironmentBlendModes.
|
||||
AddExtension(MockRuntime.XR_UNITY_mock_test);
|
||||
base.InitializeAndStart();
|
||||
yield return null;
|
||||
|
||||
//check default mode is Opaque.
|
||||
Assert.AreEqual(XrEnvironmentBlendMode.Opaque, MockRuntime.GetXrEnvironmentBlendMode());
|
||||
|
||||
//Switch to another supported mode - Additive.
|
||||
MockRuntime.ChooseEnvironmentBlendMode(XrEnvironmentBlendMode.Additive);
|
||||
yield return new WaitForXrFrame(2);
|
||||
Assert.AreEqual(XrEnvironmentBlendMode.Additive, MockRuntime.GetXrEnvironmentBlendMode());
|
||||
|
||||
//Set to unsupported mode - Alpha_blend
|
||||
MockRuntime.ChooseEnvironmentBlendMode(XrEnvironmentBlendMode.AlphaBlend);
|
||||
yield return new WaitForXrFrame(2);
|
||||
Assert.AreNotEqual(XrEnvironmentBlendMode.AlphaBlend, MockRuntime.GetXrEnvironmentBlendMode());
|
||||
}
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator BoundaryPoints()
|
||||
{
|
||||
AddExtension(MockRuntime.XR_UNITY_mock_test);
|
||||
|
||||
base.InitializeAndStart();
|
||||
|
||||
yield return null;
|
||||
|
||||
Assert.IsTrue(base.IsRunning<XRInputSubsystem>(), "Input subsystem failed to properly start!");
|
||||
|
||||
MockRuntime.SetReferenceSpaceBounds(XrReferenceSpaceType.Stage, new Vector2 { x = 1.0f, y = 3.0f });
|
||||
|
||||
yield return null;
|
||||
|
||||
var input = Loader.GetLoadedSubsystem<XRInputSubsystem>();
|
||||
Assert.That(() => input, Is.Not.Null);
|
||||
|
||||
input.TrySetTrackingOriginMode(TrackingOriginModeFlags.Floor);
|
||||
|
||||
yield return null;
|
||||
|
||||
var points = new List<Vector3>();
|
||||
Assert.IsTrue(input.TryGetBoundaryPoints(points), "Failed to get boundary points!");
|
||||
Assert.That(() => points.Count, Is.EqualTo(4), "Incorrect number of boundary points received!");
|
||||
|
||||
var comparer = new Vector3EqualityComparer(10e-6f);
|
||||
|
||||
Assert.That(points[0], Is.EqualTo(new Vector3 { x = -0.5f, y = 0.0f, z = -1.5f }).Using(comparer));
|
||||
Assert.That(points[1], Is.EqualTo(new Vector3 { x = -0.5f, y = 0.0f, z = 1.5f }).Using(comparer));
|
||||
Assert.That(points[2], Is.EqualTo(new Vector3 { x = 0.5f, y = 0.0f, z = 1.5f }).Using(comparer));
|
||||
Assert.That(points[3], Is.EqualTo(new Vector3 { x = 0.5f, y = 0.0f, z = -1.5f }).Using(comparer));
|
||||
}
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator NoBoundaryPoints()
|
||||
{
|
||||
AddExtension(MockRuntime.XR_UNITY_mock_test);
|
||||
|
||||
base.InitializeAndStart();
|
||||
|
||||
yield return null;
|
||||
|
||||
Assert.IsTrue(base.IsRunning<XRInputSubsystem>(), "Input subsystem failed to properly start!");
|
||||
|
||||
var input = Loader.GetLoadedSubsystem<XRInputSubsystem>();
|
||||
Assert.That(() => input, Is.Not.Null);
|
||||
|
||||
input.TrySetTrackingOriginMode(TrackingOriginModeFlags.Floor);
|
||||
|
||||
yield return null;
|
||||
|
||||
var points = new List<Vector3>();
|
||||
input.TryGetBoundaryPoints(points);
|
||||
Assert.That(() => points.Count, Is.EqualTo(0), "Incorrect number of boundary points received!");
|
||||
}
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator BoundryPointsForTrackingOrigin()
|
||||
{
|
||||
AddExtension(MockRuntime.XR_UNITY_mock_test);
|
||||
|
||||
base.InitializeAndStart();
|
||||
|
||||
yield return null;
|
||||
|
||||
Assert.IsTrue(base.IsRunning<XRInputSubsystem>(), "Input subsystem failed to properly start!");
|
||||
|
||||
MockRuntime.SetReferenceSpaceBounds(XrReferenceSpaceType.Stage, new Vector2 { x = 1.0f, y = 3.0f });
|
||||
|
||||
yield return null;
|
||||
|
||||
var input = Loader.GetLoadedSubsystem<XRInputSubsystem>();
|
||||
Assert.That(() => input, Is.Not.Null);
|
||||
|
||||
input.TrySetTrackingOriginMode(TrackingOriginModeFlags.Floor);
|
||||
|
||||
yield return null;
|
||||
|
||||
var points = new List<Vector3>();
|
||||
Assert.IsTrue(input.TryGetBoundaryPoints(points), "Failed to get boundary points!");
|
||||
Assert.That(() => points.Count, Is.EqualTo(4), "Incorrect number of boundary points received!");
|
||||
|
||||
input.TrySetTrackingOriginMode(TrackingOriginModeFlags.Device);
|
||||
|
||||
yield return null;
|
||||
|
||||
points.Clear();
|
||||
input.TryGetBoundaryPoints(points);
|
||||
Assert.That(() => points.Count, Is.EqualTo(0), "Incorrect number of boundary points received!");
|
||||
}
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator BeforeFunctionCallbackSuccess()
|
||||
{
|
||||
var createInstanceCalled = false;
|
||||
|
||||
MockRuntime.SetFunctionCallback("xrCreateInstance", (name) =>
|
||||
{
|
||||
createInstanceCalled = true;
|
||||
return XrResult.Success;
|
||||
});
|
||||
|
||||
InitializeAndStart();
|
||||
|
||||
yield return new WaitForXrFrame(1);
|
||||
|
||||
Assert.IsTrue(createInstanceCalled, "xrCreateInstance callback was not called");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void BeforeFunctionCallbackError()
|
||||
{
|
||||
var createInstanceCalled = false;
|
||||
|
||||
MockRuntime.SetFunctionCallback("xrCreateInstance", (name) =>
|
||||
{
|
||||
createInstanceCalled = true;
|
||||
return XrResult.RuntimeFailure;
|
||||
});
|
||||
|
||||
LogAssert.ignoreFailingMessages = true;
|
||||
InitializeAndStart();
|
||||
LogAssert.ignoreFailingMessages = false;
|
||||
|
||||
Assert.IsTrue(OpenXRLoaderBase.Instance == null, "OpenXR instance should have failed to initialize");
|
||||
|
||||
Assert.IsTrue(createInstanceCalled, "xrCreateInstance callback was not called");
|
||||
}
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator AfterFunctionCallback()
|
||||
{
|
||||
var createInstanceCalled = false;
|
||||
var createInstanceSuccess = false;
|
||||
|
||||
MockRuntime.SetFunctionCallback("xrCreateInstance", (name, result) =>
|
||||
{
|
||||
createInstanceCalled = true;
|
||||
createInstanceSuccess = result == XrResult.Success;
|
||||
});
|
||||
|
||||
InitializeAndStart();
|
||||
|
||||
yield return new WaitForXrFrame(1);
|
||||
|
||||
Assert.IsTrue(createInstanceCalled, "xrCreateInstance callback was not called");
|
||||
Assert.IsTrue(createInstanceSuccess, "xrCreateInstance result was not XR_SUCCESS");
|
||||
}
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator CallbacksClearedOnLoaderShutdown()
|
||||
{
|
||||
MockRuntime.SetFunctionCallback("xrBeginSession", (func) => XrResult.Success);
|
||||
|
||||
InitializeAndStart();
|
||||
|
||||
yield return new WaitForXrFrame(1);
|
||||
|
||||
StopAndShutdown();
|
||||
|
||||
Assert.IsTrue(OpenXRLoader.Instance == null, "OpenXR should not be running");
|
||||
Assert.IsNull(MockRuntime.GetBeforeFunctionCallback("xrBeginSession"), "Callback should have been cleared when loader shut down");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3b727f98ed496a3428842314282a4638
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
70
Packages/com.unity.xr.openxr/Tests/Runtime/NativeTests.cs
Normal file
70
Packages/com.unity.xr.openxr/Tests/Runtime/NativeTests.cs
Normal file
@@ -0,0 +1,70 @@
|
||||
using NUnit.Framework;
|
||||
using UnityEngine.XR.OpenXR.Input;
|
||||
using System.Text;
|
||||
using UnityEngine.TestTools;
|
||||
|
||||
namespace UnityEngine.XR.OpenXR.Tests
|
||||
{
|
||||
internal class NativeTests : OpenXRLoaderSetup
|
||||
{
|
||||
public override void BeforeTest()
|
||||
{
|
||||
OpenXRLoaderBase.Internal_UnloadOpenXRLibrary();
|
||||
base.BeforeTest();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void OpenXRLoader_LoadOpenXRLibrary_NullLoaderPath()
|
||||
{
|
||||
Assert.IsFalse(OpenXRLoaderBase.Internal_LoadOpenXRLibrary(null));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void OpenXRLoader_LoadOpenXRLibrary_InvalidLoaderPath()
|
||||
{
|
||||
Assert.IsFalse(OpenXRLoaderBase.Internal_LoadOpenXRLibrary(OpenXRLoaderBase.StringToWCHAR_T("abababab")));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void OpenXRLoader_InitializeSession_BeforeLoadingLibrary()
|
||||
{
|
||||
Assert.IsFalse(OpenXRLoaderBase.Internal_InitializeSession());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void OpenXRLoader_CreateSessionIfNeeded_BeforeLoadingLibrary()
|
||||
{
|
||||
Assert.IsFalse(OpenXRLoaderBase.Internal_CreateSessionIfNeeded());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void OpenXRLoader_RequestEnableExtensionString_BeforeLoadingLibrary()
|
||||
{
|
||||
Assert.IsFalse(OpenXRLoaderBase.Internal_RequestEnableExtensionString(null));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void OpenXRLoader_RequestEnableExtensionString_Null()
|
||||
{
|
||||
Assert.IsFalse(OpenXRLoaderBase.Internal_RequestEnableExtensionString("some_extension"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void OpenXRInput_TryGetInputSourceName_BeforeInitializing()
|
||||
{
|
||||
Assert.IsFalse(OpenXRInput.Internal_TryGetInputSourceName(0, 0, 0, 0, out var name));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void OpenXRInput_SuggestBindings_BeforeInitializing()
|
||||
{
|
||||
Assert.IsFalse(OpenXRInput.Internal_SuggestBindings("", null, 0));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void OpenXRInput_AttachActionSets_BeforeInitializing()
|
||||
{
|
||||
Assert.IsFalse(OpenXRInput.Internal_AttachActionSets());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 54113394d100b63429b665f2fa220a13
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
68
Packages/com.unity.xr.openxr/Tests/Runtime/NoRuntimeTests.cs
Normal file
68
Packages/com.unity.xr.openxr/Tests/Runtime/NoRuntimeTests.cs
Normal file
@@ -0,0 +1,68 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using NUnit.Framework;
|
||||
using UnityEngine.XR.Management;
|
||||
using UnityEngine.TestTools;
|
||||
|
||||
namespace UnityEngine.XR.OpenXR.Tests
|
||||
{
|
||||
internal class NoRuntimeTests : OpenXRLoaderSetup
|
||||
{
|
||||
private XRManagerSettings manager => XRGeneralSettings.Instance?.Manager ?? null;
|
||||
|
||||
private XRLoader activeLoader => manager?.activeLoader ?? null;
|
||||
|
||||
public override void BeforeTest()
|
||||
{
|
||||
base.BeforeTest();
|
||||
Environment.SetEnvironmentVariable("XR_RUNTIME_JSON", "asdf.json");
|
||||
EnableMockRuntime(false);
|
||||
Loader.DisableValidationChecksOnEnteringPlaymode = true;
|
||||
}
|
||||
|
||||
public override void AfterTest()
|
||||
{
|
||||
if (Loader != null)
|
||||
Loader.DisableValidationChecksOnEnteringPlaymode = false;
|
||||
Environment.SetEnvironmentVariable("XR_RUNTIME_JSON", "");
|
||||
base.AfterTest();
|
||||
}
|
||||
|
||||
[UnityTest]
|
||||
[Category("Loader Tests")]
|
||||
[UnityPlatform(include = new[] { RuntimePlatform.WindowsEditor })] // we can't run these tests on player because only the mock loader is included - this needs the khronos loader
|
||||
public IEnumerator NoInitNoCrash()
|
||||
{
|
||||
base.InitializeAndStart();
|
||||
|
||||
yield return null;
|
||||
|
||||
Assert.IsNull(activeLoader);
|
||||
}
|
||||
|
||||
[UnityTest]
|
||||
[Category("Loader Tests")]
|
||||
[UnityPlatform(include = new[] { RuntimePlatform.WindowsEditor })]
|
||||
public IEnumerator LoadRuntimeAfterNoRuntime()
|
||||
{
|
||||
base.InitializeAndStart();
|
||||
|
||||
yield return null;
|
||||
|
||||
Assert.IsNull(activeLoader);
|
||||
|
||||
#if !OPENXR_USE_KHRONOS_LOADER
|
||||
Environment.SetEnvironmentVariable("XR_RUNTIME_JSON", "");
|
||||
EnableMockRuntime();
|
||||
|
||||
base.InitializeAndStart();
|
||||
|
||||
yield return null;
|
||||
|
||||
Assert.IsNotNull(activeLoader);
|
||||
|
||||
Assert.AreEqual(OpenXRRuntime.name, "Unity Mock Runtime");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6040e3f8adec12540a375e422f163de9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
136
Packages/com.unity.xr.openxr/Tests/Runtime/OpenXRFeatureTests.cs
Normal file
136
Packages/com.unity.xr.openxr/Tests/Runtime/OpenXRFeatureTests.cs
Normal file
@@ -0,0 +1,136 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using NUnit.Framework;
|
||||
using UnityEngine.TestTools;
|
||||
using UnityEngine.XR.OpenXR.Features;
|
||||
using UnityEngine.XR.OpenXR.Features.Mock;
|
||||
|
||||
namespace UnityEngine.XR.OpenXR.Tests
|
||||
{
|
||||
class OpenXRFeatureTests : OpenXRLoaderSetup
|
||||
{
|
||||
private class FakeFeature : OpenXRFeature
|
||||
{
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void HighPriority()
|
||||
{
|
||||
MockRuntime.Instance.priority = Int32.MaxValue;
|
||||
|
||||
base.InitializeAndStart();
|
||||
|
||||
Assert.IsTrue(OpenXRSettings.Instance.features[0] == MockRuntime.Instance);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void LowPriority()
|
||||
{
|
||||
MockRuntime.Instance.priority = Int32.MinValue;
|
||||
|
||||
base.InitializeAndStart();
|
||||
|
||||
Assert.IsTrue(OpenXRSettings.Instance.features[OpenXRSettings.Instance.features.Length - 1] == MockRuntime.Instance);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChangeEnabledAtRuntime()
|
||||
{
|
||||
base.InitializeAndStart();
|
||||
|
||||
MockRuntime.Instance.enabled = false;
|
||||
LogAssert.Expect(LogType.Error, "OpenXRFeature.enabled cannot be changed while OpenXR is running");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void FeatureFailedInitialization()
|
||||
{
|
||||
bool enableStatus = true;
|
||||
//Force OnInstanceCreate returning false so that failedInitialization is true.
|
||||
MockRuntime.Instance.TestCallback = (methodName, param) =>
|
||||
{
|
||||
if (methodName == nameof(OpenXRFeature.OnInstanceCreate))
|
||||
return false;
|
||||
return true;
|
||||
};
|
||||
base.InitializeAndStart();
|
||||
enableStatus = MockRuntime.Instance.enabled;
|
||||
MockRuntime.Instance.enabled = enableStatus;
|
||||
Assert.IsTrue(MockRuntime.Instance.enabled == enableStatus);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetFeatureCount()
|
||||
{
|
||||
Assert.IsTrue(OpenXRSettings.Instance.featureCount == OpenXRSettings.Instance.GetFeatures(typeof(OpenXRFeature)).Length);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetFeatureByTypeBadType()
|
||||
{
|
||||
Assert.IsNull(OpenXRSettings.Instance.GetFeature(typeof(OpenXRLoader)));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetFeatureByTypeNotFound()
|
||||
{
|
||||
Assert.IsNull(OpenXRSettings.Instance.GetFeature<FakeFeature>());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetFeatureByType()
|
||||
{
|
||||
var feature = OpenXRSettings.Instance.GetFeature<MockRuntime>();
|
||||
Assert.IsNotNull(feature);
|
||||
Assert.IsTrue(feature is MockRuntime);
|
||||
|
||||
Assert.IsNotNull(OpenXRSettings.Instance.GetFeature(typeof(MockRuntime)) as MockRuntime);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetFeaturesByTypeArray()
|
||||
{
|
||||
var features = OpenXRSettings.Instance.GetFeatures<MockRuntime>();
|
||||
Assert.IsNotNull(features);
|
||||
Assert.IsTrue(features.Length == 1);
|
||||
Assert.IsTrue(features[0] is MockRuntime);
|
||||
|
||||
features = OpenXRSettings.Instance.GetFeatures(typeof(MockRuntime));
|
||||
Assert.IsNotNull(features);
|
||||
Assert.IsTrue(features.Length == 1);
|
||||
Assert.IsTrue(features[0] is MockRuntime);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetFeaturesByGenericList()
|
||||
{
|
||||
var features = new List<MockRuntime>();
|
||||
Assert.IsTrue(OpenXRSettings.Instance.GetFeatures(features) == 1);
|
||||
Assert.IsNotNull(features[0]);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetFeaturesByTypeList()
|
||||
{
|
||||
var features = new List<OpenXRFeature>();
|
||||
Assert.IsTrue(OpenXRSettings.Instance.GetFeatures(typeof(MockRuntime), features) == 1);
|
||||
Assert.IsNotNull(features[0]);
|
||||
Assert.IsTrue(features[0] is MockRuntime);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetFeaturesArray()
|
||||
{
|
||||
var features = OpenXRSettings.Instance.GetFeatures();
|
||||
Assert.IsNotNull(features);
|
||||
Assert.IsTrue(features.Length == OpenXRSettings.Instance.featureCount);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetFeaturesList()
|
||||
{
|
||||
var features = new List<OpenXRFeature>();
|
||||
Assert.IsTrue(OpenXRSettings.Instance.GetFeatures(features) == OpenXRSettings.Instance.featureCount);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9050a5edea0d4cf19f270d6c4ebac796
|
||||
timeCreated: 1604700422
|
||||
737
Packages/com.unity.xr.openxr/Tests/Runtime/OpenXRInputTests.cs
Normal file
737
Packages/com.unity.xr.openxr/Tests/Runtime/OpenXRInputTests.cs
Normal file
@@ -0,0 +1,737 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using NUnit.Framework;
|
||||
using UnityEngine.InputSystem;
|
||||
using UnityEngine.InputSystem.Layouts;
|
||||
using UnityEngine.InputSystem.XR;
|
||||
using UnityEngine.TestTools;
|
||||
using UnityEngine.XR.OpenXR.Features;
|
||||
using UnityEngine.XR.OpenXR.Features.Interactions;
|
||||
using UnityEngine.XR.OpenXR.Features.ConformanceAutomation;
|
||||
using UnityEngine.XR.OpenXR.Features.Mock;
|
||||
using UnityEngine.XR.OpenXR.Input;
|
||||
using UnityEngine.XR.OpenXR.NativeTypes;
|
||||
#if USE_INPUT_SYSTEM_POSE_CONTROL
|
||||
using PoseStruct = UnityEngine.InputSystem.XR.PoseState;
|
||||
#else
|
||||
using PoseStruct = UnityEngine.XR.OpenXR.Input.Pose;
|
||||
#endif
|
||||
|
||||
namespace UnityEngine.XR.OpenXR.Tests
|
||||
{
|
||||
internal class OpenXRInputTestsBase : OpenXRLoaderSetup
|
||||
{
|
||||
private static readonly List<XRNodeState> s_NodeStates = new List<XRNodeState>();
|
||||
|
||||
protected static bool IsNodeTracked(XRNode node)
|
||||
{
|
||||
s_NodeStates.Clear();
|
||||
InputTracking.GetNodeStates(s_NodeStates);
|
||||
return s_NodeStates.Where(s => s.nodeType == node).Select(s => s.tracked).FirstOrDefault();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// List of all known interaction features and their associated devices for testing
|
||||
/// </summary>
|
||||
protected static readonly (Type featureType, Type layoutType, string layoutNameOverride)[] s_InteractionFeatureLayouts = {
|
||||
(typeof(OculusTouchControllerProfile), typeof(OculusTouchControllerProfile.OculusTouchController), null),
|
||||
(typeof(EyeGazeInteraction), typeof(EyeGazeInteraction.EyeGazeDevice), "EyeGaze"),
|
||||
(typeof(MicrosoftHandInteraction), typeof(MicrosoftHandInteraction.HoloLensHand), null),
|
||||
(typeof(KHRSimpleControllerProfile), typeof(KHRSimpleControllerProfile.KHRSimpleController), null),
|
||||
(typeof(HandInteractionProfile), typeof(HandInteractionProfile.HandInteraction), null),
|
||||
(typeof(MetaQuestTouchProControllerProfile), typeof(MetaQuestTouchProControllerProfile.QuestProTouchController), null),
|
||||
(typeof(MetaQuestTouchPlusControllerProfile), typeof(MetaQuestTouchPlusControllerProfile.QuestTouchPlusController), null),
|
||||
#if !UNITY_ANDROID
|
||||
(typeof(HTCViveControllerProfile), typeof(HTCViveControllerProfile.ViveController), null),
|
||||
(typeof(HPReverbG2ControllerProfile), typeof(HPReverbG2ControllerProfile.ReverbG2Controller), null),
|
||||
(typeof(MicrosoftMotionControllerProfile), typeof(MicrosoftMotionControllerProfile.WMRSpatialController), null),
|
||||
(typeof(ValveIndexControllerProfile), typeof(ValveIndexControllerProfile.ValveIndexController), null)
|
||||
#endif
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// List of interaction features that should not be tested.
|
||||
/// </summary>
|
||||
protected static readonly Type[] s_IgnoreInteractionFeatures = {
|
||||
typeof(MockInteractionFeature),
|
||||
typeof(HandCommonPosesInteraction),
|
||||
typeof(DPadInteraction),
|
||||
typeof(PalmPoseInteraction)
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Return true if the given layout is registered.
|
||||
/// </summary>
|
||||
/// <param name="layoutName">Name of the layout</param>
|
||||
/// <returns>True if the layout is registered with the input system</returns>
|
||||
protected static bool IsLayoutRegistered(string layoutName)
|
||||
{
|
||||
// Force an input system update first to make sure all registrations are committed.
|
||||
InputSystem.InputSystem.Update();
|
||||
|
||||
try
|
||||
{
|
||||
return InputSystem.InputSystem.LoadLayout(layoutName) != null;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
internal class OpenXRInputTests : OpenXRInputTestsBase
|
||||
{
|
||||
protected override void QueryBuildFeatures(List<Type> featureTypes)
|
||||
{
|
||||
base.QueryBuildFeatures(featureTypes);
|
||||
featureTypes.AddRange(s_InteractionFeatureLayouts.Select(i => i.featureType));
|
||||
featureTypes.Add(typeof(MockInteractionFeature));
|
||||
featureTypes.Add(typeof(ConformanceAutomationFeature));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether or not the device layout for an interaction feature is registered at runtime
|
||||
/// </summary>
|
||||
[UnityTest]
|
||||
public IEnumerator DeviceLayoutRegistration([ValueSource(nameof(s_InteractionFeatureLayouts))] (Type featureType, Type layoutType, string layoutNameOverride) interactionFeature)
|
||||
{
|
||||
var layoutName = interactionFeature.layoutNameOverride ?? interactionFeature.layoutType.Name;
|
||||
|
||||
// Make sure the layout is not registered as it would give the test a false positive
|
||||
InputSystem.InputSystem.RemoveLayout(layoutName);
|
||||
Assert.IsFalse(IsLayoutRegistered(layoutName), "Layout is still registered, test will give a false positive");
|
||||
|
||||
// Starting OpenXR should register all layouts from interaction features. Make sure that the
|
||||
// layout is registered after starting.
|
||||
EnableFeature(interactionFeature.featureType);
|
||||
InitializeAndStart();
|
||||
yield return new WaitForXrFrame(2);
|
||||
Assert.IsTrue(IsLayoutRegistered(layoutName), "Layout was not registered during Initialization");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validate that data flows through the given OpenXR interaction path to the give action.
|
||||
/// </summary>
|
||||
/// <param name="inputAction">Input action that should receive the data</param>
|
||||
/// <param name="userPath">OpenXR User Path</param>
|
||||
/// <param name="interactionPath">OpenXR interaction path</param>
|
||||
/// <param name="value">Value to verify</param>
|
||||
/// <returns></returns>
|
||||
private static IEnumerator ValidateInputAction(InputAction inputAction, string userPath, string interactionPath, bool value)
|
||||
{
|
||||
ConformanceAutomationFeature.ConformanceAutomationSetBool(userPath, interactionPath, value);
|
||||
yield return new WaitForXrFrame(2);
|
||||
var actualValue = inputAction.ReadValue<float>() > 0.0f;
|
||||
Assert.IsTrue(actualValue == value, $"Expected '{value}' but received '{actualValue}' from '{inputAction}' bound to '{interactionPath}'");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validate that data flows through the given OpenXR interaction path to the give action.
|
||||
/// </summary>
|
||||
/// <param name="inputAction">Input action that should receive the data</param>
|
||||
/// <param name="userPath">OpenXR User Path</param>
|
||||
/// <param name="interactionPath">OpenXR interaction path</param>
|
||||
/// <param name="value">Value to verify</param>
|
||||
/// <returns></returns>
|
||||
private static IEnumerator ValidateInputAction(InputAction inputAction, string userPath, string interactionPath, float value)
|
||||
{
|
||||
ConformanceAutomationFeature.ConformanceAutomationSetFloat(userPath, interactionPath, value);
|
||||
yield return new WaitForXrFrame(2);
|
||||
var actualValue = inputAction.ReadValue<float>();
|
||||
Assert.IsTrue(actualValue >= value - float.Epsilon && actualValue <= value + float.Epsilon, $"Expected '{value}' but received '{actualValue}' from '{inputAction}' bound to '{interactionPath}'");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validate that data flows through the given OpenXR interaction path to the give action.
|
||||
/// </summary>
|
||||
/// <param name="inputAction">Input action that should receive the data</param>
|
||||
/// <param name="userPath">OpenXR User Path</param>
|
||||
/// <param name="interactionPath">OpenXR interaction path</param>
|
||||
/// <param name="value">Value to verify</param>
|
||||
/// <returns></returns>
|
||||
private static IEnumerator ValidateInputAction(InputAction inputAction, string userPath, string interactionPath, Vector2 value)
|
||||
{
|
||||
ConformanceAutomationFeature.ConformanceAutomationSetVec2(userPath, interactionPath, value);
|
||||
yield return new WaitForXrFrame(2);
|
||||
var actualValue = inputAction.ReadValue<Vector2>();
|
||||
Assert.IsTrue(
|
||||
actualValue.x >= value.x - float.Epsilon && actualValue.x <= value.x + float.Epsilon &&
|
||||
actualValue.y >= value.y - float.Epsilon && actualValue.y <= value.y + float.Epsilon,
|
||||
$"Expected '{value}' but received '{actualValue}' from '{inputAction}' bound to '{interactionPath}'"
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validate that data flows through the given OpenXR interaction path to the give action.
|
||||
/// </summary>
|
||||
/// <param name="inputAction">Input action that should receive the data</param>
|
||||
/// <param name="userPath">OpenXR User Path</param>
|
||||
/// <param name="interactionPath">OpenXR interaction path</param>
|
||||
/// <param name="expected">Value to verify</param>
|
||||
/// <returns></returns>
|
||||
private static IEnumerator ValidateInputAction(InputAction inputAction, string userPath, string interactionPath, PoseStruct expected)
|
||||
{
|
||||
ConformanceAutomationFeature.ConformanceAutomationSetPose(userPath, interactionPath, expected.position, expected.rotation);
|
||||
ConformanceAutomationFeature.ConformanceAutomationSetVelocity(
|
||||
userPath,
|
||||
interactionPath,
|
||||
((expected.trackingState & InputTrackingState.Velocity) == InputTrackingState.Velocity),
|
||||
expected.velocity,
|
||||
((expected.trackingState & InputTrackingState.AngularVelocity) == InputTrackingState.AngularVelocity),
|
||||
expected.angularVelocity);
|
||||
ConformanceAutomationFeature.ConformanceAutomationSetActive(null, userPath, expected.isTracked);
|
||||
yield return new WaitForXrFrame(2);
|
||||
|
||||
switch (inputAction.expectedControlType)
|
||||
{
|
||||
case "Vector3":
|
||||
{
|
||||
var received = inputAction.ReadValue<Vector3>();
|
||||
Assert.IsTrue(received == expected.position, $"Action '{inputAction.bindings[0].path}' bound to '{interactionPath}' expected '{expected.position} but received '{received}'");
|
||||
break;
|
||||
}
|
||||
|
||||
case "Quaternion":
|
||||
{
|
||||
var received = inputAction.ReadValue<Quaternion>();
|
||||
Assert.IsTrue(received == expected.rotation, $"Action '{inputAction.bindings[0].path}' bound to '{interactionPath}' expected '{expected.rotation}' but received '{received}'");
|
||||
break;
|
||||
}
|
||||
|
||||
case "Button":
|
||||
{
|
||||
var received = inputAction.ReadValue<float>() > 0.0f;
|
||||
Assert.IsTrue(received == expected.isTracked, $"Action '{inputAction.bindings[0].path}' bound to '{interactionPath}' expected '{expected.isTracked}' but received '{received}'");
|
||||
break;
|
||||
}
|
||||
|
||||
case "Integer":
|
||||
{
|
||||
var received = inputAction.ReadValue<int>();
|
||||
Assert.IsTrue(received == (int)expected.trackingState, $"Action '{inputAction.bindings[0].path}' bound to '{interactionPath}' expected '{expected.trackingState}' but received '{(InputTrackingState)received}'");
|
||||
break;
|
||||
}
|
||||
|
||||
case "Pose":
|
||||
{
|
||||
var received = inputAction.ReadValue<PoseStruct>();
|
||||
Assert.IsTrue(received.isTracked == expected.isTracked, $"Action '{inputAction.bindings[0].path}/isTracked' bound to '{interactionPath}' expected '{expected.isTracked}' but received '{received.isTracked}'");
|
||||
Assert.IsTrue(received.trackingState == expected.trackingState, $"Action '{inputAction.bindings[0].path}/trackingState' bound to '{interactionPath}' expected '{expected.trackingState}' but received '{received.trackingState}'");
|
||||
|
||||
if (received.isTracked)
|
||||
{
|
||||
Assert.IsTrue(received.position == expected.position, $"Action '{inputAction.bindings[0].path}/position' bound to '{interactionPath}' expected '{expected.position}' but received '{received.position}'");
|
||||
Assert.IsTrue(received.rotation == expected.rotation, $"Action '{inputAction.bindings[0].path}/rotation' bound to '{interactionPath}' expected '{expected.rotation}' but received '{received.rotation}'");
|
||||
|
||||
if ((received.trackingState & InputTrackingState.Velocity) == InputTrackingState.Velocity)
|
||||
Assert.IsTrue(received.velocity == expected.velocity, $"Action '{inputAction.bindings[0].path}/position' bound to '{interactionPath}' expected '{expected.velocity}' but received '{received.velocity}'");
|
||||
|
||||
if ((received.trackingState & InputTrackingState.AngularVelocity) == InputTrackingState.AngularVelocity)
|
||||
Assert.IsTrue(received.angularVelocity == expected.angularVelocity, $"Action '{inputAction.bindings[0].path}/position' bound to '{interactionPath}' expected '{expected.angularVelocity}' but received '{received.angularVelocity}'");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
yield return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validate that the haptic associated with an input action fires
|
||||
/// </summary>
|
||||
/// <param name="inputAction">Input action</param>
|
||||
/// <param name="amplitude">Amplitude for haptic</param>
|
||||
/// <param name="duration">Duration for haptic</param>
|
||||
/// <param name="inputDevice">Device to filter with</param>
|
||||
private static IEnumerator ValidateHaptic(InputAction inputAction, float amplitude, float duration, InputSystem.InputDevice inputDevice = null)
|
||||
{
|
||||
var hapticImpulseCount = 0;
|
||||
var hapticStopCount = 0;
|
||||
void OnHapticOutput(MockRuntime.ScriptEvent evt, ulong param)
|
||||
{
|
||||
hapticImpulseCount += evt == MockRuntime.ScriptEvent.HapticImpulse ? 1 : 0;
|
||||
hapticStopCount += evt == MockRuntime.ScriptEvent.HapticStop ? 1 : 0;
|
||||
}
|
||||
|
||||
MockRuntime.onScriptEvent += OnHapticOutput;
|
||||
|
||||
if (null == inputAction)
|
||||
{
|
||||
Assert.IsNotNull(inputDevice);
|
||||
|
||||
if (inputDevice is XRControllerWithRumble rumble)
|
||||
rumble.SendImpulse(amplitude, duration);
|
||||
}
|
||||
else
|
||||
OpenXRInput.SendHapticImpulse(inputAction, amplitude, duration, inputDevice);
|
||||
|
||||
// Give some time for the haptic event to make its way to our callback
|
||||
yield return new WaitForXrFrame(2);
|
||||
|
||||
if (null != inputAction)
|
||||
OpenXRInput.StopHaptics(inputAction, inputDevice);
|
||||
|
||||
yield return new WaitForXrFrame(2);
|
||||
|
||||
MockRuntime.onScriptEvent -= OnHapticOutput;
|
||||
|
||||
Assert.IsTrue(hapticImpulseCount == 1, null == inputAction ?
|
||||
$"Haptic impulse failed for XRControllerWithRumble '{inputDevice.name}" :
|
||||
$"Haptic impulse failed for action '{inputAction}'");
|
||||
|
||||
Assert.IsTrue(inputAction == null || hapticStopCount == 1, $"Haptic stop failed for action '{inputAction}'");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validate that data flows from OpenXR to the InputSystem through the given OpenXR interaction path to
|
||||
/// the given input ControlItem
|
||||
/// </summary>
|
||||
/// <param name="localizedActionMapName">Device layout name to validate</param>
|
||||
/// <param name="control">Control within the device layout to validate</param>
|
||||
/// <param name="userPath">OpenXR User path to bind to</param>
|
||||
/// <param name="interactionPath">OpenXR interaction path to bind to</param>
|
||||
/// <param name="controlLayoutOverride">Optional override for the control layout</param>
|
||||
/// <param name="usageOverride">Optional usage override for the binding</param>
|
||||
/// <returns></returns>
|
||||
private static IEnumerator ValidateLayoutControl(InputControlLayout layout, InputControlLayout.ControlItem control, string userPath, string interactionPath, string controlLayoutOverride = null, string usageOverride = null)
|
||||
{
|
||||
// Convert the user path to a usage to limit the bound action
|
||||
var usage = userPath switch
|
||||
{
|
||||
"/user/hand/left" => "{LeftHand}",
|
||||
"/user/hand/right" => "{RightHand}",
|
||||
_ => ""
|
||||
};
|
||||
|
||||
// Create an action bound to the control
|
||||
var action = new InputAction(
|
||||
null,
|
||||
InputActionType.Value,
|
||||
$"<{layout.name}>{usage}/{(usageOverride != null ? $"{{{usageOverride}}}" : control.name)}",
|
||||
null,
|
||||
null,
|
||||
control.layout);
|
||||
|
||||
action.Enable();
|
||||
|
||||
// Make sure the input system updates and wait a frame to ensure the action is properly bound before testing with it
|
||||
InputSystem.InputSystem.Update();
|
||||
yield return new WaitForXrFrame(1);
|
||||
|
||||
// Use the usage to find the device for the action
|
||||
var inputDevice = !string.IsNullOrEmpty(usage) ?
|
||||
InputSystem.InputSystem.GetDevice<InputSystem.InputDevice>(usage.Substring(1, usage.Length - 2)) :
|
||||
null;
|
||||
|
||||
// Check input TryGetInputSourceName
|
||||
Assert.IsTrue(
|
||||
OpenXRInput.TryGetInputSourceName(action, 0, out var actionName, OpenXRInput.InputSourceNameFlags.All, inputDevice),
|
||||
$"Failed to retrieve input source for action '{action}'.");
|
||||
Assert.IsNotEmpty(actionName, $"Input source name for action '{action}' should not be empty");
|
||||
|
||||
switch (controlLayoutOverride ?? control.layout)
|
||||
{
|
||||
case "Button":
|
||||
{
|
||||
yield return ValidateInputAction(action, userPath, interactionPath, true);
|
||||
yield return ValidateInputAction(action, userPath, interactionPath, false);
|
||||
break;
|
||||
}
|
||||
|
||||
case "Axis":
|
||||
{
|
||||
yield return ValidateInputAction(action, userPath, interactionPath, 1.0f);
|
||||
yield return ValidateInputAction(action, userPath, interactionPath, 0.0f);
|
||||
// TODO: Disabled this because the Microsoft Motion Controller and the HTC Vive controller specify Axis1D controls that are not actually 1DAxis controls
|
||||
//yield return ValidateInputAction(action, userPath, interactionPath, 0.5f);
|
||||
break;
|
||||
}
|
||||
|
||||
case "Stick":
|
||||
case "Vector2":
|
||||
{
|
||||
yield return ValidateInputAction(action, userPath, interactionPath, Vector2.one);
|
||||
yield return ValidateInputAction(action, userPath, interactionPath, Vector2.zero);
|
||||
yield return ValidateInputAction(action, userPath, interactionPath, new Vector2(1.0f, 0.0f));
|
||||
yield return ValidateInputAction(action, userPath, interactionPath, new Vector2(0.0f, 1.0f));
|
||||
break;
|
||||
}
|
||||
|
||||
case "Pose":
|
||||
{
|
||||
yield return ValidateInputAction(action, userPath, interactionPath, new PoseStruct
|
||||
{
|
||||
position = Vector3.one,
|
||||
rotation = Quaternion.identity,
|
||||
isTracked = true,
|
||||
trackingState = InputTrackingState.Position | InputTrackingState.Rotation
|
||||
});
|
||||
|
||||
yield return ValidateInputAction(action, userPath, interactionPath, new PoseStruct
|
||||
{
|
||||
position = Vector3.zero,
|
||||
rotation = Quaternion.identity,
|
||||
isTracked = false,
|
||||
trackingState = InputTrackingState.None
|
||||
});
|
||||
|
||||
yield return ValidateInputAction(action, userPath, interactionPath, new PoseStruct
|
||||
{
|
||||
position = Vector3.zero,
|
||||
rotation = Quaternion.Euler(90, 0, 0),
|
||||
isTracked = true,
|
||||
trackingState = InputTrackingState.Position | InputTrackingState.Rotation
|
||||
});
|
||||
|
||||
// Velocity only
|
||||
yield return ValidateInputAction(action, userPath, interactionPath, new PoseStruct
|
||||
{
|
||||
position = Vector3.zero,
|
||||
rotation = Quaternion.identity,
|
||||
isTracked = true,
|
||||
trackingState = InputTrackingState.Position | InputTrackingState.Rotation | InputTrackingState.Velocity,
|
||||
velocity = new Vector3(1, 2, 3)
|
||||
});
|
||||
|
||||
// AngularVelocity only
|
||||
yield return ValidateInputAction(action, userPath, interactionPath, new PoseStruct
|
||||
{
|
||||
position = Vector3.zero,
|
||||
rotation = Quaternion.Euler(90, 0, 0),
|
||||
isTracked = true,
|
||||
trackingState = InputTrackingState.Position | InputTrackingState.Rotation | InputTrackingState.AngularVelocity,
|
||||
angularVelocity = new Vector3(1, 2, 3)
|
||||
});
|
||||
|
||||
// Velocity and AngularVelocity
|
||||
yield return ValidateInputAction(action, userPath, interactionPath, new PoseStruct
|
||||
{
|
||||
position = Vector3.zero,
|
||||
rotation = Quaternion.Euler(90, 0, 0),
|
||||
isTracked = true,
|
||||
trackingState = InputTrackingState.Position | InputTrackingState.Rotation | InputTrackingState.Velocity | InputTrackingState.AngularVelocity,
|
||||
velocity = new Vector3(1, 2, 3),
|
||||
angularVelocity = new Vector3(3, 2, 1)
|
||||
});
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case "Haptic":
|
||||
{
|
||||
// Validate haptics through the action
|
||||
yield return ValidateHaptic(action, 1.0f, 1.0f, inputDevice);
|
||||
|
||||
// Validate haptics through a rumble controller
|
||||
if (inputDevice is XRControllerWithRumble)
|
||||
yield return ValidateHaptic(null, 1.0f, 1.0f, inputDevice);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
Assert.Fail($"Unknown control type `{control.layout}`");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests all controls of all interaction features to ensure data flows through properly.
|
||||
/// </summary>
|
||||
[UnityTest]
|
||||
[UnityPlatform(exclude = new[] { RuntimePlatform.OSXEditor, RuntimePlatform.OSXPlayer })] // These tests time out on 2022+ on the Mac Editor CI machines
|
||||
public IEnumerator ValidateControls([ValueSource(nameof(s_InteractionFeatureLayouts))] (Type featureType, Type layoutType, string layoutNameOverride) interactionFeature)
|
||||
{
|
||||
// Enable the needed features
|
||||
EnableMockRuntime();
|
||||
EnableFeature<ConformanceAutomationFeature>();
|
||||
var feature = EnableFeature(interactionFeature.featureType) as OpenXRInteractionFeature;
|
||||
|
||||
// Make sure all the devices are registered with the input system
|
||||
InputSystem.InputSystem.Update();
|
||||
|
||||
var actionMaps = new List<OpenXRInteractionFeature.ActionMapConfig>();
|
||||
feature.CreateActionMaps(actionMaps);
|
||||
|
||||
base.InitializeAndStart();
|
||||
yield return new WaitForXrFrame(2);
|
||||
|
||||
var layoutName = interactionFeature.layoutNameOverride ?? interactionFeature.layoutType.Name;
|
||||
var layout = InputSystem.InputSystem.LoadLayout(layoutName);
|
||||
Assert.IsNotNull(layout, $"Missing layout '{layoutName}'");
|
||||
|
||||
// Get list of all known user paths supported by this action map
|
||||
var userPaths = actionMaps.SelectMany(m => m.deviceInfos.Select(d => d.userPath)).Distinct().ToList();
|
||||
|
||||
var actionMapCoverage = new HashSet<OpenXRInteractionFeature.ActionConfig>();
|
||||
|
||||
foreach (var control in layout.controls)
|
||||
{
|
||||
// Find the ActionConfig that matches the given control name
|
||||
var actionConfigs = actionMaps.SelectMany(m => m.actions).Where(a => a.name == control.name).ToArray();
|
||||
|
||||
// Control should not be specified in more than one action map config. If there is a future reason for this then this
|
||||
// test will need to be extended to accomodate that.
|
||||
Assert.IsTrue(actionConfigs.Length < 2, $"Control '{control.name}' with type '{control.layout}' is specified in more than one ActionConfig");
|
||||
|
||||
var actionConfig = actionConfigs.Length == 1 ? actionConfigs[0] : null;
|
||||
|
||||
// Controls with offsets that are not-zero should not be in the action config as they are "virtual" controls.
|
||||
if (control.offset != uint.MaxValue && control.offset != 0)
|
||||
{
|
||||
// Any controls with offsets should not be in the ActionConfig
|
||||
Assert.IsNull(actionConfig, $"Control '{control.name}' with type '{control.layout}' has offset and should not be included in the ActionMapConfig");
|
||||
|
||||
foreach (var userPath in userPaths)
|
||||
{
|
||||
switch (control.name)
|
||||
{
|
||||
case "isTracked":
|
||||
case "trackingState":
|
||||
case "devicePosition":
|
||||
case "deviceRotation":
|
||||
yield return ValidateLayoutControl(layout, control, userPath, $"{userPath}/input/grip/pose", "Pose");
|
||||
break;
|
||||
|
||||
case "pointerPosition":
|
||||
case "pointerRotation":
|
||||
yield return ValidateLayoutControl(layout, control, userPath, $"{userPath}/input/aim/pose", "Pose");
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// Control must be in the action map config if it does not have a non-zero offset
|
||||
Assert.IsNotNull(actionConfig, $"Control '{control.name}' with type '{control.layout}' is missing from the ActionMapConfig");
|
||||
|
||||
Assert.IsTrue(
|
||||
actionConfig.usages.Count == control.usages.Count &&
|
||||
actionConfig.usages.Intersect(control.usages.Select(u => u.ToString())).Count() == actionConfig.usages.Count,
|
||||
$"ActionConfig usage list for control `{control.name}` does not match ControlItem usage list");
|
||||
|
||||
actionMapCoverage.Add(actionConfig);
|
||||
|
||||
foreach (var binding in actionConfig.bindings)
|
||||
foreach (var userPath in (binding.userPaths ?? userPaths))
|
||||
{
|
||||
yield return ValidateLayoutControl(layout, control, userPath, $"{userPath}{binding.interactionPath}");
|
||||
|
||||
// Ensure the usages all map correctly to the data as well
|
||||
foreach (var usage in actionConfig.usages)
|
||||
{
|
||||
yield return ValidateLayoutControl(layout, control, userPath, $"{userPath}{binding.interactionPath}", null, usage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure that there are no action maps that reference controls that were not paired up
|
||||
foreach (var actionConfig in actionMaps.SelectMany(m => m.actions))
|
||||
{
|
||||
Assert.IsTrue(actionMapCoverage.Contains(actionConfig), $"Action config '{actionConfig.name}' does not have a matching control in the parent layout");
|
||||
}
|
||||
}
|
||||
|
||||
private static readonly Regex k_ErrorNoDevices = new Regex("ActionMapConfig contains no `deviceInfos`.*");
|
||||
private static readonly Regex k_ErrorInvalidDeviceName = new Regex(@".*Invalid device name.*");
|
||||
private static readonly Regex k_ErrorInvalidInteractionProfile = new Regex(@".*Invalid interaction profile.*");
|
||||
private static readonly Regex k_ErrorInvalidUserPath = new Regex(@".*Invalid user path.*");
|
||||
private static readonly Regex k_ErrorInvalidUsage = new Regex(@".*Invalid Usage.*");
|
||||
private static readonly Regex k_ErrorInvalidActionSetName = new Regex(@".*Invalid ActionSet name.*");
|
||||
private static readonly Regex k_ErrorInvalidActionType = new Regex(@".*Invalid action type \'\d*' for action '.*'");
|
||||
|
||||
private static readonly (Action<OpenXRInteractionFeature.ActionMapConfig> filter, Regex expectLog, Regex expectReport)[] s_ActionMapTests =
|
||||
{
|
||||
// One or more device infos must be specified
|
||||
((c) => c.deviceInfos = null, k_ErrorNoDevices, null),
|
||||
((c) => c.deviceInfos = new List<OpenXRInteractionFeature.DeviceConfig>(), k_ErrorNoDevices, null),
|
||||
|
||||
// Desired interaction profile must be specified and be a valid path
|
||||
((c) => c.desiredInteractionProfile = "", k_ErrorInvalidInteractionProfile, k_ErrorInvalidInteractionProfile),
|
||||
((c) => c.desiredInteractionProfile = "bad", k_ErrorInvalidInteractionProfile, k_ErrorInvalidInteractionProfile),
|
||||
((c) => c.desiredInteractionProfile = new String('a', 500), k_ErrorInvalidInteractionProfile, k_ErrorInvalidInteractionProfile),
|
||||
|
||||
// Device user path must be specified and be a valid path
|
||||
((c) => c.deviceInfos[0].userPath = null, k_ErrorInvalidUserPath, k_ErrorInvalidUserPath),
|
||||
((c) => c.deviceInfos[0].userPath = "", k_ErrorInvalidUserPath, k_ErrorInvalidUserPath),
|
||||
((c) => c.deviceInfos[0].userPath = "bad", k_ErrorInvalidUserPath, k_ErrorInvalidUserPath),
|
||||
((c) => c.deviceInfos[0].userPath = "/user/" + new String('a', 500), k_ErrorInvalidUserPath, k_ErrorInvalidUserPath),
|
||||
|
||||
// Name must be valid
|
||||
((c) => c.name = null, k_ErrorInvalidActionSetName, k_ErrorInvalidActionSetName),
|
||||
((c) => c.name = "", k_ErrorInvalidActionSetName, k_ErrorInvalidActionSetName),
|
||||
((c) => c.name = new String('a', 500), k_ErrorInvalidActionSetName, k_ErrorInvalidActionSetName),
|
||||
|
||||
// Localized name
|
||||
((c) => c.localizedName = null, k_ErrorInvalidDeviceName, k_ErrorInvalidDeviceName),
|
||||
((c) => c.localizedName = "", k_ErrorInvalidDeviceName, k_ErrorInvalidDeviceName),
|
||||
((c) => c.localizedName = new String('a', 500), k_ErrorInvalidDeviceName, k_ErrorInvalidDeviceName),
|
||||
|
||||
// Manufacturer or serial number should be allowed to be null or empty
|
||||
((c) => c.manufacturer = "", null, null),
|
||||
((c) => c.manufacturer = null, null, null),
|
||||
((c) => c.serialNumber = "", null, null),
|
||||
((c) => c.serialNumber = null, null, null),
|
||||
|
||||
// Invalid action type
|
||||
((c) => c.actions[0].type = (OpenXRInteractionFeature.ActionType)100, k_ErrorInvalidActionType, k_ErrorInvalidActionType),
|
||||
|
||||
// Action Usages
|
||||
((c) => c.actions[0].usages = new List<string> {""}, k_ErrorInvalidUsage, k_ErrorInvalidUsage),
|
||||
((c) => c.actions[0].usages = new List<string> {null}, k_ErrorInvalidUsage, k_ErrorInvalidUsage),
|
||||
((c) => c.actions[0].usages = new List<string> {new string('a', 500)}, k_ErrorInvalidUsage, k_ErrorInvalidUsage),
|
||||
|
||||
// Invalid user path on binding
|
||||
((c) => c.actions[0].bindings[0].userPaths = new List<string> {"bad", "bad"}, k_ErrorInvalidUserPath, k_ErrorInvalidUserPath),
|
||||
((c) => c.actions[0].bindings[0].userPaths = new List<string> {null, null}, k_ErrorInvalidUserPath, k_ErrorInvalidUserPath),
|
||||
((c) => c.actions[0].bindings[0].userPaths = new List<string> {"/" + new string('a', 500)}, k_ErrorInvalidUserPath, k_ErrorInvalidUserPath),
|
||||
|
||||
// Invalid interaction profile on bindings
|
||||
((c) => c.actions[0].bindings[0].interactionProfileName = null, null, null),
|
||||
((c) => c.actions[0].bindings[0].interactionProfileName = "", k_ErrorInvalidInteractionProfile, k_ErrorInvalidInteractionProfile),
|
||||
((c) => c.actions[0].bindings[0].interactionProfileName = "/" + new string('a', 500), k_ErrorInvalidInteractionProfile, k_ErrorInvalidInteractionProfile),
|
||||
((c) => c.actions[0].bindings[0].interactionProfileName = "bad", k_ErrorInvalidInteractionProfile, k_ErrorInvalidInteractionProfile),
|
||||
};
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator ValidateActionMapConfig([ValueSource(nameof(s_ActionMapTests))] (Action<OpenXRInteractionFeature.ActionMapConfig> filter, Regex expectLog, Regex expectReport) test)
|
||||
{
|
||||
var feature = EnableFeature<MockInteractionFeature>();
|
||||
|
||||
// Set an action map config for the feature that has a bad interaction profile
|
||||
var actionMapConfig = feature.CreateDefaultActionMapConfig();
|
||||
test.filter(actionMapConfig);
|
||||
feature.actionMapConfig = actionMapConfig;
|
||||
|
||||
InitializeAndStart();
|
||||
|
||||
yield return new WaitForXrFrame(2);
|
||||
|
||||
if (test.expectLog != null)
|
||||
LogAssert.Expect(LogType.Error, test.expectLog);
|
||||
|
||||
if (test.expectReport != null)
|
||||
Assert.IsTrue(DoesDiagnosticReportContain(test.expectReport), "Missing report entry");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Defines a list of OpenXR API methods to test failure with
|
||||
/// </summary>
|
||||
private static readonly (string function, XrResult result, Regex expectLog)[] s_RuntimeFailureTests =
|
||||
{
|
||||
("xrSuggestInteractionProfileBindings", XrResult.FeatureUnsupported, new Regex(@".*Failed to suggest bindings for interaction profile.*XR_ERROR_FEATURE_UNSUPPORTED.*")),
|
||||
("xrCreateActionSet", XrResult.FeatureUnsupported, new Regex(@".*Failed to create ActionSet.*XR_ERROR_FEATURE_UNSUPPORTED.*")),
|
||||
("xrCreateAction", XrResult.FeatureUnsupported, new Regex(@".*Failed to create Action.*XR_ERROR_FEATURE_UNSUPPORTED.*")),
|
||||
("xrAttachSessionActionSets", XrResult.FeatureUnsupported, new Regex(@".*Failed to attach ActionSets.*XR_ERROR_FEATURE_UNSUPPORTED.*")),
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Test a failure in suggested bindings.
|
||||
/// </summary>
|
||||
[UnityTest]
|
||||
public IEnumerator RuntimeMethodFailure([ValueSource(nameof(s_RuntimeFailureTests))] (string function, XrResult result, Regex expectLog) test)
|
||||
{
|
||||
MockRuntime.SetFunctionCallback(test.function, (name) => test.result);
|
||||
|
||||
EnableFeature<OculusTouchControllerProfile>();
|
||||
InitializeAndStart();
|
||||
yield return new WaitForXrFrame(1);
|
||||
StopAndShutdown();
|
||||
LogAssert.Expect(LogType.Error, test.expectLog);
|
||||
Assert.IsTrue(DoesDiagnosticReportContain(test.expectLog));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures that the `interactionFeatureLayouts` list is not missing any entries
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void AllInteractionFeaturesCovered()
|
||||
{
|
||||
// Array of all known interaction features
|
||||
var knownInteractionFeatures = OpenXRSettings.Instance.GetFeatures<OpenXRInteractionFeature>()
|
||||
.Select(f => f.GetType())
|
||||
.Where(f => !s_IgnoreInteractionFeatures.Contains(f))
|
||||
.ToArray();
|
||||
|
||||
// Array of interaction features being tested
|
||||
var testedFeatures = s_InteractionFeatureLayouts.Select(l => l.featureType).ToArray();
|
||||
|
||||
// Make sure the two arrays are equal
|
||||
Assert.IsTrue(knownInteractionFeatures.Length == testedFeatures.Length && knownInteractionFeatures.Intersect(testedFeatures).Count() == knownInteractionFeatures.Length,
|
||||
"One or more interaction features has not been added to the testable interaction feature list.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures that EyeGaze isTracked, position, rotation features map correctly to action handles.
|
||||
/// (Since the EyeGaze features use pose instead of devicePose)
|
||||
/// </summary>
|
||||
[UnityTest]
|
||||
public IEnumerator EyeGazeFeatureTest()
|
||||
{
|
||||
EnableFeature<EyeGazeInteraction>();
|
||||
InitializeAndStart();
|
||||
yield return new WaitForXrFrame(1);
|
||||
|
||||
InputAction inputAction = new InputAction(null, InputActionType.Value, "<XRInputV1::EyeTrackingOpenXR>/pose/isTracked");
|
||||
InputControl control = inputAction.controls[0];
|
||||
|
||||
var isTrackedHandle = OpenXRInput.GetActionHandle(new InputAction(null, InputActionType.Value, "<XRInputV1::EyeTrackingOpenXR>/pose/isTracked"));
|
||||
Assert.IsTrue(isTrackedHandle != 0);
|
||||
var positionHandle = OpenXRInput.GetActionHandle(new InputAction(null, InputActionType.Value, "<XRInputV1::EyeTrackingOpenXR>/pose/position"));
|
||||
Assert.IsTrue(positionHandle != 0);
|
||||
var rotationHandle = OpenXRInput.GetActionHandle(new InputAction(null, InputActionType.Value, "<XRInputV1::EyeTrackingOpenXR>/pose/rotation"));
|
||||
Assert.IsTrue(rotationHandle != 0);
|
||||
}
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator InputTrackingAquiredAndLost()
|
||||
{
|
||||
EnableFeature<OculusTouchControllerProfile>();
|
||||
|
||||
var tracked = false;
|
||||
InputTracking.trackingAcquired += (ns) =>
|
||||
{
|
||||
if (ns.nodeType == XRNode.LeftHand)
|
||||
tracked = ns.tracked;
|
||||
};
|
||||
InputTracking.trackingLost += (ns) =>
|
||||
{
|
||||
if (ns.nodeType == XRNode.LeftHand)
|
||||
tracked = ns.tracked;
|
||||
};
|
||||
|
||||
// Node should be untracked before we start
|
||||
Assert.IsFalse(IsNodeTracked(XRNode.LeftHand));
|
||||
|
||||
// Node should be tracked after we start
|
||||
InitializeAndStart();
|
||||
yield return new WaitForXrFrame(1);
|
||||
Assert.IsTrue(tracked, "There's no tracking after initialization and start");
|
||||
|
||||
// Clear the space location flags for the node which should switch to the untracked state
|
||||
var gripAction = OpenXRInput.GetActionHandle(new InputAction(null, InputActionType.Value, "<XRInputV1::Oculus::OculusTouchControllerOpenXR>{LeftHand}/devicePose"));
|
||||
var aimAction = OpenXRInput.GetActionHandle(new InputAction(null, InputActionType.Value, "<XRInputV1::Oculus::OculusTouchControllerOpenXR>{LeftHand}/pointer"));
|
||||
MockRuntime.SetSpace(gripAction, Vector3.zero, Quaternion.identity, XrSpaceLocationFlags.None);
|
||||
MockRuntime.SetSpace(aimAction, Vector3.zero, Quaternion.identity, XrSpaceLocationFlags.None);
|
||||
yield return new WaitForXrFrame(2);
|
||||
Assert.IsFalse(tracked, "Tracking is kept after clearing space location flags");
|
||||
|
||||
// Reset the space location flags to make sure it goes back to tracked state
|
||||
var trackedFlags = XrSpaceLocationFlags.PositionValid | XrSpaceLocationFlags.OrientationValid | XrSpaceLocationFlags.PositionTracked | XrSpaceLocationFlags.OrientationTracked;
|
||||
MockRuntime.SetSpace(gripAction, Vector3.zero, Quaternion.identity, trackedFlags);
|
||||
MockRuntime.SetSpace(aimAction, Vector3.zero, Quaternion.identity, trackedFlags);
|
||||
yield return new WaitForXrFrame(2);
|
||||
Assert.IsTrue(tracked, "There's no tracking after resetting space location flags");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 32a1e7d2570a89b4caac944990252b9f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
286
Packages/com.unity.xr.openxr/Tests/Runtime/OpenXRLoaderSetup.cs
Normal file
286
Packages/com.unity.xr.openxr/Tests/Runtime/OpenXRLoaderSetup.cs
Normal file
@@ -0,0 +1,286 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text.RegularExpressions;
|
||||
using NUnit.Framework;
|
||||
using UnityEditor;
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor.XR.OpenXR.Features;
|
||||
#endif
|
||||
using UnityEngine;
|
||||
using UnityEngine.TestTools;
|
||||
using UnityEngine.XR.Management;
|
||||
using UnityEngine.XR.OpenXR.Features;
|
||||
using UnityEngine.XR.TestTooling;
|
||||
using UnityEngine.XR.OpenXR.Features.Mock;
|
||||
using UnityEngine.XR.OpenXR.NativeTypes;
|
||||
using Assert = UnityEngine.Assertions.Assert;
|
||||
|
||||
[assembly: InternalsVisibleTo("Unity.XR.OpenXR.Tests.Editor")]
|
||||
[assembly: UnityPlatform(RuntimePlatform.WindowsPlayer, RuntimePlatform.WindowsEditor, RuntimePlatform.Android, RuntimePlatform.OSXEditor, RuntimePlatform.OSXPlayer)]
|
||||
|
||||
namespace UnityEngine.XR.OpenXR.Tests
|
||||
{
|
||||
internal class OpenXRLoaderSetup : LoaderTestSetup<OpenXRLoader, OpenXRSettings>
|
||||
{
|
||||
protected override string settingsKey => "OpenXRTestSettings";
|
||||
|
||||
private OpenXRFeature[] savedFeatures = null;
|
||||
|
||||
/// <summary>
|
||||
/// Save the previous value of OpenXRRestarter.DisableApplicationQuit.
|
||||
/// We want to set it to true when running the test, and then restore the value after the test.
|
||||
/// </summary>
|
||||
private bool oldDisableApplicationQuit = false;
|
||||
|
||||
/// <summary>
|
||||
/// Helper method to return a feature of the given type
|
||||
/// </summary>
|
||||
/// <param name="featureType">Feature type</param>
|
||||
/// <returns>Reference to the requested feature or null if not found</returns>
|
||||
protected OpenXRFeature GetFeature(Type featureType) =>
|
||||
OpenXRSettings.ActiveBuildTargetInstance.GetFeature(featureType);
|
||||
|
||||
/// <summary>
|
||||
/// Helper method to return a feature of the given type
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Feature Type</typeparam>
|
||||
/// <returns>Reference to the requested feature or null if not found</returns>
|
||||
protected T GetFeature<T>() where T : OpenXRFeature => GetFeature(typeof(T)) as T;
|
||||
|
||||
/// <summary>
|
||||
/// Enables a required feature of a given type.
|
||||
/// </summary>
|
||||
/// <param name="featureType">Type of feature to enable</param>
|
||||
/// <returns>Feature that was enabled or null</returns>
|
||||
protected OpenXRFeature EnableFeature(Type featureType, bool enable = true)
|
||||
{
|
||||
var feature = GetFeature(featureType);
|
||||
Assert.IsNotNull(feature);
|
||||
feature.enabled = enable;
|
||||
return feature;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enables a required feature of a given type.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of feature to enable</typeparam>
|
||||
/// <returns>Feature that was enabled or null</returns>
|
||||
protected T EnableFeature<T>(bool enable = true) where T : OpenXRFeature => EnableFeature(typeof(T), enable) as T;
|
||||
|
||||
protected bool EnableMockRuntime(bool enable = true)
|
||||
{
|
||||
var feature = MockRuntime.Instance;
|
||||
if (null == feature)
|
||||
return false;
|
||||
|
||||
if (feature.enabled == enable)
|
||||
return true;
|
||||
|
||||
feature.enabled = enable;
|
||||
feature.openxrExtensionStrings = MockRuntime.XR_UNITY_null_gfx + " " + MockRuntime.XR_UNITY_android_present;
|
||||
feature.priority = 0;
|
||||
feature.required = false;
|
||||
feature.ignoreValidationErrors = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected void AddExtension(string extensionName)
|
||||
{
|
||||
MockRuntime.Instance.openxrExtensionStrings += $" {extensionName}";
|
||||
}
|
||||
|
||||
private void DisableAllFeatures()
|
||||
{
|
||||
foreach (var ext in OpenXRSettings.ActiveBuildTargetInstance.features)
|
||||
{
|
||||
ext.enabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma warning disable CS0618
|
||||
public OpenXRLoader Loader => XRGeneralSettings.Instance?.Manager?.loaders[0] as OpenXRLoader;
|
||||
#pragma warning restore CS0618
|
||||
|
||||
|
||||
public override void SetupTest()
|
||||
{
|
||||
base.SetupTest();
|
||||
|
||||
#if UNITY_EDITOR
|
||||
UnityEditor.XR.OpenXR.Features.FeatureHelpers.RefreshFeatures(BuildTargetGroup.Standalone);
|
||||
UnityEditor.XR.OpenXR.Features.FeatureHelpers.RefreshFeatures(BuildPipeline.GetBuildTargetGroup(UnityEditor.EditorUserBuildSettings.activeBuildTarget));
|
||||
#endif
|
||||
|
||||
// Enable all build features
|
||||
var featureTypes = new List<Type>();
|
||||
QueryBuildFeatures(featureTypes);
|
||||
featureTypes.Add(typeof(MockRuntime));
|
||||
foreach (var feature in featureTypes.Select(featureType => OpenXRSettings.ActiveBuildTargetInstance.GetFeature(featureType)).Where(feature => null != feature))
|
||||
{
|
||||
feature.enabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Override to return a list of feature types that should be enabled in the build
|
||||
/// </summary>
|
||||
protected virtual void QueryBuildFeatures(List<Type> featureTypes)
|
||||
{
|
||||
}
|
||||
|
||||
// NOTE: If you override this function, do NOT add the SetUp test attribute.
|
||||
// If you do the overriden function and this function will be called separately
|
||||
// and will most likely invalidate your test or even crash Unity.
|
||||
[SetUp]
|
||||
public virtual void BeforeTest()
|
||||
{
|
||||
// Make sure we are not running
|
||||
if (OpenXRLoaderBase.Instance != null)
|
||||
StopAndShutdown();
|
||||
|
||||
// Disable quitting the application functionality for all tests (since we don't want to exit play mode during the test)
|
||||
oldDisableApplicationQuit = OpenXRRestarter.DisableApplicationQuit;
|
||||
OpenXRRestarter.DisableApplicationQuit = true;
|
||||
|
||||
// Cache off the features before we start
|
||||
savedFeatures = (OpenXRFeature[])OpenXRSettings.ActiveBuildTargetInstance.features.Clone();
|
||||
|
||||
// Disable all features to make sure the feature list is clean before tests start.
|
||||
DisableAllFeatures();
|
||||
|
||||
// Enable the mock runtime and reset it back to default state
|
||||
Assert.IsTrue(EnableMockRuntime());
|
||||
MockRuntime.ResetDefaults();
|
||||
OpenXRRuntime.ClearEvents();
|
||||
OpenXRRestarter.Instance.ResetCallbacks();
|
||||
|
||||
#pragma warning disable CS0618
|
||||
loader = XRGeneralSettings.Instance?.Manager?.loaders[0] as OpenXRLoader;
|
||||
#pragma warning restore CS0618
|
||||
|
||||
#if UNITY_EDITOR && OPENXR_USE_KHRONOS_LOADER
|
||||
var features = FeatureHelpersInternal.GetAllFeatureInfo(BuildTargetGroup.Standalone);
|
||||
foreach (var f in features.Features)
|
||||
{
|
||||
if (f.Feature.nameUi == "Mock Runtime")
|
||||
{
|
||||
var path = Path.GetFullPath(f.PluginPath + "/unity-mock-runtime.json");
|
||||
Environment.SetEnvironmentVariable("XR_RUNTIME_JSON", path);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
[UnityTearDown]
|
||||
public IEnumerator TearDown()
|
||||
{
|
||||
AfterTest();
|
||||
|
||||
yield return null;
|
||||
}
|
||||
|
||||
// NOTE: If you override this function, do NOT add the SetUp test attribute.
|
||||
// If you do the overriden function and this function will be called separately
|
||||
// and will most likely invalidate your test or even crash Unity.
|
||||
public virtual void AfterTest()
|
||||
{
|
||||
#pragma warning disable CS0618
|
||||
var curLoader = XRGeneralSettings.Instance?.Manager?.activeLoader;
|
||||
// Restore the original loader if it got removed during the test
|
||||
if (curLoader == null)
|
||||
XRGeneralSettings.Instance?.Manager?.TryAddLoader(loader);
|
||||
#pragma warning restore CS0618
|
||||
|
||||
OpenXRRestarter.Instance.ResetCallbacks();
|
||||
StopAndShutdown();
|
||||
EnableMockRuntime(false);
|
||||
MockRuntime.Instance.TestCallback = (methodName, param) => true;
|
||||
MockRuntime.KeepFunctionCallbacks = false;
|
||||
MockRuntime.ClearFunctionCallbacks();
|
||||
|
||||
// Replace the features with the saved features
|
||||
OpenXRSettings.ActiveBuildTargetInstance.features = savedFeatures;
|
||||
|
||||
// Wait for all shutdowns and restart requests to finish
|
||||
// and then restore the value of OpenXRRestarter.DisableApplicationQuit
|
||||
WaitForRestarterToFinish();
|
||||
}
|
||||
|
||||
private IEnumerable WaitForRestarterToFinish()
|
||||
{
|
||||
// It is possible that a test may have done something to initiate the OpenXRRestarter. To ensure
|
||||
// that the restarter does not impact other tests we must make sure it finishes before continuing.
|
||||
yield return new WaitForRestarter();
|
||||
|
||||
// Restore the value of DisableApplicationQuit.
|
||||
OpenXRRestarter.DisableApplicationQuit = oldDisableApplicationQuit;
|
||||
}
|
||||
|
||||
public override void Setup()
|
||||
{
|
||||
SetupTest();
|
||||
EnableMockRuntime();
|
||||
base.Setup();
|
||||
}
|
||||
|
||||
public override void Cleanup()
|
||||
{
|
||||
base.Cleanup();
|
||||
TearDownTest();
|
||||
EnableMockRuntime(false);
|
||||
}
|
||||
|
||||
static Dictionary<XrSessionState, HashSet<XrSessionState>> s_AllowedStateTransitions = new Dictionary<XrSessionState, HashSet<XrSessionState>>()
|
||||
{
|
||||
{XrSessionState.Unknown, new HashSet<XrSessionState>() {XrSessionState.Unknown}},
|
||||
{XrSessionState.Idle, new HashSet<XrSessionState>() {XrSessionState.Unknown, XrSessionState.Unknown, XrSessionState.Exiting, XrSessionState.LossPending, XrSessionState.Stopping}},
|
||||
{XrSessionState.Ready, new HashSet<XrSessionState>() {XrSessionState.Idle}},
|
||||
{XrSessionState.Synchronized, new HashSet<XrSessionState>() {XrSessionState.Ready, XrSessionState.Visible}},
|
||||
{XrSessionState.Visible, new HashSet<XrSessionState>() {XrSessionState.Synchronized, XrSessionState.Focused}},
|
||||
{XrSessionState.Focused, new HashSet<XrSessionState>() {XrSessionState.Visible}},
|
||||
{XrSessionState.Stopping, new HashSet<XrSessionState>() {XrSessionState.Synchronized}},
|
||||
{XrSessionState.LossPending, new HashSet<XrSessionState>() {XrSessionState.Unknown, XrSessionState.Idle, XrSessionState.Ready, XrSessionState.Synchronized, XrSessionState.Visible, XrSessionState.Focused, XrSessionState.Stopping, XrSessionState.Exiting, XrSessionState.LossPending}},
|
||||
{XrSessionState.Exiting, new HashSet<XrSessionState>() {XrSessionState.Idle}},
|
||||
};
|
||||
|
||||
public void CheckValidStateTransition(XrSessionState oldState, XrSessionState newState)
|
||||
{
|
||||
bool hasNewState = s_AllowedStateTransitions.ContainsKey(newState);
|
||||
bool canTransitionTo = s_AllowedStateTransitions[newState].Contains(oldState);
|
||||
|
||||
Debug.LogWarning($"Attempting to transition from {oldState} to {newState}");
|
||||
if (!hasNewState)
|
||||
Debug.LogError($"Has {newState} : {hasNewState}");
|
||||
|
||||
if (!canTransitionTo)
|
||||
Debug.LogError($"Can transition from {oldState} to {newState} : {canTransitionTo}");
|
||||
|
||||
|
||||
NUnit.Framework.Assert.IsTrue(hasNewState);
|
||||
NUnit.Framework.Assert.IsTrue(canTransitionTo);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return true if the diagnostic report contains text that matches the given regex
|
||||
/// </summary>
|
||||
/// <param name="match">Regex to match</param>
|
||||
/// <returns>True if the report matches the regex</returns>
|
||||
protected bool DoesDiagnosticReportContain(Regex match) =>
|
||||
match.IsMatch(DiagnosticReport.GenerateReport());
|
||||
|
||||
/// <summary>
|
||||
/// Return true if the diagnostic report contains the given text
|
||||
/// </summary>
|
||||
/// <param name="match">String to search for</param>
|
||||
/// <returns>True if the report contains the given text</returns>
|
||||
protected bool DoesDiagnosticReportContain(string match) =>
|
||||
DiagnosticReport.GenerateReport().Contains(match);
|
||||
|
||||
protected void ProcessOpenXRMessageLoop() => loader.ProcessOpenXRMessageLoop();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9cd6d4e6af6509848ad46f9249bc5a03
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
184
Packages/com.unity.xr.openxr/Tests/Runtime/OpenXRLoaderTests.cs
Normal file
184
Packages/com.unity.xr.openxr/Tests/Runtime/OpenXRLoaderTests.cs
Normal file
@@ -0,0 +1,184 @@
|
||||
#if UNITY_EDITOR && ENABLE_TEST_SUPPORT
|
||||
#define TEST_SUPPORT
|
||||
#endif
|
||||
|
||||
#if TEST_SUPPORT
|
||||
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using NUnit.Framework;
|
||||
using UnityEngine.TestTools;
|
||||
|
||||
[assembly: InternalsVisibleTo("Unity.XR.OpenXR.Tests.Editor")]
|
||||
|
||||
namespace UnityEngine.XR.OpenXR.Tests
|
||||
{
|
||||
internal class OpenXRLoaderTests : OpenXRLoaderSetup
|
||||
{
|
||||
public override void BeforeTest()
|
||||
{
|
||||
RemoveLoaderAndSettings();
|
||||
DestroyLoaderAndSettings();
|
||||
SetupLoaderAndSettings();
|
||||
base.BeforeTest();
|
||||
}
|
||||
|
||||
public override void AfterTest()
|
||||
{
|
||||
base.AfterTest();
|
||||
RemoveLoaderAndSettings();
|
||||
DestroyLoaderAndSettings();
|
||||
SetupLoaderAndSettings();
|
||||
}
|
||||
|
||||
#pragma warning disable CS0649
|
||||
public struct ExpectedLogMessage
|
||||
{
|
||||
public LogType logType;
|
||||
public string matchingRegex;
|
||||
}
|
||||
#pragma warning restore CS0649
|
||||
|
||||
public struct StateTransition
|
||||
{
|
||||
public OpenXRLoader.LoaderState targetState;
|
||||
public bool expectedInitializeReturn;
|
||||
public bool expectedStartReturn;
|
||||
public bool expectedStopReturn;
|
||||
public bool expectedDeinitializeReturn;
|
||||
public ExpectedLogMessage[] expectedLogMessages;
|
||||
}
|
||||
|
||||
public static List<StateTransition> stateTransitions = new List<StateTransition>()
|
||||
{
|
||||
new StateTransition()
|
||||
{
|
||||
targetState = OpenXRLoader.LoaderState.InitializeAttempted,
|
||||
expectedInitializeReturn = false,
|
||||
expectedStartReturn = false,
|
||||
expectedStopReturn = false,
|
||||
expectedDeinitializeReturn = true,
|
||||
expectedLogMessages = new ExpectedLogMessage[] { },
|
||||
},
|
||||
new StateTransition()
|
||||
{
|
||||
targetState = OpenXRLoader.LoaderState.Initialized,
|
||||
expectedInitializeReturn = true,
|
||||
expectedStartReturn = true,
|
||||
expectedStopReturn = true,
|
||||
expectedDeinitializeReturn = true,
|
||||
expectedLogMessages = new ExpectedLogMessage[] { },
|
||||
},
|
||||
new StateTransition()
|
||||
{
|
||||
targetState = OpenXRLoader.LoaderState.StartAttempted,
|
||||
expectedInitializeReturn = true,
|
||||
expectedStartReturn = false,
|
||||
expectedStopReturn = true,
|
||||
expectedDeinitializeReturn = true,
|
||||
expectedLogMessages = new ExpectedLogMessage[] { },
|
||||
},
|
||||
new StateTransition()
|
||||
{
|
||||
targetState = OpenXRLoader.LoaderState.Started,
|
||||
expectedInitializeReturn = true,
|
||||
expectedStartReturn = true,
|
||||
expectedStopReturn = true,
|
||||
expectedDeinitializeReturn = true,
|
||||
expectedLogMessages = new ExpectedLogMessage[] { },
|
||||
},
|
||||
new StateTransition()
|
||||
{
|
||||
targetState = OpenXRLoader.LoaderState.StopAttempted,
|
||||
expectedInitializeReturn = true,
|
||||
expectedStartReturn = true,
|
||||
expectedStopReturn = false,
|
||||
expectedDeinitializeReturn = false,
|
||||
expectedLogMessages = new ExpectedLogMessage[] { },
|
||||
},
|
||||
new StateTransition()
|
||||
{
|
||||
targetState = OpenXRLoader.LoaderState.Stopped,
|
||||
expectedInitializeReturn = true,
|
||||
expectedStartReturn = true,
|
||||
expectedStopReturn = true,
|
||||
expectedDeinitializeReturn = true,
|
||||
expectedLogMessages = new ExpectedLogMessage[] { },
|
||||
},
|
||||
new StateTransition()
|
||||
{
|
||||
targetState = OpenXRLoader.LoaderState.DeinitializeAttempted,
|
||||
expectedInitializeReturn = true,
|
||||
expectedStartReturn = true,
|
||||
expectedStopReturn = true,
|
||||
expectedDeinitializeReturn = false,
|
||||
expectedLogMessages = new ExpectedLogMessage[] { },
|
||||
},
|
||||
};
|
||||
|
||||
[UnityTest]
|
||||
[Category("Loader Tests")]
|
||||
public IEnumerator StateTransitionValidation([ValueSource("stateTransitions")] StateTransition stateTransition)
|
||||
{
|
||||
Assert.IsNotNull(Loader);
|
||||
if (Loader != null)
|
||||
Loader.targetLoaderState = stateTransition.targetState;
|
||||
|
||||
bool ret = Loader.Initialize();
|
||||
yield return null;
|
||||
Assert.AreEqual(stateTransition.expectedInitializeReturn, ret);
|
||||
|
||||
ret = Loader.Start();
|
||||
yield return null;
|
||||
Assert.AreEqual(stateTransition.expectedStartReturn, ret);
|
||||
|
||||
ret = Loader.Stop();
|
||||
yield return null;
|
||||
Assert.AreEqual(stateTransition.expectedStopReturn, ret);
|
||||
|
||||
ret = Loader.Deinitialize();
|
||||
yield return null;
|
||||
Assert.AreEqual(stateTransition.expectedDeinitializeReturn, ret);
|
||||
|
||||
foreach (var expectedLog in stateTransition.expectedLogMessages)
|
||||
{
|
||||
LogAssert.Expect(expectedLog.logType, expectedLog.matchingRegex);
|
||||
}
|
||||
|
||||
Loader.targetLoaderState = OpenXRLoaderBase.LoaderState.Uninitialized;
|
||||
}
|
||||
|
||||
[UnityTest]
|
||||
[Category("Loader Tests")]
|
||||
public IEnumerator CanStopAndStartMultipleTimes()
|
||||
{
|
||||
Assert.IsNotNull(Loader);
|
||||
|
||||
bool ret = Loader.Initialize();
|
||||
Assert.IsTrue(ret);
|
||||
yield return null;
|
||||
Assert.AreEqual(OpenXRLoader.LoaderState.Initialized, Loader.currentLoaderState);
|
||||
|
||||
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
ret = Loader.Start();
|
||||
Assert.IsTrue(ret);
|
||||
yield return null;
|
||||
Assert.AreEqual(OpenXRLoader.LoaderState.Started, Loader.currentLoaderState);
|
||||
|
||||
ret = Loader.Stop();
|
||||
Assert.IsTrue(ret);
|
||||
yield return null;
|
||||
Assert.AreEqual(OpenXRLoader.LoaderState.Stopped, Loader.currentLoaderState);
|
||||
}
|
||||
|
||||
ret = Loader.Deinitialize();
|
||||
Assert.IsTrue(ret);
|
||||
yield return null;
|
||||
Assert.AreEqual(OpenXRLoader.LoaderState.Uninitialized, Loader.currentLoaderState);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // TEST_SUPPORT
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dc089751c573dfb4d8d426e22a9fe5ec
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,89 @@
|
||||
using NUnit.Framework;
|
||||
using System.Collections;
|
||||
using UnityEngine.TestTools;
|
||||
using UnityEngine.XR.OpenXR.Features.Extensions.PerformanceSettings;
|
||||
using UnityEngine.XR.OpenXR.Features.Mock;
|
||||
|
||||
namespace UnityEngine.XR.OpenXR.Tests
|
||||
{
|
||||
internal class OpenXRPerformanceSettingsTest : OpenXRLoaderSetup
|
||||
{
|
||||
[UnityTest]
|
||||
public IEnumerator PerformanceLevelHintIsSet()
|
||||
{
|
||||
base.EnableMockRuntime();
|
||||
base.EnableFeature<XrPerformanceSettingsFeature>();
|
||||
base.InitializeAndStart();
|
||||
yield return new WaitForXrFrame(2);
|
||||
|
||||
const PerformanceDomain performanceDomain = PerformanceDomain.Cpu;
|
||||
const PerformanceLevelHint expectedPerformanceLevel = PerformanceLevelHint.SustainedLow;
|
||||
|
||||
// Set performance level hint
|
||||
bool callSuccess = XrPerformanceSettingsFeature.SetPerformanceLevelHint(performanceDomain, expectedPerformanceLevel);
|
||||
|
||||
yield return new WaitForXrFrame();
|
||||
|
||||
// Get performance level hint in MockRuntime
|
||||
PerformanceLevelHint performanceHintInMock = MockRuntime.PerformanceSettings_GetPerformanceLevelHint(performanceDomain);
|
||||
|
||||
base.StopAndShutdown();
|
||||
|
||||
Assert.IsTrue(callSuccess, "Setting performance level hint failed.");
|
||||
Assert.AreEqual(expectedPerformanceLevel, performanceHintInMock, "Performance level hint wasn't set correctly.");
|
||||
}
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator ReceiveEventNotification_NormalToWarning()
|
||||
{
|
||||
base.EnableMockRuntime();
|
||||
base.EnableFeature<XrPerformanceSettingsFeature>();
|
||||
base.InitializeAndStart();
|
||||
yield return new WaitForXrFrame(2);
|
||||
|
||||
PerformanceChangeNotification expectedNotification = new()
|
||||
{
|
||||
domain = PerformanceDomain.Cpu,
|
||||
subDomain = PerformanceSubDomain.Thermal,
|
||||
fromLevel = PerformanceNotificationLevel.Normal,
|
||||
toLevel = PerformanceNotificationLevel.Warning
|
||||
};
|
||||
bool receivedEvent = false;
|
||||
bool notificationMatches = false;
|
||||
|
||||
// Subscribe to performance level change event
|
||||
XrPerformanceSettingsFeature.OnXrPerformanceChangeNotification += (notification) =>
|
||||
{
|
||||
receivedEvent = true;
|
||||
notificationMatches = expectedNotification.Equals(notification);
|
||||
};
|
||||
|
||||
// Trigger a performance level change event
|
||||
MockRuntime.PerformanceSettings_CauseNotification(PerformanceDomain.Cpu, PerformanceSubDomain.Thermal, PerformanceNotificationLevel.Warning);
|
||||
yield return new WaitForXrFrame(2);
|
||||
|
||||
base.StopAndShutdown();
|
||||
|
||||
// Verify that the event was received
|
||||
Assert.IsTrue(receivedEvent, "Performance change notification event wasn't received.");
|
||||
Assert.IsTrue(notificationMatches, "Performance change notification doesn't match expected notification.");
|
||||
}
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator ExtensionNotInitialized()
|
||||
{
|
||||
base.EnableFeature<XrPerformanceSettingsFeature>(false);
|
||||
base.InitializeAndStart();
|
||||
yield return new WaitForXrFrame(2);
|
||||
|
||||
try
|
||||
{
|
||||
Assert.IsFalse(XrPerformanceSettingsFeature.SetPerformanceLevelHint(PerformanceDomain.Cpu, PerformanceLevelHint.SustainedLow), "Setting performance level hint should fail when the extension is not initialized.");
|
||||
}
|
||||
finally
|
||||
{
|
||||
StopAndShutdown();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6948c0ff319b582489e91d86f4c615b8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
1469
Packages/com.unity.xr.openxr/Tests/Runtime/OpenXRRuntimeTests.cs
Normal file
1469
Packages/com.unity.xr.openxr/Tests/Runtime/OpenXRRuntimeTests.cs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6dbd87dbe22dfb9449d36960109d3522
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,106 @@
|
||||
using NUnit.Framework;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine.TestTools;
|
||||
using UnityEngine.XR.OpenXR.Features;
|
||||
|
||||
namespace UnityEngine.XR.OpenXR.Tests
|
||||
{
|
||||
internal class OpenXRStatisticsTests : OpenXRLoaderSetup
|
||||
{
|
||||
[UnityTest]
|
||||
public IEnumerator RegisterAndSetTestStatistic_UsingStatFlagStatOptionNone()
|
||||
{
|
||||
var feature = ScriptableObject.CreateInstance<SingleStatTestFeature>();
|
||||
feature.ClearStatOnUpdate = false;
|
||||
|
||||
base.InitializeAndStart();
|
||||
yield return null;
|
||||
|
||||
feature.CreateStat();
|
||||
yield return null;
|
||||
|
||||
feature.SetStatValue(1.0f);
|
||||
yield return null;
|
||||
var stat1Success =
|
||||
Provider.XRStats.TryGetStat(GetFirstDisplaySubsystem(), SingleStatTestFeature.StatName, out float value1);
|
||||
|
||||
base.StopAndShutdown();
|
||||
Object.DestroyImmediate(feature);
|
||||
|
||||
Assert.IsTrue(stat1Success);
|
||||
Assert.AreEqual(1.0f, value1);
|
||||
}
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator RegisterAndSetTestStatistic_UsingStatFlagClearOnUpdate()
|
||||
{
|
||||
var feature = ScriptableObject.CreateInstance<SingleStatTestFeature>();
|
||||
feature.ClearStatOnUpdate = true;
|
||||
|
||||
base.InitializeAndStart();
|
||||
yield return null;
|
||||
|
||||
feature.CreateStat();
|
||||
yield return null;
|
||||
|
||||
feature.SetStatValue(1.0f);
|
||||
yield return null;
|
||||
var beforeUpdateSuccess =
|
||||
Provider.XRStats.TryGetStat(GetFirstDisplaySubsystem(), SingleStatTestFeature.StatName, out float beforeUpdateValue);
|
||||
|
||||
yield return null;
|
||||
var afterUpdateSuccess =
|
||||
Provider.XRStats.TryGetStat(GetFirstDisplaySubsystem(), SingleStatTestFeature.StatName, out float afterUpdateValue);
|
||||
|
||||
base.StopAndShutdown();
|
||||
Object.DestroyImmediate(feature);
|
||||
|
||||
Assert.IsTrue(beforeUpdateSuccess);
|
||||
Assert.AreEqual(1.0f, beforeUpdateValue);
|
||||
|
||||
Assert.IsTrue(afterUpdateSuccess);
|
||||
Assert.AreEqual(0.0f, afterUpdateValue);
|
||||
}
|
||||
|
||||
private static IntegratedSubsystem GetFirstDisplaySubsystem()
|
||||
{
|
||||
List<XRDisplaySubsystem> displays = new();
|
||||
SubsystemManager.GetSubsystems(displays);
|
||||
if (displays.Count == 0)
|
||||
{
|
||||
Debug.Log("No display subsystem found.");
|
||||
return null;
|
||||
}
|
||||
return displays[0];
|
||||
}
|
||||
}
|
||||
|
||||
internal class SingleStatTestFeature : OpenXRFeature
|
||||
{
|
||||
public const string StatName = "TestStat";
|
||||
|
||||
[NonSerialized]
|
||||
private ulong m_statId;
|
||||
|
||||
public bool ClearStatOnUpdate { get; set; } = false;
|
||||
|
||||
public void CreateStat()
|
||||
{
|
||||
StatFlags flags = StatFlags.StatOptionNone;
|
||||
|
||||
if (ClearStatOnUpdate)
|
||||
{
|
||||
flags |= StatFlags.ClearOnUpdate;
|
||||
}
|
||||
|
||||
m_statId = RegisterStatsDescriptor(StatName, flags);
|
||||
}
|
||||
|
||||
public void SetStatValue(float value)
|
||||
{
|
||||
SetStatAsFloat(m_statId, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e13d4ddd08ed1b247bb7f401bb9702ad
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"name": "Unity.XR.OpenXR.Tests",
|
||||
"rootNamespace": "",
|
||||
"references": [
|
||||
"UnityEngine.TestRunner",
|
||||
"UnityEditor.TestRunner",
|
||||
"Unity.XR.OpenXR",
|
||||
"Unity.XR.Management",
|
||||
"Unity.XR.Management.Editor",
|
||||
"Unity.XR.OpenXR.Editor",
|
||||
"Unity.InputSystem",
|
||||
"Unity.XR.OpenXR.Features.MockRuntime",
|
||||
"Unity.XR.OpenXR.Features.ConformanceAutomation"
|
||||
],
|
||||
"includePlatforms": [],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false,
|
||||
"overrideReferences": true,
|
||||
"precompiledReferences": [
|
||||
"nunit.framework.dll"
|
||||
],
|
||||
"autoReferenced": false,
|
||||
"defineConstraints": [
|
||||
"UNITY_INCLUDE_TESTS"
|
||||
],
|
||||
"versionDefines": [],
|
||||
"noEngineReferences": false
|
||||
}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2023fa18bf9504e98b11fe2174802802
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,95 @@
|
||||
using NUnit.Framework;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace UnityEngine.XR.OpenXR.Tests
|
||||
{
|
||||
internal class UnityVersionTests
|
||||
{
|
||||
public static readonly string[] s_ValidStrings =
|
||||
{
|
||||
"2020.3.17f1",
|
||||
"2022.1.0a12",
|
||||
"2020.2.0b16",
|
||||
"2020.2.0rc16",
|
||||
"2020.2.0rc1",
|
||||
"2020.2.0p1",
|
||||
"2020.3.17F1",
|
||||
"6000.0.0b16",
|
||||
"7000.1.2f11",
|
||||
"8000.2.12b2"
|
||||
};
|
||||
|
||||
public static readonly string[] s_InvalidStrings =
|
||||
{
|
||||
"20.20.3.17f1",
|
||||
"2022.1.0x12",
|
||||
"2020.2.b16",
|
||||
"a.1.1rc1",
|
||||
"1.a.1rc1",
|
||||
"1.1.1rc",
|
||||
"2020.2,1p1",
|
||||
"2300.1.11f1",
|
||||
"6001.0.0b16",
|
||||
"7123.3.5f15"
|
||||
};
|
||||
|
||||
private static readonly string[] s_SequentialVersions =
|
||||
{
|
||||
"2019.4.0a1",
|
||||
"2020.4.0a1",
|
||||
"2020.5.0a1",
|
||||
"2020.5.0b1",
|
||||
"2020.5.0rc1",
|
||||
"2020.5.0f1",
|
||||
"2020.5.0p1",
|
||||
"2020.5.1p1",
|
||||
"2020.5.1p2",
|
||||
"6000.0.0a1",
|
||||
"6000.0.0b1",
|
||||
"6000.0.0f1",
|
||||
"6000.1.0a1",
|
||||
"7000.0.0a1",
|
||||
"8000.0.0a1"
|
||||
};
|
||||
|
||||
[Test]
|
||||
public void ValidStrings([ValueSource(nameof(s_ValidStrings))] string versionString)
|
||||
{
|
||||
var version = Internal_GetUnityVersion(versionString);
|
||||
Assert.IsTrue(version != 0);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void InvalidStrings([ValueSource(nameof(s_InvalidStrings))] string versionString)
|
||||
{
|
||||
var version = Internal_GetUnityVersion(versionString);
|
||||
Assert.IsTrue(version == 0);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void NumericalCorrectness()
|
||||
{
|
||||
// Convert all of the version strings to numbers
|
||||
var versions = new ulong[s_SequentialVersions.Length];
|
||||
for (int i = 0; i < versions.Length; i++)
|
||||
{
|
||||
versions[i] = Internal_GetUnityVersion(s_SequentialVersions[i]);
|
||||
Assert.IsFalse(versions[i] == 0, $"StringToVersion failed on `{s_SequentialVersions[i]}`");
|
||||
}
|
||||
|
||||
// Make sure all versions are greater than all versions before them in the list
|
||||
for (int i = 1; i < versions.Length; i++)
|
||||
{
|
||||
for (int j = i - 1; j >= 0; j--)
|
||||
{
|
||||
Assert.IsTrue(versions[i] > versions[j], $"{s_SequentialVersions[i]} was not greater than {s_SequentialVersions[j]}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private const string LibraryName = "UnityOpenXR";
|
||||
|
||||
[DllImport(LibraryName, EntryPoint = "NativeConfig_GetUnityVersion", CharSet = CharSet.Ansi)]
|
||||
static extern uint Internal_GetUnityVersion(string unityVersion);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 12d280e0ba13c87498a380f6e8cc0e4f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,76 @@
|
||||
using System;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace UnityEngine.XR.OpenXR.Tests
|
||||
{
|
||||
/// <summary>
|
||||
/// Custom yield instruction that waits for the OpenXRRestarter to restart the loader.
|
||||
/// Note that this will wait until a loader restart has been performed even if a restart
|
||||
/// is not in progress when created.
|
||||
/// </summary>
|
||||
internal sealed class WaitForLoaderRestart : CustomYieldInstruction
|
||||
{
|
||||
private float m_Timeout = 0;
|
||||
private Action m_OldAfterRestart;
|
||||
private Action m_OldAfterCoroutine;
|
||||
private Action m_OldAfterSuccessfulRestart;
|
||||
private bool m_Done;
|
||||
|
||||
public WaitForLoaderRestart(float timeout = 5.0f, bool mustBeSuccessfulRestart = false)
|
||||
{
|
||||
m_Timeout = Time.realtimeSinceStartup + timeout;
|
||||
|
||||
var restarter = OpenXRRestarter.Instance;
|
||||
m_OldAfterRestart = restarter.onAfterRestart;
|
||||
m_OldAfterCoroutine = restarter.onAfterCoroutine;
|
||||
m_OldAfterSuccessfulRestart = restarter.onAfterSuccessfulRestart;
|
||||
|
||||
if (mustBeSuccessfulRestart)
|
||||
{
|
||||
// Wait for a successful restart, then wait for that particular coroutine to finish.
|
||||
restarter.onAfterSuccessfulRestart = () =>
|
||||
{
|
||||
restarter.onAfterCoroutine = () => m_Done = true;
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
restarter.onAfterRestart = () =>
|
||||
{
|
||||
restarter.onAfterCoroutine = () => m_Done = true;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private void RestoreCallbacks()
|
||||
{
|
||||
var restarter = OpenXRRestarter.Instance;
|
||||
restarter.onAfterRestart = m_OldAfterRestart;
|
||||
restarter.onAfterCoroutine = m_OldAfterCoroutine;
|
||||
restarter.onAfterSuccessfulRestart = m_OldAfterSuccessfulRestart;
|
||||
}
|
||||
|
||||
public override bool keepWaiting
|
||||
{
|
||||
get
|
||||
{
|
||||
// Wait until the coroutine is done
|
||||
if (m_Done)
|
||||
{
|
||||
RestoreCallbacks();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Did we time out waiting?
|
||||
if (Time.realtimeSinceStartup > m_Timeout)
|
||||
{
|
||||
Assert.Fail("WaitForLoaderRestart: Timeout");
|
||||
RestoreCallbacks();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9e1f8c68ce3b3964ab84854b5c64cc5b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,78 @@
|
||||
using NUnit.Framework;
|
||||
using System;
|
||||
|
||||
namespace UnityEngine.XR.OpenXR.Tests
|
||||
{
|
||||
/// <summary>
|
||||
/// Custom yield instruction that waits for the OpenXRRestarter to finish shuting down the loader
|
||||
/// without restarting the loader.
|
||||
/// Note that this will wait until a loader shutdown has been performed even if a shutdown
|
||||
/// is not in progress when created.
|
||||
/// </summary>
|
||||
internal sealed class WaitForLoaderShutdown : CustomYieldInstruction
|
||||
{
|
||||
private float m_Timeout = 0;
|
||||
private Action m_OldAfterShutdown;
|
||||
private Action m_OldAfterRestart;
|
||||
private Action m_OldAfterCoroutine;
|
||||
private bool m_Shutdown;
|
||||
private bool m_Restarted;
|
||||
private bool m_Done;
|
||||
|
||||
public WaitForLoaderShutdown(float timeout = 5.0f)
|
||||
{
|
||||
m_Timeout = Time.realtimeSinceStartup + timeout;
|
||||
|
||||
var restarter = OpenXRRestarter.Instance;
|
||||
m_OldAfterShutdown = restarter.onAfterShutdown;
|
||||
m_OldAfterRestart = restarter.onAfterRestart;
|
||||
m_OldAfterCoroutine = restarter.onAfterCoroutine;
|
||||
|
||||
restarter.onAfterShutdown = () =>
|
||||
{
|
||||
m_Shutdown = true;
|
||||
restarter.onAfterRestart = () => m_Restarted = true;
|
||||
restarter.onAfterCoroutine = () => m_Done = true;
|
||||
};
|
||||
}
|
||||
|
||||
private void RestoreCallbacks()
|
||||
{
|
||||
var restarter = OpenXRRestarter.Instance;
|
||||
restarter.onAfterShutdown = m_OldAfterShutdown;
|
||||
restarter.onAfterRestart = m_OldAfterRestart;
|
||||
restarter.onAfterCoroutine = m_OldAfterCoroutine;
|
||||
}
|
||||
|
||||
public override bool keepWaiting
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_Done)
|
||||
{
|
||||
if (!m_Shutdown)
|
||||
{
|
||||
Assert.Fail("WaitForLoaderShutdown: Coroutine finished without shutting down");
|
||||
}
|
||||
else if (m_Restarted)
|
||||
{
|
||||
Assert.Fail("WaitForLoaderShutdown: Waiting for shutdown but loader was restarted");
|
||||
}
|
||||
|
||||
RestoreCallbacks();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Did we time out waiting?
|
||||
if (Time.realtimeSinceStartup > m_Timeout)
|
||||
{
|
||||
Assert.Fail("WaitForLoaderShutdown: Timeout");
|
||||
RestoreCallbacks();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e5fbc19af10596b48b2014bf03c9e8e4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,48 @@
|
||||
using System;
|
||||
using NUnit.Framework;
|
||||
using UnityEngine.XR.OpenXR.Features.Mock;
|
||||
using UnityEngine.XR.OpenXR.NativeTypes;
|
||||
|
||||
namespace UnityEngine.XR.OpenXR.Tests
|
||||
{
|
||||
/// <summary>
|
||||
/// Custom yield instruction that waits for a null OpenXRLoaderBase after initialization has started.
|
||||
/// </summary>
|
||||
internal sealed class WaitForNullXRLoader : CustomYieldInstruction
|
||||
{
|
||||
private float m_Timeout = 0;
|
||||
|
||||
public bool m_startListening = false;
|
||||
|
||||
public WaitForNullXRLoader(float timeout = 5.0f)
|
||||
{
|
||||
m_Timeout = Time.realtimeSinceStartup + timeout;
|
||||
}
|
||||
|
||||
public void StartListening()
|
||||
{
|
||||
m_startListening = true;
|
||||
}
|
||||
|
||||
public override bool keepWaiting
|
||||
{
|
||||
get
|
||||
{
|
||||
// Wait until the coroutine is done
|
||||
if (m_startListening && OpenXRLoaderBase.Instance == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Did we time out waiting?
|
||||
if (Time.realtimeSinceStartup > m_Timeout)
|
||||
{
|
||||
Assert.Fail("WaitForDestroyInstanceCall: Timeout");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bf6c0f80d1eca304fb720a7daa4bf10c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,40 @@
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace UnityEngine.XR.OpenXR.Tests
|
||||
{
|
||||
/// <summary>
|
||||
/// Custom yield instruction that waits for the OpenXRRestarter to finish if it is running.
|
||||
/// Note that ulink WaitForLoaderRestart and WaitForLoaderShutdown this yield instruction
|
||||
/// will not wait if the restarter is not already running.
|
||||
/// </summary>
|
||||
internal sealed class WaitForRestarter : CustomYieldInstruction
|
||||
{
|
||||
private float m_Timeout = 0;
|
||||
|
||||
public WaitForRestarter(float timeout = 5.0f)
|
||||
{
|
||||
m_Timeout = Time.realtimeSinceStartup + timeout;
|
||||
}
|
||||
|
||||
public override bool keepWaiting
|
||||
{
|
||||
get
|
||||
{
|
||||
// Wait until the restarter is finished
|
||||
if (!OpenXRRestarter.Instance.isRunning && OpenXRRestarter.PauseAndRestartCoroutineCount == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Did we time out waiting?
|
||||
if (Time.realtimeSinceStartup > m_Timeout)
|
||||
{
|
||||
Assert.Fail("WaitForLoaderRestart: Timeout");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dae12eb48b9554a46a7dfdccf1913a0f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,42 @@
|
||||
using NUnit.Framework;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace UnityEngine.XR.OpenXR.Tests
|
||||
{
|
||||
/// <summary>
|
||||
/// Custom yield instruction that waits for the tracking origin to regenerate.
|
||||
/// </summary>
|
||||
internal sealed class WaitForTrackingOriginRegeneration : CustomYieldInstruction
|
||||
{
|
||||
[DllImport("UnityOpenXR", EntryPoint = "unity_ext_GetRegenerateTrackingOriginFlag")]
|
||||
[return: MarshalAs(UnmanagedType.U1)]
|
||||
internal static extern bool GetRegenerateTrackingOriginFlag();
|
||||
|
||||
private float m_Timeout = 0;
|
||||
|
||||
public WaitForTrackingOriginRegeneration(float timeout = 5.0f)
|
||||
{
|
||||
m_Timeout = Time.realtimeSinceStartup + timeout;
|
||||
}
|
||||
|
||||
public override bool keepWaiting
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!GetRegenerateTrackingOriginFlag())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Did we time out waiting?
|
||||
if (Time.realtimeSinceStartup > m_Timeout)
|
||||
{
|
||||
Assert.Fail("WaitForTrackingOriginRegeneration: Timeout");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d9ebf2d64363af445b053d57a30e09c3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
59
Packages/com.unity.xr.openxr/Tests/Runtime/WaitForXrFrame.cs
Normal file
59
Packages/com.unity.xr.openxr/Tests/Runtime/WaitForXrFrame.cs
Normal file
@@ -0,0 +1,59 @@
|
||||
using System.Diagnostics;
|
||||
using UnityEngine.XR.OpenXR.Features.Mock;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace UnityEngine.XR.OpenXR.Tests
|
||||
{
|
||||
/// <summary>
|
||||
/// Custom yield instruction that waits for xrEndFrame to be called within OpenXR
|
||||
/// </summary>
|
||||
internal class WaitForXrFrame : CustomYieldInstruction
|
||||
{
|
||||
private int m_Frames = 0;
|
||||
private long m_Timeout;
|
||||
private Stopwatch m_Timer;
|
||||
|
||||
public override bool keepWaiting
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_Frames <= 0)
|
||||
return false;
|
||||
|
||||
if (m_Timer.ElapsedMilliseconds < m_Timeout)
|
||||
return true;
|
||||
|
||||
MockRuntime.onScriptEvent -= OnScriptEvent;
|
||||
Assert.Fail("WaitForXrFrame: Timeout");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public WaitForXrFrame(int frames = 1, float timeout = 10.0f)
|
||||
{
|
||||
m_Frames = frames;
|
||||
m_Timeout = (long)(timeout * 1000.0);
|
||||
if (frames == 0)
|
||||
return;
|
||||
|
||||
// Start waiting for a new frame count
|
||||
MockRuntime.onScriptEvent += OnScriptEvent;
|
||||
|
||||
m_Timer = new Stopwatch();
|
||||
m_Timer.Restart();
|
||||
}
|
||||
|
||||
private void OnScriptEvent(MockRuntime.ScriptEvent evt, ulong param)
|
||||
{
|
||||
if (evt != MockRuntime.ScriptEvent.EndFrame)
|
||||
return;
|
||||
|
||||
m_Frames--;
|
||||
if (m_Frames > 0)
|
||||
return;
|
||||
|
||||
m_Frames = 0;
|
||||
MockRuntime.onScriptEvent -= OnScriptEvent;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c13e63b99a8248eda9d91f10eb39538d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user