modular-avatar/UnitTests~/VirtualMenuTests/VirtualMenuTests.cs
bd_ cb2afcc3d5 fix: Menu Installers on the same object as Merge Armature are not processed
Changes to pass ordering caused Merge Armature to destroy menus before being
processed by Menu Installer; fix this by hoisting menu generation to occur within
the animation services context, before Merge Armature runs. This is safe because
the menu installer pass does not interact with the avatar's animator controllers
directly.
2024-10-01 20:13:02 -07:00

852 lines
32 KiB
C#

#if MA_VRCSDK3_AVATARS
using System;
using System.Collections.Generic;
using System.Linq;
using nadena.dev.modular_avatar.core;
using nadena.dev.modular_avatar.core.editor;
using nadena.dev.modular_avatar.core.editor.menu;
using nadena.dev.modular_avatar.core.menu;
using NUnit.Framework;
using UnityEditor;
using UnityEngine;
using VRC.SDK3.Avatars.Components;
using VRC.SDK3.Avatars.ScriptableObjects;
using Object = UnityEngine.Object;
using Random = UnityEngine.Random;
namespace modular_avatar_tests.VirtualMenuTests
{
public class VirtualMenuTests : TestBase
{
private Texture2D testTex;
private List<UnityEngine.Object> toDestroy;
private int controlIndex;
public override void Setup()
{
base.Setup();
testTex = new Texture2D(1, 1);
toDestroy = new List<UnityEngine.Object>();
controlIndex = 0;
}
public override void Teardown()
{
base.Teardown();
Object.DestroyImmediate(testTex);
foreach (var obj in toDestroy)
{
Object.DestroyImmediate(obj);
}
}
private T Create<T>(string name = null) where T : ScriptableObject
{
if (name == null) name = GUID.Generate().ToString();
T val = ScriptableObject.CreateInstance<T>();
val.name = name;
toDestroy.Add(val);
return val;
}
[Test]
public void TestEmptyMenu()
{
var virtualMenu = new VirtualMenu(null);
virtualMenu.FreezeMenu();
Assert.AreEqual(1, virtualMenu.ResolvedMenu.Count);
var root = virtualMenu.ResolvedMenu[RootMenu.Instance];
Assert.AreEqual(0, root.Controls.Count);
Assert.AreSame(RootMenu.Instance, root.NodeKey);
}
[Test]
public void TestBasicMenu()
{
var rootMenu = Create<VRCExpressionsMenu>();
rootMenu.controls = new List<VRCExpressionsMenu.Control>()
{
GenerateTestControl(),
GenerateTestControl()
};
var virtualMenu = new VirtualMenu(rootMenu);
virtualMenu.FreezeMenu();
Assert.AreEqual(1, virtualMenu.ResolvedMenu.Count);
var root = virtualMenu.NodeForMenuAsset(rootMenu);
Assert.AreEqual(2, root.Controls.Count);
Assert.AreSame(rootMenu, root.SourceMenu());
AssertControlEquals(rootMenu.controls[0], root.Controls[0]);
AssertControlEquals(rootMenu.controls[1], root.Controls[1]);
}
[Test]
public void TestNativeMenuWithCycles()
{
var rootMenu = Create<VRCExpressionsMenu>("root");
var sub1 = Create<VRCExpressionsMenu>("sub1");
var sub2 = Create<VRCExpressionsMenu>("sub2");
rootMenu.controls = new List<VRCExpressionsMenu.Control>()
{
GenerateTestSubmenu(sub1)
};
sub1.controls = new List<VRCExpressionsMenu.Control>()
{
GenerateTestSubmenu(sub2)
};
sub2.controls = new List<VRCExpressionsMenu.Control>()
{
GenerateTestSubmenu(rootMenu)
};
var virtualMenu = new VirtualMenu(rootMenu);
virtualMenu.FreezeMenu();
Assert.AreEqual(3, virtualMenu.ResolvedMenu.Count);
var rootNode = virtualMenu.ResolvedMenu[virtualMenu.RootMenuKey];
var sub1Node = virtualMenu.NodeForMenuAsset(sub1);
var sub2Node = virtualMenu.NodeForMenuAsset(sub2);
Assert.AreEqual(1, rootNode.Controls.Count);
Assert.AreSame(virtualMenu.RootMenuKey, rootNode.NodeKey);
Assert.AreSame(sub1Node, rootNode.Controls[0].SubmenuNode);
Assert.IsNull(rootNode.Controls[0].subMenu);
Assert.AreEqual(1, sub1Node.Controls.Count);
Assert.AreSame(sub1, sub1Node.SourceMenu());
Assert.AreSame(sub2Node, sub1Node.Controls[0].SubmenuNode);
Assert.IsNull(sub1Node.Controls[0].subMenu);
Assert.AreEqual(1, sub2Node.Controls.Count);
Assert.AreSame(sub2, sub2Node.SourceMenu());
Assert.AreSame(rootNode, sub2Node.Controls[0].SubmenuNode);
Assert.IsNull(sub2Node.Controls[0].subMenu);
}
[Test]
public void TestBasicMenuInstaller()
{
VRCExpressionsMenu testMenu = Create<VRCExpressionsMenu>();
testMenu.controls = new List<VRCExpressionsMenu.Control>()
{
GenerateTestControl(),
GenerateTestControl()
};
var installer = CreateInstaller("test");
installer.menuToAppend = testMenu;
var virtualMenu = new VirtualMenu(null);
virtualMenu.RegisterMenuInstaller(installer);
virtualMenu.FreezeMenu();
Assert.AreEqual(1, virtualMenu.ResolvedMenu.Count);
var root = virtualMenu.ResolvedMenu[RootMenu.Instance];
Assert.AreEqual(2, root.Controls.Count);
Assert.AreSame(RootMenu.Instance, root.NodeKey);
AssertControlEquals(testMenu.controls[0], root.Controls[0]);
AssertControlEquals(testMenu.controls[1], root.Controls[1]);
}
[Test]
public void TestMenuItemInstaller()
{
var installer = CreateInstaller("test");
installer.menuToAppend = Create<VRCExpressionsMenu>();
var item = installer.gameObject.AddComponent<ModularAvatarMenuItem>();
item.Control = GenerateTestControl();
var virtualMenu = new VirtualMenu(null);
virtualMenu.RegisterMenuInstaller(installer);
virtualMenu.FreezeMenu();
item.Control.name = "test";
Assert.AreEqual(1, virtualMenu.ResolvedMenu.Count);
var root = virtualMenu.ResolvedMenu[RootMenu.Instance];
Assert.AreEqual(1, root.Controls.Count);
Assert.AreSame(RootMenu.Instance, root.NodeKey);
AssertControlEquals(item.Control, root.Controls[0]);
}
[Test]
public void TestInstallOntoInstaller()
{
var installer_a = CreateInstaller("a");
var installer_b = CreateInstaller("b");
var installer_c = CreateInstaller("c");
var menu_a = Create<VRCExpressionsMenu>();
var menu_b = Create<VRCExpressionsMenu>();
var menu_c = Create<VRCExpressionsMenu>();
menu_a.controls = new List<VRCExpressionsMenu.Control>()
{
GenerateTestControl()
};
menu_b.controls = new List<VRCExpressionsMenu.Control>()
{
GenerateTestControl()
};
menu_c.controls = new List<VRCExpressionsMenu.Control>()
{
GenerateTestControl()
};
installer_a.menuToAppend = menu_a;
installer_b.menuToAppend = menu_b;
installer_c.menuToAppend = menu_c;
installer_c.installTargetMenu = installer_a.menuToAppend;
VirtualMenu virtualMenu = new VirtualMenu(null);
virtualMenu.RegisterMenuInstaller(installer_a);
virtualMenu.RegisterMenuInstaller(installer_b);
virtualMenu.RegisterMenuInstaller(installer_c);
virtualMenu.FreezeMenu();
Assert.AreEqual(1, virtualMenu.ResolvedMenu.Count);
var rootMenu = virtualMenu.ResolvedMenu[RootMenu.Instance];
Assert.AreEqual(3, rootMenu.Controls.Count);
Assert.AreSame(RootMenu.Instance, rootMenu.NodeKey);
AssertControlEquals(menu_a.controls[0], rootMenu.Controls[0]);
AssertControlEquals(menu_c.controls[0], rootMenu.Controls[1]);
AssertControlEquals(menu_b.controls[0], rootMenu.Controls[2]);
}
[Test]
public void TestInstallSubmenu()
{
var installer_a = CreateInstaller("a");
var menu_a = Create<VRCExpressionsMenu>("a");
var menu_b = Create<VRCExpressionsMenu>("b");
menu_a.controls = new List<VRCExpressionsMenu.Control>()
{
GenerateTestSubmenu(menu_b)
};
menu_b.controls = new List<VRCExpressionsMenu.Control>()
{
GenerateTestControl()
};
installer_a.menuToAppend = menu_a;
VirtualMenu virtualMenu = new VirtualMenu(null);
virtualMenu.RegisterMenuInstaller(installer_a);
virtualMenu.FreezeMenu();
Assert.AreEqual(2, virtualMenu.ResolvedMenu.Count);
var rootMenu = virtualMenu.ResolvedMenu[RootMenu.Instance];
var subMenu = virtualMenu.NodeForMenuAsset(menu_b);
Assert.AreSame(subMenu, rootMenu.Controls[0].SubmenuNode);
Assert.AreSame(RootMenu.Instance, rootMenu.NodeKey);
Assert.AreSame(menu_b, ((ValueTuple<object, object>) subMenu.NodeKey).Item1);
Assert.AreEqual(1, subMenu.Controls.Count);
AssertControlEquals(menu_b.controls[0], subMenu.Controls[0]);
}
[Test]
public void TestYankInstaller()
{
var installer_a = CreateInstaller("a");
var installer_b = CreateInstaller("b");
var menu_a = Create<VRCExpressionsMenu>("a");
menu_a.controls = new List<VRCExpressionsMenu.Control>()
{
GenerateTestControl()
};
installer_a.menuToAppend = menu_a;
var item = installer_b.gameObject.AddComponent<ModularAvatarMenuItem>();
item.Control = GenerateTestControl();
item.Control.type = VRCExpressionsMenu.Control.ControlType.SubMenu;
item.MenuSource = SubmenuSource.Children;
var child = CreateChild(item.gameObject, "child");
var childItem = child.AddComponent<ModularAvatarMenuInstallTarget>();
childItem.installer = installer_a;
VirtualMenu virtualMenu = new VirtualMenu(null);
virtualMenu.RegisterMenuInstaller(installer_a);
virtualMenu.RegisterMenuInstaller(installer_b);
virtualMenu.RegisterMenuInstallTarget(childItem);
virtualMenu.FreezeMenu();
item.Control.name = "b";
Assert.AreEqual(2, virtualMenu.ResolvedMenu.Count);
var rootMenu = virtualMenu.ResolvedMenu[RootMenu.Instance];
var item_node = virtualMenu.ResolvedMenu[new MenuNodesUnder(item.gameObject)];
Assert.AreEqual(1, rootMenu.Controls.Count);
Assert.AreSame(RootMenu.Instance, rootMenu.NodeKey);
AssertControlEquals(item.Control, rootMenu.Controls[0]);
Assert.AreSame(item_node, rootMenu.Controls[0].SubmenuNode);
Assert.AreEqual(1, item_node.Controls.Count);
Assert.AreEqual(new MenuNodesUnder(item.gameObject), item_node.NodeKey);
AssertControlEquals(menu_a.controls[0], item_node.Controls[0]);
}
[Test]
public void WhenMenuInstallersLoop_LoopIsTerminated()
{
var installer_a = CreateInstaller("a");
var installer_b = CreateInstaller("b");
var menu_a = Create<VRCExpressionsMenu>("a");
menu_a.controls = new List<VRCExpressionsMenu.Control>()
{
GenerateTestControl()
};
installer_a.menuToAppend = menu_a;
var menu_b = Create<VRCExpressionsMenu>("b");
menu_b.controls = new List<VRCExpressionsMenu.Control>()
{
GenerateTestControl()
};
installer_b.menuToAppend = menu_b;
installer_a.installTargetMenu = menu_b;
installer_b.installTargetMenu = menu_a;
VirtualMenu virtualMenu = new VirtualMenu(menu_a);
virtualMenu.RegisterMenuInstaller(installer_a);
virtualMenu.RegisterMenuInstaller(installer_b);
virtualMenu.FreezeMenu();
Assert.AreEqual(1, virtualMenu.ResolvedMenu.Count);
var rootMenu = virtualMenu.NodeForMenuAsset(menu_a);
Assert.AreEqual(3, rootMenu.Controls.Count);
}
[Test]
public void TestExternalSubmenuSource_WithMenuInstaller()
{
var installer_a = CreateInstaller("a");
var installer_b = CreateInstaller("b");
var menu_a = Create<VRCExpressionsMenu>("a");
menu_a.controls = new List<VRCExpressionsMenu.Control>()
{
GenerateTestControl()
};
var menu_b = Create<VRCExpressionsMenu>("b");
menu_b.controls = new List<VRCExpressionsMenu.Control>()
{
GenerateTestControl()
};
var item_a = installer_a.gameObject.AddComponent<ModularAvatarMenuItem>();
item_a.Control = GenerateTestControl();
item_a.Control.type = VRCExpressionsMenu.Control.ControlType.SubMenu;
item_a.MenuSource = SubmenuSource.MenuAsset;
item_a.Control.subMenu = menu_a;
installer_b.menuToAppend = menu_b;
installer_b.installTargetMenu = menu_a;
VirtualMenu virtualMenu = new VirtualMenu(null);
virtualMenu.RegisterMenuInstaller(installer_a);
virtualMenu.RegisterMenuInstaller(installer_b);
virtualMenu.FreezeMenu();
var rootNode = virtualMenu.ResolvedMenu[RootMenu.Instance];
Assert.AreEqual(VRCExpressionsMenu.Control.ControlType.SubMenu, rootNode.Controls[0].type);
var menu_a_node = rootNode.Controls[0].SubmenuNode;
AssertControlEquals(menu_a.controls[0], menu_a_node.Controls[0]);
AssertControlEquals(menu_b.controls[0], menu_a_node.Controls[1]);
}
[Test]
public void MenuItem_NestedSubmenuNodes()
{
var installer_a = CreateInstaller("root");
var root_item = installer_a.gameObject.AddComponent<ModularAvatarMenuItem>();
root_item.Control = GenerateTestControl();
root_item.Control.type = VRCExpressionsMenu.Control.ControlType.SubMenu;
root_item.MenuSource = SubmenuSource.Children;
var mid_obj = CreateChild(root_item.gameObject, "mid");
var mid_item = mid_obj.AddComponent<ModularAvatarMenuItem>();
mid_item.Control = GenerateTestControl();
mid_item.Control.type = VRCExpressionsMenu.Control.ControlType.SubMenu;
mid_item.MenuSource = SubmenuSource.Children;
var leaf_obj = CreateChild(mid_obj, "leaf");
var leaf_item = leaf_obj.AddComponent<ModularAvatarMenuItem>();
leaf_item.Control = GenerateTestControl();
VirtualMenu virtualMenu = new VirtualMenu(null);
virtualMenu.RegisterMenuInstaller(installer_a);
virtualMenu.FreezeMenu();
var rootNode = virtualMenu.ResolvedMenu[RootMenu.Instance];
Assert.AreEqual(VRCExpressionsMenu.Control.ControlType.SubMenu, rootNode.Controls[0].type);
var mid_node = rootNode.Controls[0].SubmenuNode;
var leaf_node = mid_node.Controls[0].SubmenuNode;
Assert.AreEqual(1, rootNode.Controls.Count);
Assert.AreEqual(1, mid_node.Controls.Count);
Assert.AreEqual(1, leaf_node.Controls.Count);
root_item.Control.name = "root";
mid_item.Control.name = "mid";
leaf_item.Control.name = "leaf";
AssertControlEquals(root_item.Control, rootNode.Controls[0]);
AssertControlEquals(mid_item.Control, mid_node.Controls[0]);
AssertControlEquals(leaf_item.Control, leaf_node.Controls[0]);
}
[Test]
public void MenuItem_RemoteReference()
{
var installer_a = CreateInstaller("root");
var root_item = installer_a.gameObject.AddComponent<ModularAvatarMenuItem>();
var extern_root = CreateRoot("test");
var extern_obj = CreateChild(extern_root, "control");
var extern_item = extern_obj.AddComponent<ModularAvatarMenuItem>();
extern_item.Control = GenerateTestControl();
root_item.Control = GenerateTestControl();
root_item.Control.type = VRCExpressionsMenu.Control.ControlType.SubMenu;
root_item.MenuSource = SubmenuSource.Children;
root_item.menuSource_otherObjectChildren = extern_root;
VirtualMenu virtualMenu = new VirtualMenu(null);
virtualMenu.RegisterMenuInstaller(installer_a);
virtualMenu.FreezeMenu();
root_item.Control.name = "root";
extern_item.Control.name = "control";
var rootNode = virtualMenu.ResolvedMenu[RootMenu.Instance];
Assert.AreEqual(VRCExpressionsMenu.Control.ControlType.SubMenu, rootNode.Controls[0].type);
var extern_node = rootNode.Controls[0].SubmenuNode;
Assert.AreEqual(1, rootNode.Controls.Count);
Assert.AreEqual(1, extern_node.Controls.Count);
AssertControlEquals(root_item.Control, rootNode.Controls[0]);
AssertControlEquals(extern_item.Control, extern_node.Controls[0]);
}
[Test]
public void InstallerInstallsInstallTarget()
{
var installer_a = CreateInstaller("a");
var installer_b = CreateInstaller("b");
var menu_b = Create<VRCExpressionsMenu>("b");
menu_b.controls = new List<VRCExpressionsMenu.Control>()
{
GenerateTestControl()
};
installer_b.menuToAppend = menu_b;
var item_a = installer_a.gameObject.AddComponent<ModularAvatarMenuInstallTarget>();
item_a.installer = installer_b;
VirtualMenu virtualMenu = new VirtualMenu(null);
virtualMenu.RegisterMenuInstaller(installer_a);
virtualMenu.RegisterMenuInstaller(installer_b);
virtualMenu.RegisterMenuInstallTarget(item_a);
virtualMenu.FreezeMenu();
var rootNode = virtualMenu.ResolvedMenu[RootMenu.Instance];
Assert.AreEqual(1, rootNode.Controls.Count);
AssertControlEquals(menu_b.controls[0], rootNode.Controls[0]);
}
[Test]
public void TestSerializeMenu()
{
var menu_a = Create<VRCExpressionsMenu>("test");
var menu_b = Create<VRCExpressionsMenu>("test2");
var menu_c = Create<VRCExpressionsMenu>("test3");
menu_a.controls = new List<VRCExpressionsMenu.Control>()
{
GenerateTestControl(),
GenerateTestSubmenu(menu_b),
};
menu_b.controls = new List<VRCExpressionsMenu.Control>()
{
GenerateTestControl(),
GenerateTestSubmenu(menu_c),
};
menu_c.controls = new List<VRCExpressionsMenu.Control>()
{
GenerateTestSubmenu(menu_a),
};
var virtualMenu = new VirtualMenu(menu_a);
virtualMenu.FreezeMenu();
var assetSet = new HashSet<UnityEngine.Object>();
var serialized = virtualMenu.SerializeMenu(obj => assetSet.Add(obj));
Assert.AreEqual(3, assetSet.Count);
Assert.AreEqual(2, serialized.controls.Count);
AssertControlEquals(menu_a.controls[0], serialized.controls[0]);
AssertControlEquals(menu_a.controls[1], serialized.controls[1]);
var serialized_b = serialized.controls[1].subMenu;
Assert.AreEqual(2, serialized_b.controls.Count);
AssertControlEquals(menu_b.controls[0], serialized_b.controls[0]);
AssertControlEquals(menu_b.controls[1], serialized_b.controls[1]);
var serialized_c = serialized_b.controls[1].subMenu;
Assert.AreEqual(1, serialized_c.controls.Count);
AssertControlEquals(menu_c.controls[0], serialized_c.controls[0]);
Assert.True(assetSet.Contains(serialized));
Assert.True(assetSet.Contains(serialized_b));
Assert.True(assetSet.Contains(serialized_c));
}
[Test]
public void InstallTargetToInstallerToInstaller()
{
var menu_a = Create<VRCExpressionsMenu>();
var menu_b = Create<VRCExpressionsMenu>();
var menu_c = Create<VRCExpressionsMenu>();
menu_c.controls = new List<VRCExpressionsMenu.Control>
{
GenerateTestControl()
};
var node_a = CreateInstaller("root");
var item_a = node_a.gameObject.AddComponent<ModularAvatarMenuInstallTarget>();
var node_b = CreateInstaller("menu_b");
node_b.menuToAppend = menu_c;
var node_c = CreateInstaller("menu_c");
node_c.installTargetMenu = menu_c;
node_b.transform.parent = node_a.transform;
item_a.installer = node_b;
var virtualMenu = new VirtualMenu(menu_a);
virtualMenu.RegisterMenuInstallTarget(item_a);
virtualMenu.RegisterMenuInstaller(node_a);
virtualMenu.RegisterMenuInstaller(node_b);
virtualMenu.RegisterMenuInstaller(node_c);
virtualMenu.FreezeMenu();
var root = virtualMenu.NodeForMenuAsset(menu_a);
Assert.AreEqual(1, root.Controls.Count);
}
[Test]
public void multipleMenuAssets_areInstalledMultipleTimes()
{
var menu_a = Create<VRCExpressionsMenu>();
menu_a.controls.Add(new VRCExpressionsMenu.Control()
{
name = "control",
parameter = new VRCExpressionsMenu.Control.Parameter()
{
name = "p"
},
type = VRCExpressionsMenu.Control.ControlType.Toggle
});
var av_root = CreateRoot("avatar");
var node_a = CreateInstaller("menu_a");
node_a.transform.SetParent(av_root.transform);
node_a.menuToAppend = menu_a;
var node_b = CreateInstaller("menu_b");
node_b.transform.SetParent(av_root.transform);
node_b.menuToAppend = menu_a;
var virtualMenu = VirtualMenu.ForAvatar(av_root.GetComponent<VRCAvatarDescriptor>());
virtualMenu.FreezeMenu();
Assert.AreEqual(2, virtualMenu.RootMenuNode.Controls.Count);
}
[Test]
public void remapParams_isAppliedSeparatelyForEachDedup()
{
var menu_a = Create<VRCExpressionsMenu>();
menu_a.controls.Add(new VRCExpressionsMenu.Control()
{
name = "control",
parameter = new VRCExpressionsMenu.Control.Parameter()
{
name = "p"
},
type = VRCExpressionsMenu.Control.ControlType.Toggle
});
var menu_outer = Create<VRCExpressionsMenu>();
menu_outer.controls.Add(new VRCExpressionsMenu.Control()
{
name = "control",
type = VRCExpressionsMenu.Control.ControlType.SubMenu,
subMenu = menu_a
});
var av_root = CreateRoot("avatar");
var node_a = CreateInstaller("menu_a");
node_a.transform.SetParent(av_root.transform);
node_a.menuToAppend = menu_outer;
node_a.gameObject.AddComponent<ModularAvatarParameters>().parameters = new List<ParameterConfig>()
{
new ParameterConfig()
{
nameOrPrefix = "p",
remapTo = "a",
}
};
var node_b = CreateInstaller("menu_b");
node_b.transform.SetParent(av_root.transform);
node_b.menuToAppend = menu_outer;
node_b.gameObject.AddComponent<ModularAvatarParameters>().parameters = new List<ParameterConfig>()
{
new ParameterConfig()
{
nameOrPrefix = "p",
remapTo = "b",
}
};
var buildContext = new BuildContext(av_root.GetComponent<VRCAvatarDescriptor>());
new RenameParametersHook().OnPreprocessAvatar(av_root, buildContext);
var virtualMenu = VirtualMenu.ForAvatar(av_root.GetComponent<VRCAvatarDescriptor>(), buildContext);
virtualMenu.FreezeMenu();
Assert.IsTrue(virtualMenu.RootMenuNode.Controls.Any(c =>
c.SubmenuNode.Controls[0].parameter.name == "a"
));
Assert.IsTrue(virtualMenu.RootMenuNode.Controls.Any(c =>
c.SubmenuNode.Controls[0].parameter.name == "b"
));
}
[Test]
public void internalParameterTest()
{
var root = CreatePrefab("InternalParameterTest.prefab");
BuildContext buildContext = new BuildContext(root.GetComponent<VRCAvatarDescriptor>());
new RenameParametersHook().OnPreprocessAvatar(root, buildContext);
var virtualMenu = VirtualMenu.ForAvatar(root.GetComponent<VRCAvatarDescriptor>(), buildContext);
Assert.AreNotEqual("x", virtualMenu.RootMenuNode.Controls[0]
.SubmenuNode.Controls[0].parameter.name);
}
[Test]
public void UnusedSubParametersAreStripped()
{
var root = CreatePrefab("UnusedSubParametersAreStripped.prefab");
BuildContext buildContext = new BuildContext(root.GetComponent<VRCAvatarDescriptor>());
new RenameParametersHook().OnPreprocessAvatar(root, buildContext);
var virtualMenu = VirtualMenu.ForAvatar(root.GetComponent<VRCAvatarDescriptor>(), buildContext);
// Button
Assert.AreEqual(0, virtualMenu.RootMenuNode.Controls[0].SubmenuNode.Controls[0].subParameters.Length);
// Toggle
Assert.AreEqual(0, virtualMenu.RootMenuNode.Controls[0].SubmenuNode.Controls[1].subParameters.Length);
// SubMenu
Assert.AreEqual(0, virtualMenu.RootMenuNode.Controls[0].SubmenuNode.Controls[2].subParameters.Length);
// Two Axis
Assert.AreEqual(2, virtualMenu.RootMenuNode.Controls[0].SubmenuNode.Controls[3].subParameters.Length);
// Radial
Assert.AreEqual(1, virtualMenu.RootMenuNode.Controls[0].SubmenuNode.Controls[4].subParameters.Length);
// Four Axis
Assert.AreEqual(4, virtualMenu.RootMenuNode.Controls[0].SubmenuNode.Controls[5].subParameters.Length);
}
[Test]
public void MergeArmatureAndMenuInstallerOnSameObjectWorks()
{
var root = CreateRoot("root");
var armature = CreateChild(root, "Armature");
var installer = armature.AddComponent<ModularAvatarMenuInstaller>();
var merge = installer.gameObject.AddComponent<ModularAvatarMergeArmature>();
merge.mergeTarget.Set(root);
var menu = Create<VRCExpressionsMenu>();
menu.controls.Add(new VRCExpressionsMenu.Control()
{
name = "control",
type = VRCExpressionsMenu.Control.ControlType.Toggle
});
installer.menuToAppend = menu;
AvatarProcessor.ProcessAvatar(root);
var realizedMenu = root.GetComponent<VRCAvatarDescriptor>().expressionsMenu;
Assert.AreEqual(1, realizedMenu.controls.Count);
Assert.AreEqual("control", realizedMenu.controls[0].name);
}
ModularAvatarMenuInstaller CreateInstaller(string name)
{
GameObject obj = new GameObject();
obj.name = name;
var installer = obj.AddComponent<ModularAvatarMenuInstaller>();
installer.name = name;
toDestroy.Add(obj);
return installer;
}
VRCExpressionsMenu.Control GenerateTestSubmenu(VRCExpressionsMenu menu)
{
var control = GenerateTestControl();
control.type = VRCExpressionsMenu.Control.ControlType.SubMenu;
control.subMenu = menu;
control.subParameters = Array.Empty<VRCExpressionsMenu.Control.Parameter>();
return control;
}
VRCExpressionsMenu.Control GenerateTestControl()
{
var control = new VRCExpressionsMenu.Control();
VRCExpressionsMenu.Control.ControlType[] types = new[]
{
VRCExpressionsMenu.Control.ControlType.Button,
// VRCExpressionsMenu.Control.ControlType.SubMenu,
VRCExpressionsMenu.Control.ControlType.Toggle,
VRCExpressionsMenu.Control.ControlType.RadialPuppet,
VRCExpressionsMenu.Control.ControlType.FourAxisPuppet,
VRCExpressionsMenu.Control.ControlType.TwoAxisPuppet,
};
control.type = types[Random.Range(0, types.Length)];
control.name = "Test Control " + controlIndex++;
control.parameter = new VRCExpressionsMenu.Control.Parameter();
control.parameter.name = "Test Parameter " + GUID.Generate();
control.icon = new Texture2D(1, 1);
control.labels = new[]
{
new VRCExpressionsMenu.Control.Label()
{
name = "label",
icon = testTex
}
};
if (control.type == VRCExpressionsMenu.Control.ControlType.RadialPuppet
|| control.type == VRCExpressionsMenu.Control.ControlType.FourAxisPuppet
|| control.type == VRCExpressionsMenu.Control.ControlType.TwoAxisPuppet)
{
control.subParameters = new[]
{
new VRCExpressionsMenu.Control.Parameter()
{
name = "Test Sub Parameter " + GUID.Generate()
}
};
} else
{
control.subParameters = Array.Empty<VRCExpressionsMenu.Control.Parameter>();
}
control.value = 0.42f;
control.style = VRCExpressionsMenu.Control.Style.Style3;
return control;
}
void AssertControlEquals(VRCExpressionsMenu.Control expected, VRCExpressionsMenu.Control actual)
{
Assert.AreEqual(expected.type, actual.type);
Assert.AreEqual(expected.name, actual.name);
Assert.AreEqual(expected.parameter.name, actual.parameter.name);
Assert.AreNotSame(expected.parameter, actual.parameter);
Assert.AreEqual(expected.icon, actual.icon);
Assert.AreEqual(expected.labels.Length, actual.labels.Length);
Assert.AreEqual(expected.labels[0].name, actual.labels[0].name);
Assert.AreEqual(expected.labels[0].icon, actual.labels[0].icon);
switch (expected.type)
{
case VRCExpressionsMenu.Control.ControlType.Button:
case VRCExpressionsMenu.Control.ControlType.Toggle:
case VRCExpressionsMenu.Control.ControlType.SubMenu:
Assert.AreEqual(0, actual.subParameters.Length);
break;
default:
{
Assert.AreEqual(expected.subParameters.Length, actual.subParameters.Length);
Assert.AreEqual(expected.subParameters[0].name, actual.subParameters[0].name);
Assert.AreNotSame(expected.subParameters[0], actual.subParameters[0]);
break;
}
}
Assert.AreEqual(expected.value, actual.value);
Assert.AreEqual(expected.style, actual.style);
}
}
internal static class TestHelpers
{
internal static VirtualMenuNode NodeForMenuAsset(this VirtualMenu menu, VRCExpressionsMenu asset)
{
return menu.ResolvedMenu.FirstOrDefault(
kvp => kvp.Key is ValueTuple<object, object> tuple && ReferenceEquals(tuple.Item1, asset)
).Value;
}
internal static VRCExpressionsMenu SourceMenu(this VirtualMenuNode node)
{
if (node.NodeKey is ValueTuple<object, object> tuple && tuple.Item1 is VRCExpressionsMenu menu)
{
return menu;
}
return null;
}
}
}
#endif