Adaug proiectul Unity

This commit is contained in:
2026-11-01 23:39:55 +02:00
parent cb14d9d37d
commit 863eac8f2c
23463 changed files with 5412601 additions and 0 deletions
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 914613548ded44a43a59eb9852503f18
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,3 @@
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("Unity.TextMeshPro.Editor.Tests")]
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 06b06f5f5563e9a43b274eadc5373cc0
@@ -0,0 +1,52 @@
using UnityEngine.TestTools;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace TMPro
{
/// <summary>
/// Imports the TMP Essential Resources into the test project before the test run starts, so that
/// fixtures relying on TMP_Settings and the default font asset have them available.
/// </summary>
/// <remarks>
/// This runs in the Editor for Edit Mode and Play Mode runs alike, and ahead of a player build,
/// which is why it lives in the Play Mode assembly with an editor-only body.
///
/// To reduce boilerplate derive from <see cref="TMPTestSuiteWithEssentialResources"/> rather than referencing this
/// directly.
/// </remarks>
internal class TMPEssentialResourcesImporter : IPrebuildSetup
{
/// <summary>
/// GUID of the "TextMesh Pro" folder that importing the Essential Resources creates.
/// </summary>
const string k_EssentialResourcesFolderGUID = "f54d1bd14bd3ca042bd867b519fee8cc";
/// <summary>
/// GUID for Roboto-Bold.ttf, which ships in Examples and Extras rather than the Essential Resources.
/// </summary>
internal const string k_ExampleFontGUID = "4beb055f07aaff244873dec698d0363e";
public virtual void Setup()
{
Import(essentials: true, examples: false);
}
protected static void Import(bool essentials, bool examples)
{
#if UNITY_EDITOR
var importEssentials = essentials
&& string.IsNullOrEmpty(AssetDatabase.GUIDToAssetPath(k_EssentialResourcesFolderGUID));
var importExamples = examples
&& string.IsNullOrEmpty(AssetDatabase.GUIDToAssetPath(k_ExampleFontGUID));
if (!importEssentials && !importExamples)
return;
TMP_PackageResourceImporter.ImportResources(importEssentials, importExamples, false);
AssetDatabase.Refresh();
#endif
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 2b3c35f9f9f3cb04b96b5cd06fc548bd
@@ -0,0 +1,25 @@
using NUnit.Framework;
using UnityEngine.TestTools;
namespace TMPro
{
/// <summary>
/// Base fixture for TMP tests that need the TMP Essential Resources.
/// </summary>
[PrebuildSetup(typeof(TMPEssentialResourcesImporter))]
internal abstract class TMPTestSuiteWithEssentialResources
{
/// <summary>
/// Checks that the resources arrived before any test in the fixture runs. Override to add
/// fixture-specific setup, ensuring to call base.OneTimeSetup().
/// </summary>
[OneTimeSetUp]
public virtual void OneTimeSetup()
{
Assert.IsNotNull(TMP_Settings.instance,
$"TMP_Settings could not be loaded, so {nameof(TMPEssentialResourcesImporter)} did not make the TMP Essential Resources available to this fixture.");
Assert.IsNotNull(TMP_Settings.defaultFontAsset,
$"TMP_Settings has no default font asset, so {nameof(TMPEssentialResourcesImporter)} did not make the TMP Essential Resources available to this fixture.");
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 897920160c2045844a3119a3a3d937fb
@@ -0,0 +1,47 @@
using UnityEngine;
using NUnit.Framework;
using System.IO;
namespace TMPro
{
internal class TMP_CanvasTests
{
[OneTimeSetUp]
public void OneTimeSetUp()
{
if (!Directory.Exists(Path.GetFullPath("Assets/TextMesh Pro")))
{
Debug.Log("Skipping over Editor tests as TMP Essential Resources are missing from the current test project.");
Assert.Ignore();
return;
}
}
[Test]
public void EnablingAndDisablingCanvasDoesNotRegenerateText() // (UUM-45320)
{
var go = new GameObject();
var canvas = go.AddComponent<Canvas>();
var goChild = new GameObject();
var text = goChild.AddComponent<TextMeshProUGUI>();
goChild.transform.SetParent(go.transform);
// Force text to be generated
text.text = "Hello World";
text.ForceMeshUpdate();
Assert.IsFalse(text.havePropertiesChanged, "Text should not have changed yet");
canvas.enabled = false;
canvas.enabled = true;
Assert.IsFalse(text.havePropertiesChanged, "Text should not have changed after enabling / disabling canvas");
// Cleanup
Object.DestroyImmediate(go);
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 5348cbfcd4f74699b33fe6b43747a016
timeCreated: 1703006962
@@ -0,0 +1,268 @@
using System.IO;
using NUnit.Framework;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.TestTools;
using UnityEngine.UI;
namespace TMPro
{
/// <summary>
/// End to end coverage for the reported bug: "TextMeshPro input field doesn't remove
/// characters from the first backspace when variant characters are used with the input
/// field", where a variant character is a base character followed by a variation selector -
/// typically an Ideographic Variation Sequence, whose selector (U+E0100 and up) is a
/// surrogate pair.
///
/// In its default editing mode the input field resolves every delete range and caret step
/// through textInfo.characterInfo[n].index and .stringLength. SetArraySizes used to record
/// the span of the last code point consumed by the variation selector block instead of the
/// span of the whole cluster, so the first Backspace either removed the wrong span or
/// indexed a characterInfo slot past characterCount and removed nothing. The span mapping
/// itself is covered by TMP_CharacterClusterTests; these tests assert the behaviour a user
/// sees.
/// </summary>
[Category("Text Parsing & Layout")]
[UnityPlatform(RuntimePlatform.OSXEditor, RuntimePlatform.WindowsEditor, RuntimePlatform.LinuxEditor)]
internal class TMP_InputFieldClusterTests : TMPTestSuiteWithEssentialResources
{
// U+E0100 VARIATION SELECTOR-17, encoded as a surrogate pair: two UTF-16 code units.
const string k_VariationSelector17 = "\U000E0100";
// U+1F600 GRINNING FACE. A plain surrogate pair, with no variation selector.
const string k_AstralCharacter = "\U0001F600";
GameObject m_CameraObject;
GameObject m_CanvasObject;
GameObject m_InputObject;
GameObject m_EventObject;
TMP_InputField m_InputField;
[SetUp]
public void SetUp()
{
m_CameraObject = new GameObject("Camera Object", typeof(Camera));
m_CanvasObject = new GameObject("Canvas Object", typeof(Canvas), typeof(GraphicRaycaster));
m_CanvasObject.GetComponent<Canvas>().renderMode = RenderMode.ScreenSpaceOverlay;
m_InputObject = new GameObject("Input Object", typeof(TMP_InputField));
m_InputObject.transform.SetParent(m_CanvasObject.transform);
m_InputObject.AddComponent<Image>();
GameObject textAreaObject = new GameObject("Text Area", typeof(TextMeshProUGUI));
textAreaObject.transform.SetParent(m_InputObject.transform);
m_InputField = m_InputObject.GetComponent<TMP_InputField>();
m_InputField.targetGraphic = m_InputObject.GetComponent<Image>();
m_InputField.textViewport = m_InputObject.GetComponent<RectTransform>();
m_InputField.textComponent = textAreaObject.GetComponent<TextMeshProUGUI>();
m_InputField.textComponent.fontSize = 18;
m_InputField.lineType = TMP_InputField.LineType.SingleLine;
// Selecting the whole text on focus would make every delete a selection delete and
// hide the single character behaviour under test.
m_InputField.onFocusSelectAll = false;
m_EventObject = new GameObject("Event Object", typeof(EventSystem), typeof(StandaloneInputModule));
// Address the EventSystem this fixture created rather than EventSystem.current,
// which resolves to the first registered one and could belong to another fixture.
m_EventObject.GetComponent<EventSystem>().SetSelectedGameObject(m_InputObject);
m_InputField.ActivateInputField();
}
[TearDown]
public void TearDown()
{
if (m_InputField != null)
{
m_InputField.DeactivateInputField();
m_InputField = null;
}
// SetUp can bail out through Assert.IsTrue before any of these exist.
if (m_EventObject != null)
{
Object.DestroyImmediate(m_EventObject);
m_EventObject = null;
}
if (m_InputObject != null)
{
Object.DestroyImmediate(m_InputObject);
m_InputObject = null;
}
if (m_CanvasObject != null)
{
Object.DestroyImmediate(m_CanvasObject);
m_CanvasObject = null;
}
if (m_CameraObject != null)
{
Object.DestroyImmediate(m_CameraObject);
m_CameraObject = null;
}
}
/// <summary>
/// Assigns the text and puts the caret at a known position in the raw string. The
/// stringPosition setter derives the caret index from characterInfo synchronously, so the
/// label has to be generated first.
/// </summary>
void SetTextAndStringPosition(string value, int stringPosition)
{
m_InputField.text = value;
m_InputField.textComponent.ForceMeshUpdate();
m_InputField.stringPosition = stringPosition;
}
void PressKey(KeyCode keyCode)
{
m_InputField.ProcessEvent(new Event { type = EventType.KeyDown, keyCode = keyCode, modifiers = EventModifiers.None });
m_InputField.ForceLabelUpdate();
m_InputField.textComponent.ForceMeshUpdate();
}
[Test]
public void Backspace_RemovesTheWholeVariationSelectorCluster_OnTheFirstPress()
{
// "a" | "b" + VS17 - four code units, two rendered characters.
string sourceText = "ab" + k_VariationSelector17;
SetTextAndStringPosition(sourceText, sourceText.Length);
PressKey(KeyCode.Backspace);
// The whole cluster goes in one press. Before the fix this press removed nothing
// (or only part of the cluster), which is the reported symptom.
Assert.AreEqual("a", m_InputField.text, "The first Backspace should remove the whole variant character.");
// And the caret has to land on the cluster boundary that is left behind, otherwise
// the following press works from a stale position.
Assert.AreEqual(1, m_InputField.stringPosition, "The caret should be left at the end of the remaining text.");
PressKey(KeyCode.Backspace);
Assert.AreEqual(string.Empty, m_InputField.text, "The second Backspace should remove the remaining character.");
}
[Test]
public void Backspace_RemovesTheOnlyVariationSelectorCluster_OnTheFirstPress()
{
// The cluster is the entire content, so the field should end up empty.
string sourceText = "a" + k_VariationSelector17;
SetTextAndStringPosition(sourceText, sourceText.Length);
PressKey(KeyCode.Backspace);
Assert.AreEqual(string.Empty, m_InputField.text, "The first Backspace should empty the field.");
}
[Test]
public void Delete_RemovesTheWholeVariationSelectorCluster_OnTheFirstPress()
{
// Caret sits before the cluster, at raw string index 1.
string sourceText = "ab" + k_VariationSelector17;
SetTextAndStringPosition(sourceText, 1);
PressKey(KeyCode.Delete);
Assert.AreEqual("a", m_InputField.text, "The first Delete should remove the whole variant character.");
}
[Test]
public void Backspace_PlainText_RemovesOneCharacter()
{
// Regression guard: nothing about single code unit characters changes.
SetTextAndStringPosition("abc", 3);
PressKey(KeyCode.Backspace);
Assert.AreEqual("ab", m_InputField.text);
}
[Test]
public void Backspace_SurrogatePairWithoutVariationSelector_RemovesBothCodeUnits()
{
// Regression guard: a plain surrogate pair was already removed as a unit and must
// still be, never leaving half a pair behind.
string sourceText = "a" + k_AstralCharacter;
SetTextAndStringPosition(sourceText, sourceText.Length);
PressKey(KeyCode.Backspace);
Assert.AreEqual("a", m_InputField.text);
}
[Test]
public void Arrows_StepOverTheWholeVariationSelectorCluster()
{
// In the default editing mode the arrows move by rendered character, so one press
// has to cross the whole cluster and land on a cluster boundary - otherwise a
// following edit would split the base character from its selector.
string sourceText = "ab" + k_VariationSelector17;
SetTextAndStringPosition(sourceText, sourceText.Length);
PressKey(KeyCode.LeftArrow);
Assert.AreEqual(1, m_InputField.stringPosition, "Arrow-left should land at the start of the cluster.");
PressKey(KeyCode.RightArrow);
Assert.AreEqual(sourceText.Length, m_InputField.stringPosition, "Arrow-right should land at the end of the cluster.");
}
[Test]
public void Arrows_InRichTextEditingMode_StepByCodePoint()
{
// With rich text editing enabled the arrows deliberately move by code point rather
// than by rendered character, so crossing a base character plus a surrogate pair
// selector takes two presses. The raw string positions come straight from
// char.IsLowSurrogate and are unaffected by the cluster mapping; the caret index is
// not - it now stays after the glyph until the raw position has fully left the
// cluster, which is what GetCaretPositionFromStringIndex reports for a position
// inside a cluster. Pinned here so the two are not allowed to drift apart again.
string sourceText = "ab" + k_VariationSelector17;
m_InputField.isRichTextEditingAllowed = true;
SetTextAndStringPosition(sourceText, sourceText.Length);
PressKey(KeyCode.LeftArrow);
Assert.AreEqual(2, m_InputField.stringPosition, "The first arrow-left should skip the surrogate pair selector.");
Assert.AreEqual(2, m_InputField.caretPosition, "The caret should stay after the cluster while the raw position is still inside it.");
PressKey(KeyCode.LeftArrow);
Assert.AreEqual(1, m_InputField.stringPosition, "The second arrow-left should step over the base character.");
Assert.AreEqual(1, m_InputField.caretPosition, "The caret should move once the raw position reaches the cluster boundary.");
}
[Test]
public void Backspace_InRichTextEditingMode_RemovesTheSelectorOnly()
{
// Documented counterpart to the default mode: rich text editing is code point
// granular by design, so Backspace removes the surrogate pair selector and leaves
// the base character. This path reads char.IsLowSurrogate rather than characterInfo,
// so it is unaffected by the cluster mapping fix - pinned so the two modes cannot
// quietly converge.
string sourceText = "ab" + k_VariationSelector17;
m_InputField.isRichTextEditingAllowed = true;
SetTextAndStringPosition(sourceText, sourceText.Length);
PressKey(KeyCode.Backspace);
Assert.AreEqual("ab", m_InputField.text);
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: ea489b4043ccf6e49b2acea3c66595a9
@@ -0,0 +1,337 @@
using UnityEngine;
using NUnit.Framework;
using System.IO;
using System.Collections.Generic;
using UnityEngine.UI;
using UnityEngine.EventSystems;
using UnityEngine.TestTools;
namespace TMPro
{
[Category("Text Parsing & Layout")]
internal class TMP_RuntimeTests
{
private TextMeshPro m_TextComponent;
// Characters: 22 Spaces: 4 Words: 5 Lines:
private const string m_TextBlock_00 = "A simple line of text.";
// Characters: 104 Spaces: 14 Words: 15 Lines:
private const string m_TextBlock_01 = "Unity 2017 introduces new features that help teams of artists and developers build experiences together.";
// Characters: 1500 Spaces: 228 Words: 241
private const string m_TextBlock_02 = "The European languages are members of the same family. Their separate existence is a myth. For science, music, sport, etc, Europe uses the same vocabulary. The languages only differ in their grammar, their pronunciation and their most common words." +
"Everyone realizes why a new common language would be desirable: one could refuse to pay expensive translators.To achieve this, it would be necessary to have uniform grammar, pronunciation and more common words.If several languages coalesce, the grammar of the resulting language is more simple and regular than that of the individual languages." +
"The new common language will be more simple and regular than the existing European languages.It will be as simple as Occidental; in fact, it will be Occidental.To an English person, it will seem like simplified English, as a skeptical Cambridge friend of mine told me what Occidental is. The European languages are members of the same family." +
"Their separate existence is a myth. For science, music, sport, etc, Europe uses the same vocabulary.The languages only differ in their grammar, their pronunciation and their most common words.Everyone realizes why a new common language would be desirable: one could refuse to pay expensive translators.To achieve this, it would be necessary to" +
"have uniform grammar, pronunciation and more common words.If several languages coalesce, the grammar of the resulting language is more simple and regular than that of the individual languages.The new common language will be";
// Characters: 2500 Spaces: 343 Words: 370
private const string m_TextBlock_03 = "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu. In enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo. " +
"Nullam dictum felis eu pede mollis pretium.Integer tincidunt.Cras dapibus.Vivamus elementum semper nisi. Aenean vulputate eleifend tellus. Aenean leo ligula, porttitor eu, consequat vitae, eleifend ac, enim.Aliquam lorem ante, dapibus in, viverra quis, feugiat a, tellus.Phasellus viverra nulla ut metus varius laoreet.Quisque rutrum. Aenean imperdiet. Etiam ultricies nisi vel augue.Curabitur ullamcorper ultricies nisi. " +
"Nam eget dui.Etiam rhoncus. Maecenas tempus, tellus eget condimentum rhoncus, sem quam semper libero, sit amet adipiscing sem neque sed ipsum.Nam quam nunc, blandit vel, luctus pulvinar, hendrerit id, lorem.Maecenas nec odio et ante tincidunt tempus.Donec vitae sapien ut libero venenatis faucibus.Nullam quis ante.Etiam sit amet orci eget eros faucibus tincidunt. Duis leo. Sed fringilla mauris sit amet nibh. " +
"Donec sodales sagittis magna. Sed consequat, leo eget bibendum sodales, augue velit cursus nunc, quis gravida magna mi a libero. Fusce vulputate eleifend sapien. Vestibulum purus quam, scelerisque ut, mollis sed, nonummy id, metus.Nullam accumsan lorem in dui.Cras ultricies mi eu turpis hendrerit fringilla.Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; In ac dui quis mi consectetuer lacinia. Nam pretium turpis et arcu. " +
"Duis arcu tortor, suscipit eget, imperdiet nec, imperdiet iaculis, ipsum. Sed aliquam ultrices mauris.Integer ante arcu, accumsan a, consectetuer eget, posuere ut, mauris.Praesent adipiscing. Phasellus ullamcorper ipsum rutrum nunc.Nunc nonummy metus.Vestibulum volutpat pretium libero. Cras id dui.Aenean ut eros et nisl sagittis vestibulum.Nullam nulla eros, ultricies sit amet, nonummy id, imperdiet feugiat, pede.Sed lectus. Donec mollis hendrerit risus. Phasellus nec sem in justo pellentesque facilisis. " +
"Etiam imperdiet imperdiet orci. Nunc nec neque.Phasellus leo dolor, tempus non, auctor et, hendrerit quis, nisi.Curabitur ligula sapien, tincidunt non, euismod vitae, posuere imperdiet, leo.Maecenas malesuada. Praesent nan. The end of this of this long block of text.";
// Characters: 3423 Spaces: 453 Words: 500
private const string m_TextBlock_04 = "Lorem ipsum dolor sit amet, consectetuer adipiscing elit.Aenean commodo ligula eget dolor.Aenean massa.Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem.Nulla consequat massa quis enim.Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu.In enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo.Nullam dictum felis eu pede mollis pretium.Integer tincidunt. Cras dapibus. Vivamus elementum semper nisi. Aenean vulputate eleifend tellus." +
"Aenean leo ligula, porttitor eu, consequat vitae, eleifend ac, enim.Aliquam lorem ante, dapibus in, viverra quis, feugiat a, tellus.Phasellus viverra nulla ut metus varius laoreet.Quisque rutrum. Aenean imperdiet. Etiam ultricies nisi vel augue.Curabitur ullamcorper ultricies nisi. Nam eget dui.Etiam rhoncus. Maecenas tempus, tellus eget condimentum rhoncus, sem quam semper libero, sit amet adipiscing sem neque sed ipsum.Nam quam nunc, blandit vel, luctus pulvinar, hendrerit id, lorem.Maecenas nec odio et ante tincidunt tempus.Donec vitae sapien ut libero venenatis faucibus.Nullam quis ante." +
"Etiam sit amet orci eget eros faucibus tincidunt. Duis leo. Sed fringilla mauris sit amet nibh. Donec sodales sagittis magna. Sed consequat, leo eget bibendum sodales, augue velit cursus nunc, quis gravida magna mi a libero. Fusce vulputate eleifend sapien. Vestibulum purus quam, scelerisque ut, mollis sed, nonummy id, metus.Nullam accumsan lorem in dui.Cras ultricies mi eu turpis hendrerit fringilla.Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; In ac dui quis mi consectetuer lacinia.Nam pretium turpis et arcu." +
"Duis arcu tortor, suscipit eget, imperdiet nec, imperdiet iaculis, ipsum. Sed aliquam ultrices mauris.Integer ante arcu, accumsan a, consectetuer eget, posuere ut, mauris.Praesent adipiscing. Phasellus ullamcorper ipsum rutrum nunc.Nunc nonummy metus.Vestibulum volutpat pretium libero. Cras id dui.Aenean ut eros et nisl sagittis vestibulum.Nullam nulla eros, ultricies sit amet, nonummy id, imperdiet feugiat, pede.Sed lectus. Donec mollis hendrerit risus. Phasellus nec sem in justo pellentesque facilisis.Etiam imperdiet imperdiet orci. Nunc nec neque." +
"Phasellus leo dolor, tempus non, auctor et, hendrerit quis, nisi.Curabitur ligula sapien, tincidunt non, euismod vitae, posuere imperdiet, leo.Maecenas malesuada. Praesent congue erat at massa.Sed cursus turpis vitae tortor.Donec posuere vulputate arcu. Phasellus accumsan cursus velit. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Sed aliquam, nisi quis porttitor congue, elit erat euismod orci, ac placerat dolor lectus quis orci.Phasellus consectetuer vestibulum elit.Aenean tellus metus, bibendum sed, posuere ac, mattis non, nunc.Vestibulum fringilla pede sit amet augue." +
"In turpis. Pellentesque posuere. Praesent turpis. Aenean posuere, tortor sed cursus feugiat, nunc augue blandit nunc, eu sollicitudin urna dolor sagittis lacus. Donec elit libero, sodales nec, volutpat a, suscipit non, turpis.Nullam sagittis. Suspendisse pulvinar, augue ac venenatis condimentum, sem libero volutpat nibh, nec pellentesque velit pede quis nunc. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Fusce id purus.Ut varius tincidunt libero.Phasellus dolor.Maecenas vestibulum mollis";
//
private const string m_TextBlock_05 = "This block of text contains <b>bold</b> and <i>italicized</i> characters.";
private const string m_TextBlock_06 = "<align=center><style=H1><#ffffff><u>Multiple<#80f0ff> Alignment</color> per text object</u></color></style></align><line-height=2em>\n" +
"</line-height> The <<#ffffa0>align</color>> tag in TextMesh<#40a0ff>Pro</color> provides the ability to control the alignment of lines and paragraphs which is essential when working with text.\n" +
"<align=left> You may want some block of text to be<#80f0ff>left aligned</color> <<#ffffa0>align=<#80f0ff>left</color></color>> which is sort of the standard.</align>\n" +
"<style=Quote><#ffffa0>\"Using <#80f0ff>Center Alignment</color> <<#ffffa0>align=<#80f0ff>center</color></color>> for a title or displaying a quote is another good example of text alignment.\"</color></style>\n" +
"<align=right><#80f0ff>Right Alignment</color> <<#ffffa0>align=<#80f0ff>right</color></color>> can be useful to create contrast between lines and paragraphs of text.\n" +
"<align=justified><#80f0ff>Justified Alignment</color> <<#ffffa0>align=<#80f0ff>justified</color></color>> results in text that is flush on both the left and right margins. Used well, justified type can look clean and classy.\n" +
"<style=Quote><align=left><#ffffa0>\"Text formatting and alignment has a huge impact on how people will read and perceive your text.\"</color>\n" +
"<size=65%><align=right> -Stephan Bouchard</style>";
private readonly string[] testStrings = new string[] { m_TextBlock_00, m_TextBlock_01, m_TextBlock_02, m_TextBlock_03, m_TextBlock_04, m_TextBlock_05, m_TextBlock_06 };
[OneTimeSetUp]
public void Setup()
{
if (Directory.Exists(Path.GetFullPath("Assets/TextMesh Pro")) || Directory.Exists(Path.GetFullPath("Packages/com.unity.textmeshpro.tests/TextMesh Pro")))
{
GameObject textObject = new GameObject("Text Object");
m_TextComponent = textObject.AddComponent<TextMeshPro>();
m_TextComponent.fontSize = 18;
}
else
{
Debug.Log("Skipping over Editor tests as TMP Essential Resources are missing from the current test project.");
Assert.Ignore();
return;
}
}
public static IEnumerable<object[]> TestCases_Parsing_TextInfo_WordWrapDisabled()
{
yield return new object[] { 0, 22, 4, 5, 1 };
yield return new object[] { 1, 104, 14, 15, 1 };
yield return new object[] { 2, 1500, 228, 241, 1 };
yield return new object[] { 3, 2500, 343, 370, 1 };
yield return new object[] { 4, 3423, 453, 500, 1 };
}
[Test, TestCaseSource("TestCases_Parsing_TextInfo_WordWrapDisabled")]
public void Parsing_TextInfo_WordWrapDisabled(int sourceTextIndex, int characterCount, int spaceCount, int wordCount, int lineCount)
{
m_TextComponent.text = testStrings[sourceTextIndex];
m_TextComponent.textWrappingMode = TextWrappingModes.NoWrap;
m_TextComponent.alignment = TextAlignmentOptions.TopLeft;
// Size the RectTransform
m_TextComponent.rectTransform.sizeDelta = new Vector2(50, 5);
// Force text generation to populate the TextInfo data structure.
m_TextComponent.ForceMeshUpdate();
Assert.AreEqual(m_TextComponent.textInfo.characterCount, characterCount);
Assert.AreEqual(m_TextComponent.textInfo.spaceCount, spaceCount);
Assert.AreEqual(m_TextComponent.textInfo.wordCount, wordCount);
Assert.AreEqual(m_TextComponent.textInfo.lineCount, lineCount);
}
public static IEnumerable<object[]> TestCases_Parsing_TextInfo_WordWrapEnabled()
{
yield return new object[] { 0, 22, 4, 5, 1 };
yield return new object[] { 1, 104, 14, 15, 1 };
yield return new object[] { 2, 1500, 228, 241, 13 };
yield return new object[] { 3, 2500, 343, 370, 21 };
yield return new object[] { 4, 3423, 453, 500, 29 };
}
[Test, TestCaseSource("TestCases_Parsing_TextInfo_WordWrapEnabled")]
public void Parsing_TextInfo_WordWrapEnabled(int sourceTextIndex, int characterCount, int spaceCount, int wordCount, int lineCount)
{
m_TextComponent.text = testStrings[sourceTextIndex];
m_TextComponent.textWrappingMode = TextWrappingModes.Normal;
m_TextComponent.alignment = TextAlignmentOptions.TopLeft;
// Size the RectTransform
m_TextComponent.rectTransform.sizeDelta = new Vector2(100, 50);
// Force text generation to populate the TextInfo data structure.
m_TextComponent.ForceMeshUpdate();
Assert.AreEqual(m_TextComponent.textInfo.characterCount, characterCount);
Assert.AreEqual(m_TextComponent.textInfo.spaceCount, spaceCount);
Assert.AreEqual(m_TextComponent.textInfo.wordCount, wordCount);
Assert.AreEqual(m_TextComponent.textInfo.lineCount, lineCount);
}
public static IEnumerable<object[]> TestCases_Parsing_TextInfo_AlignmentTopJustified()
{
yield return new object[] { 2, 1500, 228, 241, 13 };
yield return new object[] { 3, 2500, 343, 370, 20 };
yield return new object[] { 4, 3423, 453, 500, 27 };
}
[Test, TestCaseSource("TestCases_Parsing_TextInfo_AlignmentTopJustified")]
public void Parsing_TextInfo_AlignmentTopJustified(int sourceTextIndex, int characterCount, int spaceCount, int wordCount, int lineCount)
{
m_TextComponent.text = testStrings[sourceTextIndex];
m_TextComponent.textWrappingMode = TextWrappingModes.Normal;
m_TextComponent.alignment = TextAlignmentOptions.TopJustified;
// Size the RectTransform
m_TextComponent.rectTransform.sizeDelta = new Vector2(100, 50);
// Force text generation to populate the TextInfo data structure.
m_TextComponent.ForceMeshUpdate();
Assert.AreEqual(m_TextComponent.textInfo.characterCount, characterCount);
Assert.AreEqual(m_TextComponent.textInfo.spaceCount, spaceCount);
Assert.AreEqual(m_TextComponent.textInfo.wordCount, wordCount);
Assert.AreEqual(m_TextComponent.textInfo.lineCount, lineCount);
}
public static IEnumerable<object[]> TestCases_Parsing_TextInfo_RichText()
{
yield return new object[] { 5, 59, 8, 9, 1 };
yield return new object[] { 6, 768, 124, 126, 14 };
}
[Test, TestCaseSource("TestCases_Parsing_TextInfo_RichText")]
public void Parsing_TextInfo_RichText(int sourceTextIndex, int characterCount, int spaceCount, int wordCount, int lineCount)
{
m_TextComponent.text = testStrings[sourceTextIndex];
m_TextComponent.textWrappingMode = TextWrappingModes.Normal;
m_TextComponent.alignment = TextAlignmentOptions.TopLeft;
// Size the RectTransform
m_TextComponent.rectTransform.sizeDelta = new Vector2(70, 35);
// Force text generation to populate the TextInfo data structure.
m_TextComponent.ForceMeshUpdate();
Assert.AreEqual(m_TextComponent.textInfo.characterCount, characterCount);
Assert.AreEqual(m_TextComponent.textInfo.spaceCount, spaceCount);
Assert.AreEqual(m_TextComponent.textInfo.wordCount, wordCount);
Assert.AreEqual(m_TextComponent.textInfo.lineCount, lineCount);
}
public static IEnumerable<object[]> TestCases_MultiLineNewline_OnLastLine_WhenPressedEnter_Caret_ShouldNotGoto_NextLine()
{
yield return new object[] { 1, 1 };
yield return new object[] { 2, 2 };
yield return new object[] { 3, 3 };
yield return new object[] { 4, 4 };
yield return new object[] { 5, 5 };
yield return new object[] { 6, 6 };
}
[Test, TestCaseSource(nameof(TestCases_MultiLineNewline_OnLastLine_WhenPressedEnter_Caret_ShouldNotGoto_NextLine))]
public void MultiLineNewline_OnLastLine_WhenPressedEnter_Caret_ShouldNotGoto_NextLine(int lineLimit,
int expectedLineCount)
{
MultiLineNewline_LineLimit_ExpectedLineCount_Logic(lineLimit, lineLimit, 3);
Assert.AreEqual(m_TextComponent.textInfo.lineCount, expectedLineCount);
}
private void MultiLineNewline_LineLimit_ExpectedLineCount_Logic(int lineLimitValue, int lineLimitApplied,
int extraKeyDownEventCount)
{
GameObject cameraObject = new GameObject("Camera Object", typeof(Camera));
GameObject canvasObject = new GameObject("Canvas Object", typeof(Canvas), typeof(GraphicRaycaster));
canvasObject.GetComponent<Canvas>().renderMode = RenderMode.ScreenSpaceOverlay;
GameObject inputObject = new GameObject("Input Object", typeof(TMP_InputField));
inputObject.transform.parent = canvasObject.transform;
inputObject.AddComponent<Image>();
TMP_InputField m_InputField = inputObject.GetComponent<TMP_InputField>();
m_InputField.targetGraphic = inputObject.GetComponent<Image>();
m_InputField.textComponent = m_TextComponent;
m_InputField.lineType = TMP_InputField.LineType.MultiLineNewline;
m_InputField.lineLimit = lineLimitValue;
GameObject eventGameObject = new GameObject("Event Object", typeof(EventSystem), typeof(StandaloneInputModule));
Event enterKeyDownEvent = new Event { type = EventType.KeyDown, keyCode = KeyCode.KeypadEnter, modifiers = EventModifiers.None, character = '\n' };
m_InputField.text = "POTUS";
EventSystem.current.SetSelectedGameObject(inputObject);
m_InputField.ActivateInputField();
int count = lineLimitApplied + extraKeyDownEventCount;
while (count > 0)
{
m_InputField.ProcessEvent(enterKeyDownEvent);
m_InputField.ForceLabelUpdate();
count--;
}
m_InputField.textComponent.ForceMeshUpdate();
CanvasUpdateRegistry.RegisterCanvasElementForGraphicRebuild(m_InputField);
m_InputField.DeactivateInputField();
GameObject.Destroy(eventGameObject);
GameObject.Destroy(inputObject);
GameObject.Destroy(canvasObject);
GameObject.Destroy(cameraObject);
}
public static IEnumerable<object[]> TestCases_MultiLineNewLine_NegativeOrZeroLineLimit_AddsNewLine()
{
yield return new object[] { 0, 0, 1 };
yield return new object[] { 0, 0, 4 };
yield return new object[] { -1, 0, 2 };
}
[Test, TestCaseSource(nameof(TestCases_MultiLineNewLine_NegativeOrZeroLineLimit_AddsNewLine))]
public void MultiLineNewLine_NegativeOrZeroLineLimit_AddsNewLine(int lineLimitValue, int lineLimitApplied,
int extraKeyDownEventCount)
{
MultiLineNewline_LineLimit_ExpectedLineCount_Logic(lineLimitValue, lineLimitApplied, extraKeyDownEventCount);
Assert.AreEqual(m_TextComponent.textInfo.lineCount, extraKeyDownEventCount + 1);
}
[OneTimeTearDown]
public void OneTimeTearDown()
{
if (m_TextComponent != null)
{
Object.DestroyImmediate(m_TextComponent.gameObject);
m_TextComponent = null;
}
// // Remove TMP Essential Resources if they were imported in the project as a result of running tests.
// if (TMPro_EventManager.temporaryResourcesImported == true)
// {
// string testResourceFolderPath = Path.GetFullPath("Assets/TextMesh Pro");
// if (Directory.Exists(testResourceFolderPath))
// {
// Directory.Delete(testResourceFolderPath);
// File.Delete(Path.GetFullPath("Assets/TextMesh Pro.meta"));
// }
// TMPro_EventManager.temporaryResourcesImported = false;
// }
}
[Test]
public void SettingIsTextObjectScaleStatic_OnDisabledObject_DoesntProduceErrors()
{
// Reset the text component to before its OnEnable
var textObject = m_TextComponent.gameObject;
Object.DestroyImmediate(m_TextComponent);
textObject.SetActive(false);
m_TextComponent = textObject.AddComponent<TextMeshPro>();
// UUM-92041: verify that we only register if enabled
m_TextComponent.isTextObjectScaleStatic = true; // Make sure we're not starting false
m_TextComponent.isTextObjectScaleStatic = false;
Canvas.ForceUpdateCanvases();
LogAssert.NoUnexpectedReceived();
}
[Test]
public void SettingIsTextObjectScaleStatic_OnDisabledObject_IsAppliedCorrectlyAfterObjectIsEnabled()
{
m_TextComponent.text = "Test";
// Reset the text component to before its OnEnable
var textObject = m_TextComponent.gameObject;
Object.DestroyImmediate(m_TextComponent);
textObject.SetActive(false);
m_TextComponent = textObject.AddComponent<TextMeshPro>();
// Set isTextObjectScaleStatic to false while the object is disabled
m_TextComponent.isTextObjectScaleStatic = true;
m_TextComponent.isTextObjectScaleStatic = false; // Make sure we're not starting false
Canvas.ForceUpdateCanvases();
m_TextComponent.gameObject.SetActive(true);
Canvas.ForceUpdateCanvases();
var w = m_TextComponent.textInfo.meshInfo[0].uvs0[0].w;
// When isTextObjectScaleStatic is false, scale is updated in mesh uv
m_TextComponent.transform.localScale = Vector3.one * 2;
Canvas.ForceUpdateCanvases();
Assert.AreEqual(w * 2, m_TextComponent.textInfo.meshInfo[0].uvs0[0].w);
// Setting isTextObjectScaleStatic to true shouldn't change mesh
m_TextComponent.isTextObjectScaleStatic = true;
Canvas.ForceUpdateCanvases();
Assert.AreEqual(w * 2, m_TextComponent.textInfo.meshInfo[0].uvs0[0].w);
// When isTextObjectScaleStatic is true, scale is NOT updated in mesh
m_TextComponent.transform.localScale = Vector3.one;
Canvas.ForceUpdateCanvases();
Assert.AreEqual(w * 2, m_TextComponent.textInfo.meshInfo[0].uvs0[0].w);
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9de24983a2c6cbe4f925c3e98a79b804
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,14 @@
{
"name": "Unity.TextMeshPro.Tests",
"references": [
"Unity.TextMeshPro"
],
"optionalUnityReferences": [
"TestAssemblies"
],
"defineConstraints": [
"UNITY_INCLUDE_TESTS"
],
"includePlatforms": [],
"excludePlatforms": []
}
@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: bb05cab7d802aa5468f8f2f86840d984
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 53caebc5909234b4ebe1fcaf3dc913a5
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 239dd6edc8e5cd14585c03e09e86a747
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,151 @@
using System.Collections;
using System.IO;
using NUnit.Framework;
using UnityEditor;
using UnityEngine.EventSystems;
using UnityEngine.TestTools;
using UnityEngine.UI;
using UnityEngine;
internal class ButtonTests : IPrebuildSetup
{
GameObject m_PrefabRoot;
const string kPrefabPath = "Assets/Resources/ButtonPrefab.prefab";
public void Setup()
{
#if UNITY_EDITOR
var rootGO = new GameObject("rootGo");
var canvasGO = new GameObject("Canvas", typeof(Canvas));
canvasGO.transform.SetParent(rootGO.transform);
var canvas = canvasGO.GetComponent<Canvas>();
canvas.referencePixelsPerUnit = 100;
GameObject eventSystemGO = new GameObject("EventSystem", typeof(EventSystem));
eventSystemGO.transform.SetParent(rootGO.transform);
GameObject TestButtonGO = new GameObject("TestButton", typeof(RectTransform), typeof(TestButton));
TestButtonGO.transform.SetParent(canvasGO.transform);
if (!Directory.Exists("Assets/Resources/"))
Directory.CreateDirectory("Assets/Resources/");
PrefabUtility.SaveAsPrefabAsset(rootGO, kPrefabPath);
GameObject.DestroyImmediate(rootGO);
#endif
}
[SetUp]
public void TestSetup()
{
m_PrefabRoot = Object.Instantiate(Resources.Load("ButtonPrefab")) as GameObject;
}
[TearDown]
public void TearDown()
{
GameObject.DestroyImmediate(m_PrefabRoot);
}
[OneTimeTearDown]
public void OneTimeTearDown()
{
#if UNITY_EDITOR
AssetDatabase.DeleteAsset(kPrefabPath);
#endif
}
[Test]
public void PressShouldCallClickHandler()
{
Button button = m_PrefabRoot.GetComponentInChildren<Button>();
bool called = false;
button.onClick.AddListener(() => { called = true; });
button.OnPointerClick(new PointerEventData(m_PrefabRoot.GetComponentInChildren<EventSystem>()) { button = PointerEventData.InputButton.Left });
Assert.True(called);
}
[Test]
public void PressInactiveShouldNotCallClickHandler()
{
Button button = m_PrefabRoot.GetComponentInChildren<Button>();
bool called = false;
button.enabled = false;
button.onClick.AddListener(() => { called = true; });
button.OnPointerClick(new PointerEventData(m_PrefabRoot.GetComponentInChildren<EventSystem>()) { button = PointerEventData.InputButton.Left });
Assert.False(called);
}
[Test]
public void PressNotInteractableShouldNotCallClickHandler()
{
Button button = m_PrefabRoot.GetComponentInChildren<Button>();
bool called = false;
button.interactable = false;
button.onClick.AddListener(() => { called = true; });
button.OnPointerClick(new PointerEventData(m_PrefabRoot.GetComponentInChildren<EventSystem>()) { button = PointerEventData.InputButton.Left });
Assert.False(called);
}
[Test]
public void SelectShouldHoldThePreviousStateAfterDisablingAndEnabling()
{
TestButton button = m_PrefabRoot.GetComponentInChildren<TestButton>();
button.onClick.AddListener(() => {
button.Select();
button.enabled = false;
});
button.OnPointerClick(new PointerEventData(m_PrefabRoot.GetComponentInChildren<EventSystem>()) { button = PointerEventData.InputButton.Left });
Assert.False(button.enabled, "Expected button to not be enabled");
button.enabled = true;
Assert.True(button.isStateSelected, "Expected selected state to be true");
}
[Test]
public void SubmitShouldCallClickHandler()
{
Button button = m_PrefabRoot.GetComponentInChildren<Button>();
bool called = false;
button.onClick.AddListener(() => { called = true; });
button.OnSubmit(null);
Assert.True(called);
}
[Test]
public void SubmitInactiveShouldNotCallClickHandler()
{
Button button = m_PrefabRoot.GetComponentInChildren<Button>();
bool called = false;
button.enabled = false;
button.onClick.AddListener(() => { called = true; });
button.OnSubmit(new PointerEventData(m_PrefabRoot.GetComponentInChildren<EventSystem>()) { button = PointerEventData.InputButton.Left });
Assert.False(called);
}
[Test]
public void SubmitNotInteractableShouldNotCallClickHandler()
{
Button button = m_PrefabRoot.GetComponentInChildren<Button>();
bool called = false;
button.interactable = false;
button.onClick.AddListener(() => { called = true; });
button.OnSubmit(new PointerEventData(m_PrefabRoot.GetComponentInChildren<EventSystem>()) { button = PointerEventData.InputButton.Left });
Assert.False(called);
}
[UnityTest]
public IEnumerator SubmitShouldTransitionToPressedStateAndBackToNormal()
{
TestButton button = m_PrefabRoot.GetComponentInChildren<TestButton>();
Assert.True(button.IsTransitionToNormal(0));
button.OnSubmit(null);
Assert.True(button.isStateNormal);
Assert.True(button.IsTransitionToPressed(1));
yield return new WaitWhile(() => button.StateTransitionCount == 2);
// 3rd transition back to normal should have started
Assert.True(button.IsTransitionToNormal(2));
yield return null;
Assert.True(button.isStateNormal);
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1ef2923b9c5521948a04299da53ae750
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,31 @@
using System.Collections.Generic;
using UnityEngine.UI;
class TestButton : Button
{
public bool isStateNormal { get { return currentSelectionState == SelectionState.Normal; } }
public bool isStateHighlighted { get { return currentSelectionState == SelectionState.Highlighted; } }
public bool isStatePressed { get { return currentSelectionState == SelectionState.Pressed; } }
public bool isStateDisabled { get { return currentSelectionState == SelectionState.Disabled; } }
public bool isStateSelected { get { return currentSelectionState == SelectionState.Selected; } }
private bool IsTransitionTo(int index, SelectionState selectionState)
{
return index < m_StateTransitions.Count && m_StateTransitions[index] == selectionState;
}
public bool IsTransitionToNormal(int index) { return IsTransitionTo(index, SelectionState.Normal); }
public bool IsTransitionToHighlighted(int index) { return IsTransitionTo(index, SelectionState.Highlighted); }
public bool IsTransitionToPressed(int index) { return IsTransitionTo(index, SelectionState.Pressed); }
public bool IsTransitionToDisabled(int index) { return IsTransitionTo(index, SelectionState.Disabled); }
private readonly List<SelectionState> m_StateTransitions = new List<SelectionState>();
public int StateTransitionCount { get { return m_StateTransitions.Count; } }
protected override void DoStateTransition(SelectionState state, bool instant)
{
m_StateTransitions.Add(state);
base.DoStateTransition(state, instant);
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4f5ed95515938d14189b094f8654d0bd
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 9a3557da07c729b4eb774b8e30e157a4
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,9 @@
using UnityEngine;
internal class BridgeScriptForRetainingObjects : MonoBehaviour
{
public const string bridgeObjectName = "BridgeGameObject";
public GameObject canvasGO;
public GameObject nestedCanvasGO;
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1a26e19d51cbfac42a02631ad1f9e39e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,203 @@
using UnityEngine;
using UnityEngine.TestTools;
using NUnit.Framework;
using System.Collections;
using UnityEngine.UI;
[TestFixture]
internal class CanvasGroupTests
{
GameObject m_CanvasObject;
CanvasGroup m_CanvasGroup;
CanvasRenderer m_ChildCanvasRenderer;
CanvasGroup m_ChildCanvasGroup;
CanvasRenderer m_GrandChildCanvasRenderer;
CanvasGroup m_GrandChildCanvasGroup;
GameObject m_CanvasTwoObject;
CanvasGroup m_CanvasTwoGroup;
CanvasRenderer m_ChildCanvasTwoRenderer;
const float m_CanvasAlpha = 0.25f;
const float m_ChildAlpha = 0.5f;
const float m_GrandChildAlpha = 0.8f;
[SetUp]
public void TestSetup()
{
m_CanvasObject = new GameObject("Canvas", typeof(Canvas));
m_CanvasGroup = m_CanvasObject.AddComponent<CanvasGroup>();
m_CanvasGroup.alpha = m_CanvasAlpha;
var childObject = new GameObject("Child Object", typeof(Image));
childObject.transform.SetParent(m_CanvasObject.transform);
m_ChildCanvasGroup = childObject.AddComponent<CanvasGroup>();
m_ChildCanvasGroup.alpha = m_ChildAlpha;
m_ChildCanvasRenderer = childObject.GetComponent<CanvasRenderer>();
var grandChildObject = new GameObject("Grand Child Object", typeof(Image));
grandChildObject.transform.SetParent(childObject.transform);
m_GrandChildCanvasGroup = grandChildObject.AddComponent<CanvasGroup>();
m_GrandChildCanvasGroup.alpha = m_GrandChildAlpha;
m_GrandChildCanvasRenderer = grandChildObject.GetComponent<CanvasRenderer>();
m_CanvasTwoObject = new GameObject("CanvasTwo", typeof(Canvas));
m_CanvasTwoObject.transform.SetParent(m_CanvasObject.transform);
m_CanvasTwoGroup = m_CanvasTwoObject.AddComponent<CanvasGroup>();
m_CanvasTwoGroup.alpha = m_CanvasAlpha;
var childTwoObject = new GameObject("Child Two Object", typeof(Image));
childTwoObject.transform.SetParent(m_CanvasTwoObject.transform);
m_ChildCanvasTwoRenderer = childTwoObject.GetComponent<CanvasRenderer>();
}
[TearDown]
public void TearDown()
{
GameObject.DestroyImmediate(m_CanvasObject);
}
private void SetUpCanvasGroupState()
{
m_CanvasGroup.enabled = false;
m_CanvasGroup.ignoreParentGroups = false;
m_ChildCanvasGroup.enabled = false;
m_ChildCanvasGroup.ignoreParentGroups = false;
m_GrandChildCanvasGroup.enabled = false;
m_GrandChildCanvasGroup.ignoreParentGroups = false;
m_CanvasTwoGroup.enabled = false;
m_CanvasTwoGroup.ignoreParentGroups = false;
}
[Test]
public void EnabledCanvasGroupEffectSelfAndChildrenAlpha()
{
// Set up the states of the canvas groups for the tests.
SetUpCanvasGroupState();
// With no enabled CanvasGroup the Alphas should be 1
Assert.AreEqual(1.0f, m_ChildCanvasRenderer.GetInheritedAlpha());
Assert.AreEqual(1.0f, m_GrandChildCanvasRenderer.GetInheritedAlpha());
// Enable the child CanvasGroup. It and its children should now have the same alpha value.
m_ChildCanvasGroup.enabled = true;
Assert.AreEqual(m_ChildAlpha, m_ChildCanvasRenderer.GetInheritedAlpha());
Assert.AreEqual(m_ChildAlpha, m_GrandChildCanvasRenderer.GetInheritedAlpha());
}
[Test]
public void EnabledCanvasGroupOnACanvasEffectAllChildrenAlpha()
{
// Set up the states of the canvas groups for the tests.
SetUpCanvasGroupState();
// With no enabled CanvasGroup the Alphas should be 1
Assert.AreEqual(1.0f, m_ChildCanvasRenderer.GetInheritedAlpha());
Assert.AreEqual(1.0f, m_GrandChildCanvasRenderer.GetInheritedAlpha());
// Children under a different nest canvas should also obey the alpha
Assert.AreEqual(1.0f, m_ChildCanvasTwoRenderer.GetInheritedAlpha());
// Enable the Canvas CanvasGroup. All of the Canvas children should now have the same alpha value.
m_CanvasGroup.enabled = true;
Assert.AreEqual(m_CanvasAlpha, m_ChildCanvasRenderer.GetInheritedAlpha());
Assert.AreEqual(m_CanvasAlpha, m_GrandChildCanvasRenderer.GetInheritedAlpha());
// Children under a different nest canvas should also obey the alpha
Assert.AreEqual(m_CanvasAlpha, m_ChildCanvasTwoRenderer.GetInheritedAlpha());
}
[Test]
public void EnabledCanvasGroupOnLeafChildEffectOnlyThatChild()
{
// Set up the states of the canvas groups for the tests.
SetUpCanvasGroupState();
// With no enabled CanvasGroup the Alphas should be 1
Assert.AreEqual(1.0f, m_ChildCanvasRenderer.GetInheritedAlpha());
Assert.AreEqual(1.0f, m_GrandChildCanvasRenderer.GetInheritedAlpha());
// Enable the Leaf child CanvasGroup. Only it should have a modified alpha
m_GrandChildCanvasGroup.enabled = true;
Assert.AreEqual(1.0f, m_ChildCanvasRenderer.GetInheritedAlpha());
Assert.AreEqual(m_GrandChildAlpha, m_GrandChildCanvasRenderer.GetInheritedAlpha());
}
[Test]
public void EnabledCanvasGroupOnCanvasAndChildMultipleAlphaValuesCorrectly()
{
// Set up the states of the canvas groups for the tests.
SetUpCanvasGroupState();
// With no enabled CanvasGroup the Alphas should be 1
Assert.AreEqual(1.0f, m_ChildCanvasRenderer.GetInheritedAlpha());
Assert.AreEqual(1.0f, m_GrandChildCanvasRenderer.GetInheritedAlpha());
// Enable the Canvas CanvasGroup. All of the Canvas children should now have the same alpha value.
m_CanvasGroup.enabled = true;
m_ChildCanvasGroup.enabled = true;
Assert.AreEqual(m_CanvasAlpha * m_ChildAlpha, m_ChildCanvasRenderer.GetInheritedAlpha());
Assert.AreEqual(m_CanvasAlpha * m_ChildAlpha, m_GrandChildCanvasRenderer.GetInheritedAlpha());
}
[Test]
public void EnabledCanvasGroupOnCanvasAndChildWithChildIgnoringParentGroupMultipleAlphaValuesCorrectly()
{
// Set up the states of the canvas groups for the tests.
SetUpCanvasGroupState();
// With no enabled CanvasGroup the Alphas should be 1
Assert.AreEqual(1.0f, m_ChildCanvasRenderer.GetInheritedAlpha());
Assert.AreEqual(1.0f, m_GrandChildCanvasRenderer.GetInheritedAlpha());
// Enable the Canvas CanvasGroup. All of the Canvas children should now have the same alpha value.
m_CanvasGroup.enabled = true;
m_ChildCanvasGroup.enabled = true;
m_ChildCanvasGroup.ignoreParentGroups = true;
Assert.AreEqual(m_ChildAlpha, m_ChildCanvasRenderer.GetInheritedAlpha());
Assert.AreEqual(m_ChildAlpha, m_GrandChildCanvasRenderer.GetInheritedAlpha());
}
[Test]
public void EnabledCanvasGroupOnCanvasAndChildrenWithAllChildrenIgnoringParentGroupMultipleAlphaValuesCorrectly()
{
// Set up the states of the canvas groups for the tests.
SetUpCanvasGroupState();
// With no enabled CanvasGroup the Alphas should be 1
Assert.AreEqual(1.0f, m_ChildCanvasRenderer.GetInheritedAlpha());
Assert.AreEqual(1.0f, m_GrandChildCanvasRenderer.GetInheritedAlpha());
// Enable the Canvas CanvasGroup. All of the Canvas children should now have the same alpha value.
m_CanvasGroup.enabled = true;
m_ChildCanvasGroup.enabled = true;
m_GrandChildCanvasGroup.enabled = true;
m_ChildCanvasGroup.ignoreParentGroups = true;
m_GrandChildCanvasGroup.ignoreParentGroups = true;
Assert.AreEqual(m_ChildAlpha, m_ChildCanvasRenderer.GetInheritedAlpha());
Assert.AreEqual(m_GrandChildAlpha, m_GrandChildCanvasRenderer.GetInheritedAlpha());
}
[Test]
public void EnabledCanvasGroupOnNestedCanvasIgnoringParentGroupMultipleAlphaValuesCorrectly()
{
// Set up the states of the canvas groups for the tests.
SetUpCanvasGroupState();
// With no enabled CanvasGroup the Alphas should be 1
Assert.AreEqual(1.0f, m_ChildCanvasTwoRenderer.GetInheritedAlpha());
// Enable the Canvas CanvasGroup. All of the Canvas children should now have the same alpha value.
m_CanvasGroup.enabled = true;
m_CanvasTwoGroup.enabled = true;
m_CanvasTwoGroup.ignoreParentGroups = true;
Assert.AreEqual(m_CanvasAlpha, m_ChildCanvasTwoRenderer.GetInheritedAlpha());
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a14a59f2a5c757e469c3e4e17b798c2e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,47 @@
using System;
using System.Collections;
using UnityEngine;
using UnityEngine.TestTools;
using NUnit.Framework;
using UnityEngine.SceneManagement;
using UnityEditor;
[TestFixture]
internal class CanvasResizeCorrectlyForRenderTexture
{
Canvas m_Canvas;
Camera m_Camera;
RenderTexture m_RenderTexture;
[Test]
public void CanvasResizeCorrectly()
{
var cameraGameObject = new GameObject("PreviewCamera", typeof(Camera));
var canvasGameObject = new GameObject("Canvas", typeof(Canvas));
m_Camera = cameraGameObject.GetComponent<Camera>();
m_Canvas = canvasGameObject.GetComponent<Canvas>();
m_Canvas.renderMode = RenderMode.ScreenSpaceCamera;
m_Canvas.worldCamera = m_Camera;
var rectTransform = canvasGameObject.transform as RectTransform;
m_Canvas.updateRectTransformForStandalone = StandaloneRenderResize.Disabled;
m_RenderTexture = new RenderTexture(100, 100, 0);
m_Camera.targetTexture = m_RenderTexture;
m_Camera.Render();
Assert.AreNotEqual(new Vector2(100, 100), rectTransform.sizeDelta);
m_Canvas.updateRectTransformForStandalone = StandaloneRenderResize.Enabled;
m_Camera.Render();
Assert.AreEqual(new Vector2(100, 100), rectTransform.sizeDelta);
}
[TearDown]
public void TearDown()
{
GameObject.DestroyImmediate(m_Canvas.gameObject);
GameObject.DestroyImmediate(m_Camera.gameObject);
UnityEngine.Object.DestroyImmediate(m_RenderTexture);
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d4dbe29df1b294c4f8f0a2fb529d19fd
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,54 @@
using UnityEngine;
using UnityEngine.TestTools;
using NUnit.Framework;
using System.Collections;
using UnityEngine.UI;
[TestFixture]
[Category("RegressionTest")]
[Description("CoveredBugID = 734299")]
internal class CanvasScalerWithChildTextObjectDoesNotCrash
{
GameObject m_CanvasObject;
[SetUp]
public void TestSetup()
{
#if UNITY_EDITOR
UnityEditor.EditorApplication.ExecuteMenuItem("Window/General/Game");
#endif
}
[UnityTest]
public IEnumerator CanvasScalerWithChildTextObjectWithTextFontDoesNotCrash()
{
//This adds a Canvas component as well
m_CanvasObject = new GameObject("Canvas");
var canvasScaler = m_CanvasObject.AddComponent<CanvasScaler>();
m_CanvasObject.GetComponent<Canvas>().renderMode = RenderMode.ScreenSpaceCamera;
//the crash only reproduces if the text component is a child of the game object
//that has the CanvasScaler component and if it has an actual font and text set
var textObject = new GameObject("Text").AddComponent<UnityEngine.UI.Text>();
textObject.font = Font.CreateDynamicFontFromOSFont("Arial", 14);
textObject.text = "New Text";
textObject.transform.SetParent(m_CanvasObject.transform);
canvasScaler.uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize;
canvasScaler.referenceResolution = new Vector2(1080, 1020);
//The crash happens when setting the referenceResolution to a small value
canvasScaler.referenceResolution = new Vector2(9, 9);
//We need to wait a few milliseconds for the crash to happen, otherwise Debug.Log will
//get executed and the test will pass
yield return new WaitForSeconds(0.1f);
Assert.That(true);
}
[TearDown]
public void TearDown()
{
GameObject.DestroyImmediate(m_CanvasObject);
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 13d161b14bb3ab74e8a9634e26fb7a5e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,64 @@
using System;
using System.Collections;
using UnityEngine;
using UnityEngine.TestTools;
using NUnit.Framework;
using UnityEngine.SceneManagement;
using UnityEditor;
[TestFixture]
internal class CanvasSizeCorrectInAwakeAndStart : IPrebuildSetup
{
const string k_SceneName = "CanvasSizeCorrectInAwakeAndStartScene";
GameObject m_CanvasGameObject;
Scene m_InitScene;
public void Setup()
{
#if UNITY_EDITOR
Action codeToExecute = delegate()
{
var canvasGO = new GameObject("CanvasToAddImage", typeof(Canvas));
var imageGO = new GameObject("ImageOnCanvas", typeof(UnityEngine.UI.Image));
imageGO.transform.localPosition = Vector3.one;
imageGO.transform.SetParent(canvasGO.transform);
imageGO.AddComponent<CanvasSizeCorrectInAwakeAndStartScript>();
canvasGO.GetComponent<Canvas>().renderMode = RenderMode.ScreenSpaceOverlay;
imageGO.SetActive(false);
};
CreateSceneUtility.CreateScene(k_SceneName, codeToExecute);
#endif
}
[SetUp]
public void TestSetup()
{
m_InitScene = SceneManager.GetActiveScene();
}
[UnityTest]
public IEnumerator CanvasSizeIsCorrectInAwakeAndStart()
{
AsyncOperation operation = SceneManager.LoadSceneAsync(k_SceneName, LoadSceneMode.Additive);
yield return operation;
SceneManager.SetActiveScene(SceneManager.GetSceneByName(k_SceneName));
m_CanvasGameObject = GameObject.Find("CanvasToAddImage");
var imageGO = m_CanvasGameObject.transform.Find("ImageOnCanvas");
imageGO.gameObject.SetActive(true);
var component = imageGO.GetComponent<CanvasSizeCorrectInAwakeAndStartScript>();
yield return new WaitUntil(() => component.isAwakeCalled && component.isStartCalled);
}
[TearDown]
public void TearDown()
{
GameObject.DestroyImmediate(m_CanvasGameObject);
SceneManager.SetActiveScene(m_InitScene);
SceneManager.UnloadSceneAsync(k_SceneName);
#if UNITY_EDITOR
AssetDatabase.DeleteAsset("Assets/" + k_SceneName + ".unity");
#endif
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ec35c13a8280a8d4e817bc4afd8a95de
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,21 @@
using NUnit.Framework;
using UnityEngine;
using UnityEngine.TestTools.Utils;
internal class CanvasSizeCorrectInAwakeAndStartScript : MonoBehaviour
{
public bool isStartCalled { get; private set; }
public bool isAwakeCalled { get; private set; }
protected void Awake()
{
Assert.That(transform.position, Is.Not.EqualTo(Vector3.zero).Using(new Vector3EqualityComparer(0.0f)));
isAwakeCalled = true;
}
protected void Start()
{
Assert.That(transform.position, Is.Not.EqualTo(Vector3.zero).Using(new Vector3EqualityComparer(0.0f)));
isStartCalled = true;
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: df1ba932d4ce4534e97a0f10c85cd3c9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,106 @@
using UnityEngine;
using UnityEngine.TestTools;
using NUnit.Framework;
using System.Collections;
using UnityEngine.Rendering;
using UnityEngine.TestTools.Utils;
[TestFixture]
internal class CheckMeshColorsAndColors32Match
{
GameObject m_CanvasGO;
GameObject m_ColorMeshGO;
GameObject m_Color32MeshGO;
GameObject m_CameraGO;
Texture2D m_ScreenTexture;
RenderTexture m_ScreenRenderTexture;
Camera m_ScreenCamera;
[SetUp]
public void TestSetup()
{
// Create Camera
m_ScreenRenderTexture = new RenderTexture(Screen.width, Screen.height, 16, RenderTextureFormat.ARGB32);
m_ScreenRenderTexture.Create();
m_CameraGO = new GameObject("Camera");
m_ScreenCamera = m_CameraGO.AddComponent<Camera>();
m_ScreenCamera.orthographic = true;
m_ScreenCamera.orthographicSize = 1;
m_ScreenCamera.targetTexture = m_ScreenRenderTexture;
// Create Canvas
m_CanvasGO = new GameObject("Canvas");
Canvas canvas = m_CanvasGO.AddComponent<Canvas>();
canvas.renderMode = RenderMode.ScreenSpaceCamera;
canvas.worldCamera = m_ScreenCamera;
// Create Color UI GameObject
m_ColorMeshGO = new GameObject("ColorMesh");
CanvasRenderer colorMeshCanvasRenderer = m_ColorMeshGO.AddComponent<CanvasRenderer>();
RectTransform colorMeshRectTransform = m_ColorMeshGO.AddComponent<RectTransform>();
colorMeshRectTransform.pivot = colorMeshRectTransform.anchorMin = colorMeshRectTransform.anchorMax = Vector2.zero;
m_ColorMeshGO.transform.SetParent(m_CanvasGO.transform);
m_ColorMeshGO.transform.localPosition = Vector3.zero;
// Create Color32 UI GameObject
m_Color32MeshGO = new GameObject("Color32Mesh");
CanvasRenderer color32MeshCanvasRenderer = m_Color32MeshGO.AddComponent<CanvasRenderer>();
RectTransform color32MeshRectTransform = m_Color32MeshGO.AddComponent<RectTransform>();
color32MeshRectTransform.pivot = color32MeshRectTransform.anchorMin = color32MeshRectTransform.anchorMax = Vector2.zero;
m_Color32MeshGO.transform.SetParent(m_CanvasGO.transform);
m_Color32MeshGO.transform.localPosition = Vector3.zero;
Material material = new Material(Shader.Find("UI/Default"));
// Setup Color mesh and add it to Color CanvasRenderer
Mesh meshColor = new Mesh();
meshColor.vertices = new Vector3[3] { new Vector3(0, 0, 0), new Vector3(0, -3, 0), new Vector3(-3, 0, 0) };
meshColor.triangles = new int[3] { 0, 1, 2 };
meshColor.normals = new Vector3[3] { Vector3.zero, Vector3.zero, Vector3.zero };
meshColor.colors = new Color[3] { Color.white, Color.white, Color.white };
meshColor.uv = new Vector2[3] { new Vector2(0, 0), new Vector2(0, 1), new Vector2(1, 0) };
colorMeshCanvasRenderer.SetMesh(meshColor);
colorMeshCanvasRenderer.SetMaterial(material, null);
// Setup Color32 mesh and add it to Color32 CanvasRenderer
Mesh meshColor32 = new Mesh();
meshColor32.vertices = new Vector3[3] { new Vector3(0, 0, 0), new Vector3(0, 3, 0), new Vector3(3, 0, 0) };
meshColor32.triangles = new int[3] { 0, 1, 2 };
meshColor32.normals = new Vector3[3] { Vector3.zero, Vector3.zero, Vector3.zero };
meshColor32.colors32 = new Color32[3] { Color.white, Color.white, Color.white };
meshColor32.uv = new Vector2[3] { new Vector2(0, 0), new Vector2(0, 1), new Vector2(1, 0) };
color32MeshCanvasRenderer.SetMesh(meshColor32);
color32MeshCanvasRenderer.SetMaterial(material, null);
}
[TearDown]
public void TearDown()
{
GameObject.DestroyImmediate(m_CanvasGO);
m_ScreenCamera.targetTexture = null;
GameObject.DestroyImmediate(m_ScreenRenderTexture);
GameObject.DestroyImmediate(m_CameraGO);
}
[UnityTest]
public IEnumerator CheckMeshColorsAndColors32Matches()
{
Assert.That(m_ScreenRenderTexture.IsCreated(), "RenderTexture is not created");
Texture2D screenTexture = new Texture2D(m_ScreenRenderTexture.width, m_ScreenRenderTexture.height);
RenderTexture.active = m_ScreenRenderTexture;
screenTexture.ReadPixels(new Rect(0, 0, m_ScreenRenderTexture.width, m_ScreenRenderTexture.height), 0, 0);
screenTexture.Apply();
yield return null;
Color screenPixelColorForMeshColor = screenTexture.GetPixel(Screen.width / 2 + 100, Screen.height / 2 + 100);
Color screenPixelColorForMesh32Color = screenTexture.GetPixel(100, 100);
Color screenPixelOutsideMesh = screenTexture.GetPixel(Screen.width / 2 + 100, 100);
Assert.That(screenPixelColorForMesh32Color, Is.EqualTo(screenPixelColorForMeshColor).Using(new ColorEqualityComparer(0.0f)), "UI Mesh with Colors does not match UI Mesh with Colors32");
Assert.That(screenPixelOutsideMesh, Is.Not.EqualTo(screenPixelColorForMeshColor).Using(new ColorEqualityComparer(0.0f)), "Empty space should not be matching the color of the UI Mesh");
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9390296e78291b543b2f4a9761ef8139
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,74 @@
using UnityEngine;
using UnityEngine.TestTools;
using NUnit.Framework;
using System.Collections;
using UnityEngine.UI;
[TestFixture]
[UnityPlatform(include = new RuntimePlatform[] { RuntimePlatform.OSXEditor, RuntimePlatform.LinuxEditor, RuntimePlatform.WindowsEditor })]
[Category("RegressionTest")]
[Description("CoveredBugID = 904415")]
internal class CoroutineWorksIfUIObjectIsAttached
{
GameObject m_CanvasMaster;
GameObject m_ImageObject;
[SetUp]
public void TestSetup()
{
m_CanvasMaster = new GameObject("Canvas", typeof(Canvas), typeof(CanvasScaler), typeof(GraphicRaycaster));
m_ImageObject = new GameObject("Image", typeof(Image));
m_ImageObject.SetActive(false);
}
[UnityTest]
public IEnumerator CoroutineWorksOnAttachingUIObject()
{
// Generating Basic scene
m_CanvasMaster.AddComponent<CoroutineObject>();
yield return null;
m_ImageObject.transform.SetParent(m_CanvasMaster.transform);
m_ImageObject.AddComponent<BugObject>();
m_ImageObject.SetActive(true);
yield return null;
yield return null;
yield return null;
Assert.That(m_CanvasMaster.GetComponent<CoroutineObject>().coroutineCount, Is.GreaterThan(1), "The Coroutine wasn't supposed to stop but continue to run, something made it stopped");
}
[TearDown]
public void TearDown()
{
GameObject.DestroyImmediate(m_CanvasMaster);
GameObject.DestroyImmediate(m_ImageObject);
}
}
internal class BugObject : MonoBehaviour
{
void Awake()
{
GameObject newObject = new GameObject("NewGameObjectThatTriggersBug");
newObject.transform.SetParent(transform);
newObject.AddComponent<Text>();
}
}
internal class CoroutineObject : MonoBehaviour
{
public int coroutineCount { get; private set; }
public IEnumerator Start()
{
// This coroutine should not stop and continue adding to the timer
while (true)
{
coroutineCount++;
yield return null;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8573c56c34e616248a3881b2c56280ef
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,25 @@
using System;
using System.Linq;
using UnityEngine;
using UnityEngine.SceneManagement;
class CreateSceneUtility
{
public static void CreateScene(string sceneName, Action delegateToExecute)
{
#if UNITY_EDITOR
string scenePath = "Assets/" + sceneName + ".unity";
var initScene = SceneManager.GetActiveScene();
var list = UnityEditor.EditorBuildSettings.scenes.ToList();
var newScene = UnityEditor.SceneManagement.EditorSceneManager.NewScene(UnityEditor.SceneManagement.NewSceneSetup.DefaultGameObjects, UnityEditor.SceneManagement.NewSceneMode.Additive);
GameObject.DestroyImmediate(Camera.main.GetComponent<AudioListener>());
delegateToExecute();
UnityEditor.SceneManagement.EditorSceneManager.SaveScene(newScene, scenePath);
UnityEditor.SceneManagement.EditorSceneManager.UnloadSceneAsync(newScene);
list.Add(new UnityEditor.EditorBuildSettingsScene(scenePath, true));
UnityEditor.EditorBuildSettings.scenes = list.ToArray();
SceneManager.SetActiveScene(initScene);
#endif
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: db339ef553721e94999125c0b9f909dc
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,102 @@
using System.IO;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.TestTools;
using NUnit.Framework;
using System.Collections;
using UnityEditor;
internal class NestedCanvas : IPrebuildSetup
{
Object m_GO1;
Object m_GO2;
const string kPrefabPath = "Assets/Resources/NestedCanvasPrefab.prefab";
public void Setup()
{
#if UNITY_EDITOR
var rootGO = new GameObject("RootGO");
var rootCanvasGO = new GameObject("Canvas", typeof(Canvas), typeof(CanvasGroup));
rootCanvasGO.transform.SetParent(rootGO.transform);
var nestedCanvas = new GameObject("Nested Canvas", typeof(Canvas), typeof(Image));
nestedCanvas.transform.SetParent(rootCanvasGO.transform);
var nestedCanvasCamera = new GameObject("Nested Canvas Camera", typeof(Camera));
nestedCanvasCamera.transform.SetParent(rootCanvasGO.transform);
var rootCanvas = rootCanvasGO.GetComponent<Canvas>();
rootCanvas.renderMode = RenderMode.WorldSpace;
rootCanvas.worldCamera = nestedCanvasCamera.GetComponent<Camera>();
if (!Directory.Exists("Assets/Resources/"))
Directory.CreateDirectory("Assets/Resources/");
UnityEditor.PrefabUtility.SaveAsPrefabAsset(rootGO, kPrefabPath);
GameObject.DestroyImmediate(rootGO);
#endif
}
[UnityTest]
[Description("[UI] Button does not interact after nested canvas is used(case 892913)")]
public IEnumerator WorldCanvas_CanFindCameraAfterDisablingAndEnablingRootCanvas()
{
m_GO1 = Object.Instantiate(Resources.Load("NestedCanvasPrefab"));
yield return null;
var nestedCanvasGo = GameObject.Find("Nested Canvas");
var nestedCanvas = nestedCanvasGo.GetComponent<Canvas>();
Assert.IsNotNull(nestedCanvas.worldCamera, "Expected the nested canvas worldCamera to NOT be null after loading the scene.");
nestedCanvasGo.transform.parent.gameObject.SetActive(false);
nestedCanvasGo.transform.parent.gameObject.SetActive(true);
Assert.IsNotNull(nestedCanvas.worldCamera, "Expected the nested canvas worldCamera to NOT be null after the parent canvas has been re-enabled.");
}
[UnityTest]
public IEnumerator WorldCanvas_CanFindTheSameCameraAfterDisablingAndEnablingRootCanvas()
{
m_GO2 = Object.Instantiate(Resources.Load("NestedCanvasPrefab"));
yield return null;
var nestedCanvasGo = GameObject.Find("Nested Canvas");
var nestedCanvas = nestedCanvasGo.GetComponent<Canvas>();
var worldCamera = nestedCanvas.worldCamera;
nestedCanvasGo.transform.parent.gameObject.SetActive(false);
nestedCanvasGo.transform.parent.gameObject.SetActive(true);
Assert.AreEqual(worldCamera, nestedCanvas.worldCamera, "Expected the same world camera to be returned after the root canvas was disabled and then re-enabled.");
}
[UnityTest]
public IEnumerator NestedCanvasHasProperInheritedAlpha()
{
GameObject root = (GameObject)Object.Instantiate(Resources.Load("NestedCanvasPrefab"));
CanvasGroup group = root.GetComponentInChildren<CanvasGroup>();
Image image = root.GetComponentInChildren<Image>();
group.alpha = 0.5f;
yield return null;
Assert.True(image.canvasRenderer.GetInheritedAlpha() == 0.5f);
GameObject.DestroyImmediate(root);
}
[TearDown]
public void TearDown()
{
GameObject.DestroyImmediate(m_GO1);
GameObject.DestroyImmediate(m_GO2);
}
[OneTimeTearDown]
public void OneTimeTearDown()
{
#if UNITY_EDITOR
AssetDatabase.DeleteAsset(kPrefabPath);
#endif
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: fc8d686a4c18b8d49bb1db4150de0459
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,57 @@
using UnityEngine;
using UnityEngine.TestTools;
using NUnit.Framework;
internal class NestedCanvasMaintainsCorrectSize : IPrebuildSetup
{
BridgeScriptForRetainingObjects m_BridgeComponent;
public void Setup()
{
#if UNITY_EDITOR
var canvasGO = new GameObject("Canvas", typeof(Canvas));
canvasGO.GetComponent<Canvas>().renderMode = RenderMode.ScreenSpaceOverlay;
var nestedCanvasGO = new GameObject("NestedCanvas", typeof(Canvas));
nestedCanvasGO.transform.SetParent(canvasGO.transform);
var rectTransform = (RectTransform)nestedCanvasGO.transform;
rectTransform.anchorMin = Vector2.zero;
rectTransform.anchorMax = Vector2.one;
rectTransform.anchoredPosition = Vector2.zero;
rectTransform.sizeDelta = new Vector2(-20f, -20f);
var bridgeObject = GameObject.Find(BridgeScriptForRetainingObjects.bridgeObjectName) ?? new GameObject(BridgeScriptForRetainingObjects.bridgeObjectName);
var component = bridgeObject.GetComponent<BridgeScriptForRetainingObjects>() ?? bridgeObject.AddComponent<BridgeScriptForRetainingObjects>();
component.canvasGO = canvasGO;
component.nestedCanvasGO = nestedCanvasGO;
canvasGO.SetActive(false);
nestedCanvasGO.SetActive(false);
#endif
}
[SetUp]
public void TestSetup()
{
m_BridgeComponent = GameObject.Find(BridgeScriptForRetainingObjects.bridgeObjectName).GetComponent<BridgeScriptForRetainingObjects>();
m_BridgeComponent.canvasGO.SetActive(true);
m_BridgeComponent.nestedCanvasGO.SetActive(true);
}
[Test]
public void NestedCanvasMaintainsCorrectSizeAtGameStart()
{
var rectTransform = (RectTransform)m_BridgeComponent.nestedCanvasGO.transform;
Assert.That(rectTransform.anchoredPosition, Is.EqualTo(Vector2.zero));
Assert.That(rectTransform.sizeDelta, Is.EqualTo(new Vector2(-20f, -20f)));
Assert.That(rectTransform.anchorMin, Is.EqualTo(Vector2.zero));
Assert.That(rectTransform.anchorMax, Is.EqualTo(Vector2.one));
}
[TearDown]
public void TearDown()
{
GameObject.DestroyImmediate(m_BridgeComponent.canvasGO);
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: be07f70ee67e6d74e851a9333719bbb6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,61 @@
using System;
using UnityEngine;
using UnityEngine.TestTools;
using NUnit.Framework;
using System.Collections;
using UnityEngine.Profiling;
using UnityEngine.SceneManagement;
using UnityEditor;
[TestFixture]
[UnityPlatform(include = new RuntimePlatform[] { RuntimePlatform.OSXEditor, RuntimePlatform.LinuxEditor, RuntimePlatform.WindowsEditor })]
[Category("RegressionTest")]
[Description("CoveredBugID = 883807, CoveredBugDescription = \"Object::GetInstanceID crash when trying to switch canvas\"")]
internal class NoActiveCameraInSceneDoesNotCrashEditor : IPrebuildSetup
{
Scene m_InitScene;
const string k_SceneName = "NoActiveCameraInSceneDoesNotCrashEditorScene";
public void Setup()
{
#if UNITY_EDITOR
Action codeToExecute = delegate()
{
UnityEditor.EditorApplication.ExecuteMenuItem("GameObject/UI (Canvas)/Legacy/Button");
};
CreateSceneUtility.CreateScene(k_SceneName, codeToExecute);
#endif
}
[SetUp]
public void TestSetup()
{
m_InitScene = SceneManager.GetActiveScene();
}
[UnityTest]
public IEnumerator EditorShouldNotCrashWithoutActiveCamera()
{
AsyncOperation operationResult = SceneManager.LoadSceneAsync(k_SceneName, LoadSceneMode.Additive);
yield return operationResult;
SceneManager.SetActiveScene(SceneManager.GetSceneByName(k_SceneName));
Profiler.enabled = true;
Camera.main.gameObject.SetActive(false);
yield return new WaitForSeconds(0.1f);
Assert.That(Profiler.enabled, Is.True, "Expected the profiler to be enabled. Unable to test if the profiler will crash the editor if it is not enabled.");
}
[TearDown]
public void TearDown()
{
SceneManager.SetActiveScene(m_InitScene);
SceneManager.UnloadSceneAsync(k_SceneName);
#if UNITY_EDITOR
AssetDatabase.DeleteAsset("Assets/" + k_SceneName + ".unity");
#endif
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9c21cfda3336137438c3001d40564be0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,47 @@
using NUnit.Framework;
using UnityEngine;
using UnityEngine.UI;
namespace Graphics
{
[Category("RegressionTest")]
[Description("CoveredBugID = 1395695, CoveredBugDescription = \"RectMask2D hides all content when parented from other display to first dislpay in the Game view window\"")]
internal class RectMask2DReparentedToDifferentCanvas
{
GameObject m_GameObjectA;
GameObject m_GameObjectB;
Canvas m_CanvasA;
Canvas m_CanvasB;
RectMask2D m_Mask;
[SetUp]
public void TestSetup()
{
m_GameObjectA = new GameObject("Canvas A");
m_GameObjectB = new GameObject("Canvas B");
m_CanvasA = m_GameObjectA.AddComponent<Canvas>();
m_CanvasB = m_GameObjectB.AddComponent<Canvas>();
var rectMaskGameObject = new GameObject("RectMask2D");
m_Mask = rectMaskGameObject.AddComponent<RectMask2D>();
}
[TearDown]
public void TearDown()
{
Object.DestroyImmediate(m_Mask.gameObject);
Object.DestroyImmediate(m_GameObjectA);
Object.DestroyImmediate(m_GameObjectB);
}
[Test]
public void ReparentingRectMask2D_UpdatesCanvas()
{
m_Mask.transform.SetParent(m_GameObjectA.transform);
Assert.AreSame(m_CanvasA, m_Mask.Canvas);
m_Mask.transform.SetParent(m_GameObjectB.transform);
Assert.AreSame(m_CanvasB, m_Mask.Canvas, "Expected Canvas to be updated after parent changed.");
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2ba506934dc2ce14591a37f3b00dca92
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,63 @@
using UnityEngine;
using UnityEngine.TestTools;
using NUnit.Framework;
using System.Collections;
using UnityEngine.UI;
namespace Graphics
{
[TestFixture]
[Category("RegressionTest")]
[Description(
"CoveredBugID = 782957, CoveredBugDescription = \"Some element from scroll view are invisible when they're masked with RectMask2D and sub-canvases\"")]
internal class RectMask2DWithNestedCanvasCullsUsingCorrectCanvasRect
{
GameObject m_RootCanvasGO;
GameObject m_MaskGO;
GameObject m_ImageGO;
[SetUp]
public void TestSetup()
{
m_RootCanvasGO = new GameObject("Canvas");
m_MaskGO = new GameObject("Mask", typeof(RectMask2D));
m_ImageGO = new GameObject("Image");
}
[UnityTest]
public IEnumerator RectMask2DShouldNotCullImagesWithCanvas()
{
//Root Canvas
var canvas = m_RootCanvasGO.AddComponent<Canvas>();
canvas.renderMode = RenderMode.ScreenSpaceOverlay;
// Rectmaskk2D
var maskRect = m_MaskGO.GetComponent<RectTransform>();
maskRect.sizeDelta = new Vector2(200, 200);
// Our image that will be in the RectMask2D
var image = m_ImageGO.AddComponent<Image>();
var imageRenderer = m_ImageGO.GetComponent<CanvasRenderer>();
var imageRect = m_ImageGO.GetComponent<RectTransform>();
m_ImageGO.AddComponent<Canvas>();
imageRect.sizeDelta = new Vector2(10, 10);
m_MaskGO.transform.SetParent(canvas.transform);
image.transform.SetParent(m_MaskGO.transform);
imageRect.position = maskRect.position = Vector3.zero;
yield return new WaitForSeconds(0.1f);
Assert.That(imageRenderer.cull, Is.False,
"Expected image(with canvas) to not be culled by the RectMask2D but it was.");
}
[TearDown]
public void TearDown()
{
GameObject.DestroyImmediate(m_RootCanvasGO);
GameObject.DestroyImmediate(m_MaskGO);
GameObject.DestroyImmediate(m_ImageGO);
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 735d54f21944f834f931716514c87a84
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,71 @@
using System;
using System.Collections;
using UnityEngine;
using UnityEngine.TestTools;
using NUnit.Framework;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
using UnityEditor;
[TestFixture]
internal class RectTransformValidAfterEnable : IPrebuildSetup
{
const string kSceneName = "DisabledCanvasScene";
const string kGameObjectName = "DisabledCanvas";
public void Setup()
{
#if UNITY_EDITOR
Action codeToExecute = delegate()
{
var canvasGameObject = new GameObject(kGameObjectName, typeof(Canvas));
canvasGameObject.SetActive(false);
canvasGameObject.GetComponent<Canvas>().renderMode = RenderMode.ScreenSpaceOverlay;
canvasGameObject.GetComponent<RectTransform>().sizeDelta = new Vector2(0, 0);
canvasGameObject.GetComponent<RectTransform>().anchoredPosition = new Vector2(0, 0);
CanvasScaler canvasScaler = canvasGameObject.AddComponent<CanvasScaler>();
canvasScaler.uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize;
canvasScaler.referenceResolution = new Vector2(1024, 768);
};
CreateSceneUtility.CreateScene(kSceneName, codeToExecute);
#endif
}
[UnityTest]
public IEnumerator CheckRectTransformValidAfterEnable()
{
AsyncOperation operation = SceneManager.LoadSceneAsync(kSceneName, LoadSceneMode.Additive);
yield return operation;
Scene scene = SceneManager.GetSceneByName(kSceneName);
GameObject[] gameObjects = scene.GetRootGameObjects();
GameObject canvasGameObject = null;
foreach (GameObject gameObject in gameObjects)
{
if (gameObject.name == kGameObjectName)
{
canvasGameObject = gameObject;
break;
}
}
Assert.IsNotNull(canvasGameObject);
RectTransform rectTransform = canvasGameObject.GetComponent<RectTransform>();
canvasGameObject.SetActive(true);
Rect rect = rectTransform.rect;
Assert.Greater(rect.width, 0);
Assert.Greater(rect.height, 0);
operation = SceneManager.UnloadSceneAsync(kSceneName);
yield return operation;
}
[TearDown]
public void TearDown()
{
//Manually add Assets/ and .unity as CreateSceneUtility does that for you.
#if UNITY_EDITOR
AssetDatabase.DeleteAsset("Assets/" + kSceneName + ".unity");
#endif
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 811d999912a5f3f459a637aad029fbc8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,76 @@
using NUnit.Framework;
using UnityEditor;
using UnityEngine;
using System.Linq;
internal class RectangleContainsScreenPointTest : MonoBehaviour
{
RectTransform m_RectTransform;
Camera m_MainCamera;
GameObject m_CanvasObject;
GameObject m_RectObject;
[SetUp]
public void Setup()
{
m_MainCamera = new GameObject("MainCamera").AddComponent<Camera>();
m_MainCamera.transform.position = new Vector3(0, 1, -10);
m_MainCamera.depth = -1;
m_CanvasObject = new GameObject("Canvas");
Canvas m_canvas = m_CanvasObject.AddComponent<Canvas>();
m_canvas.transform.localPosition = new Vector3(0, 1, 90);
m_canvas.renderMode = RenderMode.ScreenSpaceCamera;
m_canvas.worldCamera = m_MainCamera;
m_RectObject = new GameObject("RectTransformObject");
m_RectTransform = m_RectObject.AddComponent<RectTransform>();
m_RectTransform.SetParent(m_CanvasObject.transform, false);
}
[TearDown]
public void TearDown()
{
Destroy(m_MainCamera.gameObject);
Destroy(m_CanvasObject);
Destroy(m_RectObject);
Destroy(m_RectTransform);
}
[Test]
public void RectangleContainsScreenPoint_ReturnsTrue_ForAllPointsInTheRectangle()
{
var fourCourners = new Vector3[4];
m_RectTransform.GetWorldCorners(fourCourners);
var worldCorners = fourCourners
.Select(p => m_MainCamera.WorldToScreenPoint(p))
.ToArray();
var minValue = new Vector2(
x: worldCorners.Min(p => p.x),
y: worldCorners.Min(p => p.y));
var maxValue = new Vector2(
x: worldCorners.Max(p => p.x),
y: worldCorners.Max(p => p.y));
var steps = 10000;
bool ErrorHit = false;
for (float i = 0; i < steps; i++)
{
var point = Vector2.Lerp(minValue, maxValue, i / steps);
if (!RectTransformUtility.RectangleContainsScreenPoint(m_RectTransform, point, m_MainCamera))
{
ErrorHit = true;
Assert.Fail("Rectangle does not Contains ScreenPoint");
}
}
if (!ErrorHit)
{
Assert.Pass();
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 08a8587e5f07fb64f88a0cbe2ebe327e
@@ -0,0 +1,83 @@
using UnityEngine;
using UnityEngine.TestTools;
using NUnit.Framework;
using System.Collections;
using UnityEngine.UI;
[TestFixture]
[Category("RegressionTest")]
[Description("Case 723062")]
internal class SiblingOrderChangesLayout
{
GameObject m_CanvasGO;
GameObject m_ParentGO;
GameObject m_Child1GO;
GameObject m_Child2GO;
[SetUp]
public void TestSetup()
{
m_CanvasGO = new GameObject("Canvas", typeof(Canvas));
m_ParentGO = new GameObject("ParentRenderer");
m_Child1GO = CreateTextObject("ChildRenderer1");
m_Child2GO = CreateTextObject("ChildRenderer2");
#if UNITY_EDITOR
UnityEditor.EditorApplication.ExecuteMenuItem("Window/General/Game");
#endif
}
[UnityTest]
public IEnumerator ReorderingSiblingChangesLayout()
{
m_ParentGO.transform.SetParent(m_CanvasGO.transform);
m_Child1GO.transform.SetParent(m_ParentGO.transform);
m_Child2GO.transform.SetParent(m_ParentGO.transform);
m_ParentGO.AddComponent<CanvasRenderer>();
m_ParentGO.AddComponent<RectTransform>();
m_ParentGO.AddComponent<VerticalLayoutGroup>();
m_ParentGO.AddComponent<ContentSizeFitter>();
yield return null;
Vector2 child1Pos = m_Child1GO.GetComponent<RectTransform>().anchoredPosition;
Vector2 child2Pos = m_Child2GO.GetComponent<RectTransform>().anchoredPosition;
Assert.That(child1Pos, Is.Not.EqualTo(child2Pos));
Assert.That(m_Child1GO.GetComponent<CanvasRenderer>().hasMoved, Is.False, "CanvasRenderer.hasMoved should be false");
m_Child2GO.transform.SetAsFirstSibling();
Canvas.ForceUpdateCanvases();
Assert.That(m_Child1GO.GetComponent<CanvasRenderer>().hasMoved, Is.True, "CanvasRenderer.hasMoved should be true");
Vector2 newChild1Pos = m_Child1GO.GetComponent<RectTransform>().anchoredPosition;
Vector2 newChild2Pos = m_Child2GO.GetComponent<RectTransform>().anchoredPosition;
Assert.That(newChild1Pos, Is.EqualTo(child2Pos), "Child1 should have moved to Child2's position");
Assert.That(newChild2Pos, Is.EqualTo(child1Pos), "Child2 should have moved to Child1's position");
}
[TearDown]
public void TearDown()
{
GameObject.DestroyImmediate(m_CanvasGO);
}
// Factory method for creating UI text objects taken from the original bug repro scene:
private GameObject CreateTextObject(string name)
{
GameObject outputTextGameObject = new GameObject("OutputContent", typeof(CanvasRenderer));
RectTransform outputTextTransform = outputTextGameObject.AddComponent<RectTransform>();
outputTextTransform.pivot = new Vector2(0.5f, 0);
outputTextTransform.anchorMin = Vector2.zero;
outputTextTransform.anchorMax = new Vector2(1, 0);
outputTextTransform.anchoredPosition = Vector2.zero;
outputTextTransform.sizeDelta = Vector2.zero;
Text outputText = outputTextGameObject.AddComponent<Text>();
outputText.text = "Hello World!";
outputTextGameObject.name = name;
return outputTextGameObject;
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c32df609537c54c46adf92992a673693
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: a69b08936f9f74179ae333a4bebe7c54
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,177 @@
using UnityEngine;
using UnityEngine.TestTools;
using NUnit.Framework;
internal class CanvasRendererTests
{
private const int Width = 32;
private const int Height = 32;
GameObject m_GraphicObj;
CanvasRenderer m_CanvasRenderer;
private Texture2D m_DefaultTexture;
static readonly string k_MaskTexPropName = "_MaskTex";
static readonly string k_GlowTexPropName = "_GlowTex";
Texture2D m_MaskTex;
Texture2D m_GlowTex;
[SetUp]
public void SetUp()
{
m_GraphicObj = new GameObject("Graphic");
m_CanvasRenderer = m_GraphicObj.AddComponent<CanvasRenderer>();
m_MaskTex = CreateTexture(Color.red);
m_GlowTex = CreateTexture(Color.yellow);
}
Texture2D CreateTexture(Color color)
{
var tex = new Texture2D(Width, Height);
Color[] colors = new Color[Width * Height];
for (int i = 0; i < Width * Height; i++)
colors[i] = color;
tex.SetPixels(colors);
tex.Apply();
return tex;
}
[Test]
public void InitialData()
{
Assert.AreEqual(0, m_CanvasRenderer.GetSecondaryTextureCount());
}
[Test]
public void AddSecondaryTextures()
{
m_CanvasRenderer.SetSecondaryTextureCount(2);
Assert.AreEqual(2, m_CanvasRenderer.GetSecondaryTextureCount());
Assert.True(string.IsNullOrEmpty(m_CanvasRenderer.GetSecondaryTextureName(0)));
Assert.Null(m_CanvasRenderer.GetSecondaryTexture(0));
Assert.True(string.IsNullOrEmpty(m_CanvasRenderer.GetSecondaryTextureName(1)));
Assert.Null(m_CanvasRenderer.GetSecondaryTexture(1));
m_CanvasRenderer.SetSecondaryTexture(0, k_MaskTexPropName, m_MaskTex);
m_CanvasRenderer.SetSecondaryTexture(1, k_GlowTexPropName, m_GlowTex);
Assert.AreEqual(k_MaskTexPropName, m_CanvasRenderer.GetSecondaryTextureName(0));
Assert.AreEqual(m_MaskTex, m_CanvasRenderer.GetSecondaryTexture(0));
Assert.AreEqual(k_GlowTexPropName, m_CanvasRenderer.GetSecondaryTextureName(1));
Assert.AreEqual(m_GlowTex, m_CanvasRenderer.GetSecondaryTexture(1));
}
[Test]
public void RemoveSecondaryTextures()
{
m_CanvasRenderer.SetSecondaryTextureCount(2);
m_CanvasRenderer.SetSecondaryTexture(0, k_MaskTexPropName, m_MaskTex);
m_CanvasRenderer.SetSecondaryTexture(1, k_GlowTexPropName, m_GlowTex);
// The last secondary texture
m_CanvasRenderer.SetSecondaryTextureCount(1);
Assert.AreEqual(1, m_CanvasRenderer.GetSecondaryTextureCount());
Assert.AreEqual(k_MaskTexPropName, m_CanvasRenderer.GetSecondaryTextureName(0));
Assert.AreEqual(m_MaskTex, m_CanvasRenderer.GetSecondaryTexture(0));
}
[Test]
public void SetSecondaryTextureCount()
{
m_CanvasRenderer.SetSecondaryTextureCount(2);
m_CanvasRenderer.SetSecondaryTexture(0, k_MaskTexPropName, m_MaskTex);
m_CanvasRenderer.SetSecondaryTexture(1, k_GlowTexPropName, m_GlowTex);
m_CanvasRenderer.SetSecondaryTextureCount(1);
Assert.AreEqual(1, m_CanvasRenderer.GetSecondaryTextureCount());
Assert.AreEqual(k_MaskTexPropName, m_CanvasRenderer.GetSecondaryTextureName(0));
Assert.AreEqual(m_MaskTex, m_CanvasRenderer.GetSecondaryTexture(0));
// Increase the number of secondary textures and verify that the new entries are empty
m_CanvasRenderer.SetSecondaryTextureCount(3);
Assert.AreEqual(3, m_CanvasRenderer.GetSecondaryTextureCount());
Assert.AreEqual(k_MaskTexPropName, m_CanvasRenderer.GetSecondaryTextureName(0));
Assert.AreEqual(m_MaskTex, m_CanvasRenderer.GetSecondaryTexture(0));
Assert.True(string.IsNullOrEmpty(m_CanvasRenderer.GetSecondaryTextureName(1)));
Assert.Null(m_CanvasRenderer.GetSecondaryTexture(1));
Assert.True(string.IsNullOrEmpty(m_CanvasRenderer.GetSecondaryTextureName(2)));
Assert.Null(m_CanvasRenderer.GetSecondaryTexture(2));
// Clear all the secondary textures
m_CanvasRenderer.SetSecondaryTextureCount(0);
Assert.AreEqual(0, m_CanvasRenderer.GetSecondaryTextureCount());
// Add an element again and verify that it is empty
m_CanvasRenderer.SetSecondaryTextureCount(1);
Assert.True(string.IsNullOrEmpty(m_CanvasRenderer.GetSecondaryTextureName(0)));
Assert.Null(m_CanvasRenderer.GetSecondaryTexture(0));
}
// UUM-154589
[Test]
public void SetSecondaryTexture_WithNoSlotsAllocated_IsIgnored()
{
LogAssert.Expect(LogType.Error, "Failed setting secondary texture. Index is out of bounds.");
m_CanvasRenderer.SetSecondaryTexture(0, k_MaskTexPropName, m_MaskTex);
Assert.AreEqual(0, m_CanvasRenderer.GetSecondaryTextureCount(), "SetSecondaryTexture with no slots allocated should not add a slot.");
}
// UUM-154589
[Test]
public void SetSecondaryTexture_WithIndexPastAllocatedSlots_IsIgnored()
{
m_CanvasRenderer.SetSecondaryTextureCount(2);
LogAssert.Expect(LogType.Error, "Failed setting secondary texture. Index is out of bounds.");
m_CanvasRenderer.SetSecondaryTexture(3, k_MaskTexPropName, m_MaskTex);
Assert.AreEqual(2, m_CanvasRenderer.GetSecondaryTextureCount(), "SetSecondaryTexture past the allocated slots should not change the slot count.");
}
[Test]
public void SetSecondaryTextureCount_WithNegativeCount_IsIgnored()
{
m_CanvasRenderer.SetSecondaryTextureCount(2);
LogAssert.Expect(LogType.Error, "Failed setting secondary texture count. Count cannot be negative.");
m_CanvasRenderer.SetSecondaryTextureCount(-1);
Assert.AreEqual(2, m_CanvasRenderer.GetSecondaryTextureCount(), "SetSecondaryTextureCount with a negative count should keep the current slot count.");
}
// UUM-154589
[Test]
public void MaterialCount_WithNegativeValue_IsIgnored()
{
m_CanvasRenderer.materialCount = 2;
LogAssert.Expect(LogType.Error, "Failed setting material count. Count cannot be negative.");
m_CanvasRenderer.materialCount = -1;
Assert.AreEqual(2, m_CanvasRenderer.materialCount, "A negative materialCount should keep the current count.");
}
// UUM-154589
[Test]
public void PopMaterialCount_WithNegativeValue_IsIgnored()
{
m_CanvasRenderer.popMaterialCount = 2;
LogAssert.Expect(LogType.Error, "Failed setting pop material count. Count cannot be negative.");
m_CanvasRenderer.popMaterialCount = -1;
Assert.AreEqual(2, m_CanvasRenderer.popMaterialCount, "A negative popMaterialCount should keep the current count.");
}
[TearDown]
public void TearDown()
{
GameObject.DestroyImmediate(m_GraphicObj);
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 6fac6d65d2fb7472d94e95ddb8b8798c
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: bae197be297529d4fa735fbe7c91828d
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,162 @@
using System.Collections;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.TestTools;
using NUnit.Framework;
using System.IO;
using UnityEditor;
using System.Collections.Generic;
internal class DropdownTests : IPrebuildSetup
{
GameObject m_PrefabRoot;
GameObject m_CameraGO;
const string kPrefabPath = "Assets/Resources/DropdownPrefab.prefab";
public void Setup()
{
#if UNITY_EDITOR
var rootGO = new GameObject("rootGo");
var canvasGO = new GameObject("Canvas", typeof(Canvas));
var canvas = canvasGO.GetComponent<Canvas>();
canvas.renderMode = RenderMode.WorldSpace;
canvasGO.transform.SetParent(rootGO.transform);
var dropdownGO = new GameObject("Dropdown", typeof(RectTransform), typeof(Dropdown));
var dropdownTransform = dropdownGO.GetComponent<RectTransform>();
dropdownTransform.SetParent(canvas.transform);
dropdownTransform.anchoredPosition = Vector2.zero;
var dropdown = dropdownGO.GetComponent<Dropdown>();
var templateGO = new GameObject("Template", typeof(RectTransform));
templateGO.SetActive(false);
var templateTransform = templateGO.GetComponent<RectTransform>();
templateTransform.SetParent(dropdownTransform);
var itemGo = new GameObject("Item", typeof(RectTransform), typeof(Toggle));
itemGo.transform.SetParent(templateTransform);
dropdown.template = templateTransform;
if (!Directory.Exists("Assets/Resources/"))
Directory.CreateDirectory("Assets/Resources/");
PrefabUtility.SaveAsPrefabAsset(rootGO, kPrefabPath);
GameObject.DestroyImmediate(rootGO);
// add a custom sorting layer before test. It doesn't seem to be serialized so no need to remove it after test
SerializedObject tagManager = new SerializedObject(AssetDatabase.LoadAllAssetsAtPath("ProjectSettings/TagManager.asset")[0]);
SerializedProperty sortingLayers = tagManager.FindProperty("m_SortingLayers");
sortingLayers.InsertArrayElementAtIndex(sortingLayers.arraySize);
var arrayElement = sortingLayers.GetArrayElementAtIndex(sortingLayers.arraySize - 1);
foreach (SerializedProperty a in arrayElement)
{
switch (a.name)
{
case "name":
a.stringValue = "test layer";
break;
case "uniqueID":
a.intValue = 314159265;
break;
case "locked":
a.boolValue = false;
break;
}
}
tagManager.ApplyModifiedProperties();
#endif
}
[SetUp]
public void TestSetup()
{
m_PrefabRoot = Object.Instantiate(Resources.Load("DropdownPrefab")) as GameObject;
m_CameraGO = new GameObject("Camera", typeof(Camera));
}
// test for case 958281 - [UI] Dropdown list does not copy the parent canvas layer when the panel is opened
[UnityTest]
public IEnumerator Dropdown_Canvas()
{
var dropdown = m_PrefabRoot.GetComponentInChildren<Dropdown>();
var rootCanvas = m_PrefabRoot.GetComponentInChildren<Canvas>();
rootCanvas.sortingLayerName = "test layer";
dropdown.Show();
yield return null;
var dropdownList = dropdown.transform.Find("Dropdown List");
var dropdownListCanvas = dropdownList.GetComponentInChildren<Canvas>();
Assert.AreEqual(rootCanvas.sortingLayerID, dropdownListCanvas.sortingLayerID, "Sorting layers should match");
}
// test for case 1343542 - [UI] Child Canvas' Sorting Layer is changed to the same value as the parent
[UnityTest]
public IEnumerator Dropdown_Canvas_Already_Exists()
{
var dropdown = m_PrefabRoot.GetComponentInChildren<Dropdown>();
var rootCanvas = m_PrefabRoot.GetComponentInChildren<Canvas>();
var templateCanvas = dropdown.transform.Find("Template").gameObject.AddComponent<Canvas>();
templateCanvas.overrideSorting = true;
templateCanvas.sortingLayerName = "test layer";
dropdown.Show();
yield return null;
var dropdownList = dropdown.transform.Find("Dropdown List");
var dropdownListCanvas = dropdownList.GetComponentInChildren<Canvas>();
Assert.AreNotEqual(rootCanvas.sortingLayerName, dropdownListCanvas.sortingLayerName, "Sorting layers should not match");
}
// test for case 935649 - open dropdown menus become unresponsive when disabled and reenabled
[UnityTest]
public IEnumerator Dropdown_Disable()
{
var dropdown = m_PrefabRoot.GetComponentInChildren<Dropdown>();
dropdown.Show();
dropdown.gameObject.SetActive(false);
yield return null;
var dropdownList = dropdown.transform.Find("Dropdown List");
Assert.IsNull(dropdownList);
}
[UnityTest]
public IEnumerator Dropdown_ResetAndClear()
{
var options = new List<string> { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10" };
var dropdown = m_PrefabRoot.GetComponentInChildren<Dropdown>();
// generate a first dropdown
dropdown.ClearOptions();
dropdown.AddOptions(options);
dropdown.value = 3;
yield return null;
// clear it and generate a new one
dropdown.ClearOptions();
yield return null;
// check is the value is 0
Assert.IsTrue(dropdown.value == 0);
}
[TearDown]
public void TearDown()
{
Object.DestroyImmediate(m_PrefabRoot);
GameObject.DestroyImmediate(m_CameraGO);
}
[OneTimeTearDown]
public void OneTimeTearDown()
{
#if UNITY_EDITOR
AssetDatabase.DeleteAsset(kPrefabPath);
SerializedObject tagManager = new SerializedObject(AssetDatabase.LoadAllAssetsAtPath("ProjectSettings/TagManager.asset")[0]);
SerializedProperty sortingLayers = tagManager.FindProperty("m_SortingLayers");
sortingLayers.DeleteArrayElementAtIndex(sortingLayers.arraySize - 1);
tagManager.ApplyModifiedProperties();
#endif
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 358a618bc6bd9354d81cc206fd2ed80e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: ab93e1a81defc3243a6e9cd0df3cb443
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,114 @@
using System.Collections;
using System.Collections.Generic;
using NUnit.Framework;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.TestTools;
using UnityEngine.UI;
internal class GraphicRaycasterButtonTests
{
Camera m_Camera;
EventSystem m_EventSystem;
Canvas m_Canvas;
Button m_ParentButton;
Button m_ChildButton;
Sprite m_Sprite;
const int TextureSize = 64;
readonly Texture2D texture = new Texture2D(TextureSize, TextureSize);
[UnitySetUp]
public IEnumerator TestSetup()
{
m_Camera = new GameObject("Camera").AddComponent<Camera>();
m_Camera.transform.position = new Vector3(0, 0, -10);
m_Canvas = new GameObject("Canvas").AddComponent<Canvas>();
m_Canvas.renderMode = RenderMode.ScreenSpaceOverlay;
m_Canvas.gameObject.AddComponent<GraphicRaycaster>();
m_EventSystem = new GameObject("Event System").AddComponent<EventSystem>();
Color[] colors = new Color[TextureSize * TextureSize];
for (int y = 24; y < 40; y++)
for (int x = 0; x < TextureSize; x++)
colors[y + TextureSize * x] = colors[x + TextureSize * y] = Color.red;
texture.SetPixels(colors);
texture.Apply();
m_Sprite = Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height), new Vector2(0.5f, 0.5f), 100);
var parentImage = new GameObject("ParentButton", typeof(RectTransform)).AddComponent<Image>();
parentImage.transform.SetParent(m_Canvas.transform);
parentImage.rectTransform.anchoredPosition = new Vector2(0, 0);
parentImage.sprite = m_Sprite;
parentImage.SetNativeSize();
m_ParentButton = parentImage.gameObject.AddComponent<Button>();
// Duplicate Button as a child GO
m_ChildButton = Object.Instantiate(m_ParentButton, m_ParentButton.transform);
m_ChildButton.name = "ChildButton";
var childImage = m_ChildButton.GetComponent<Image>();
childImage.rectTransform.anchoredPosition = new Vector2(8, 50);
parentImage.alphaHitTestMinimumThreshold = 0.5f;
childImage.alphaHitTestMinimumThreshold = 0.5f;
// Yield one frame so that all tests below work.
yield return null;
}
[Test]
[TestCase( 0f, -40f, false, false, TestName = "Should not hit either button (outside of both boundaries)")]
[TestCase( 0f, 0f, true, false, TestName = "Should hit the parent (opaque pixel), but not the child (outside)")]
[TestCase(-30f, -30f, false, false, TestName = "Should not hit the parent (transparent pixel), nor the child (outside)")]
[TestCase( 10f, 30f, false, true, TestName = "Should not hit the parent (transparent pixel), but hit the child (opaque pixel),")]
[TestCase( 5f, 30f, true, true, TestName = "Should hit the parent (opaque pixel), and the child (opaque pixel)")]
[TestCase( -5f, 30f, true, false, TestName = "Should hit the parent (opaque pixel), but not the child (transparent pixel)")]
[TestCase(-10f, 30f, false, false, TestName = "Should not hit the parent (transparent pixel), nor the child (transparent pixel)")]
[TestCase( 8f, 50f, false, true, TestName = "Should not hit the parent (outside), but hit the child (opaque pixel)")]
[TestCase(-10f, 30f, false, false, TestName = "Should not hit the parent (outside), nor the child (transparent pixel)")]
public void GraphicRaycaster_ChildButtonOutsideOfParentButton(float x, float y, bool hitParent, bool hitChild)
{
var results = new List<RaycastResult>();
var pointerEvent = new PointerEventData(m_EventSystem)
{
position = new Vector2(Screen.width / 2f + x, Screen.height / 2f + y)
};
m_EventSystem.RaycastAll(pointerEvent, results);
if (hitParent == false && hitChild == false)
{
Assert.IsEmpty(results, "Expected no results from a raycast.");
}
else
{
if (hitParent && hitChild)
Assert.AreEqual(2, results.Count, "Expected 2 results from a raycast.");
else
Assert.AreEqual(1, results.Count, "Expected 1 result from a raycast.");
int hitIndex = 0;
if (hitChild)
{
Assert.AreSame(m_ChildButton.gameObject, results[hitIndex].gameObject);
hitIndex++;
}
if (hitParent)
{
Assert.AreSame(m_ParentButton.gameObject, results[hitIndex].gameObject);
}
}
}
[TearDown]
public void TearDown()
{
Object.DestroyImmediate(m_Camera.gameObject);
Object.DestroyImmediate(m_EventSystem.gameObject);
Object.DestroyImmediate(m_Canvas.gameObject);
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 53ee1d135588bf447a7af7ba2e8709d7
@@ -0,0 +1,151 @@
using System.Collections;
using System.Collections.Generic;
using NUnit.Framework;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.TestTools;
using UnityEngine.UI;
using UnityEngine.TestTools.Utils;
internal class GraphicRaycasterTests
{
Camera m_Camera;
EventSystem m_EventSystem;
Canvas m_Canvas;
RectTransform m_CanvasRectTrans;
Text m_TextComponent;
[SetUp]
public void TestSetup()
{
m_Camera = new GameObject("GraphicRaycaster Camera").AddComponent<Camera>();
m_Camera.transform.position = Vector3.zero;
m_Camera.transform.LookAt(Vector3.forward);
m_Camera.farClipPlane = 10;
m_EventSystem = new GameObject("Event System").AddComponent<EventSystem>();
m_Canvas = new GameObject("Canvas").AddComponent<Canvas>();
m_Canvas.renderMode = RenderMode.WorldSpace;
m_Canvas.worldCamera = m_Camera;
m_Canvas.gameObject.AddComponent<GraphicRaycaster>();
m_CanvasRectTrans = m_Canvas.GetComponent<RectTransform>();
m_CanvasRectTrans.sizeDelta = new Vector2(100, 100);
var textGO = new GameObject("Text");
m_TextComponent = textGO.AddComponent<Text>();
var textRectTrans = m_TextComponent.rectTransform;
textRectTrans.SetParent(m_Canvas.transform);
textRectTrans.anchorMin = Vector2.zero;
textRectTrans.anchorMax = Vector2.one;
textRectTrans.offsetMin = Vector2.zero;
textRectTrans.offsetMax = Vector2.zero;
}
[UnityTest]
public IEnumerator GraphicRaycasterDoesNotHitGraphicBehindCameraFarClipPlane()
{
m_CanvasRectTrans.anchoredPosition3D = new Vector3(0, 0, 11);
yield return null;
var results = new List<RaycastResult>();
var pointerEvent = new PointerEventData(m_EventSystem)
{
position = new Vector2(Screen.width / 2f, Screen.height / 2f)
};
m_EventSystem.RaycastAll(pointerEvent, results);
Assert.IsEmpty(results, "Expected no results from a raycast against a graphic behind the camera's far clip plane.");
}
[UnityTest]
public IEnumerator GraphicRaycasterReturnsWorldPositionAndWorldNormal()
{
m_CanvasRectTrans.anchoredPosition3D = new Vector3(0, 0, 11);
m_Camera.farClipPlane = 12;
yield return null;
var results = new List<RaycastResult>();
var pointerEvent = new PointerEventData(m_EventSystem)
{
position = new Vector2(Screen.width / 2f, Screen.height / 2f)
};
m_EventSystem.RaycastAll(pointerEvent, results);
// on katana on 10.13 agents world position returned is 0, -0.00952, 11
// it does not reproduce for me localy, so we just tweak the comparison threshold
Assert.That(new Vector3(0, 0, 11), Is.EqualTo(results[0].worldPosition).Using(new Vector3EqualityComparer(0.01f)));
Assert.That(new Vector3(0, 0, -1), Is.EqualTo(results[0].worldNormal).Using(new Vector3EqualityComparer(0.001f)));
}
[UnityTest]
public IEnumerator GraphicRaycasterUsesGraphicPadding()
{
m_CanvasRectTrans.anchoredPosition3D = new Vector3(0, 0, 11);
m_TextComponent.raycastPadding = new Vector4(-50, -50, -50, -50);
m_Camera.farClipPlane = 12;
yield return null;
var results = new List<RaycastResult>();
var pointerEvent = new PointerEventData(m_EventSystem)
{
position = new Vector2((Screen.width / 2f) - 60, Screen.height / 2f)
};
m_EventSystem.RaycastAll(pointerEvent, results);
Assert.IsNotEmpty(results, "Expected at least 1 result from a raycast outside the graphics RectTransform whose padding would make the click hit.");
}
[UnityTest]
public IEnumerator GraphicOnTheSamePlaneAsTheCameraCanBeTargetedForEvents()
{
m_Canvas.renderMode = RenderMode.ScreenSpaceCamera;
m_Canvas.planeDistance = 0;
m_Camera.orthographic = true;
m_Camera.orthographicSize = 1;
m_Camera.nearClipPlane = 0;
m_Camera.farClipPlane = 12;
yield return null;
var results = new List<RaycastResult>();
var pointerEvent = new PointerEventData(m_EventSystem)
{
position = new Vector2((Screen.width / 2f), Screen.height / 2f)
};
m_EventSystem.RaycastAll(pointerEvent, results);
Assert.IsNotEmpty(results, "Expected at least 1 result from a raycast ");
}
#if ENABLE_INPUT_SYSTEM && PACKAGE_INPUTSYSTEM
[UnityTest]
public IEnumerator GraphicRaycasterIgnoresEventsFromTheWrongDisplay()
{
m_CanvasRectTrans.anchoredPosition3D = new Vector3(0, 0, 11);
m_Camera.farClipPlane = 12;
yield return null;
var results = new List<RaycastResult>();
var pointerEvent = new PointerEventData(m_EventSystem)
{
position = new Vector2(Screen.width / 2f, Screen.height / 2f),
displayIndex = 1,
};
m_EventSystem.RaycastAll(pointerEvent, results);
Assert.IsEmpty(results, "Pointer event on display 1 was not ignored on display 0");
}
#endif
[TearDown]
public void TearDown()
{
Object.DestroyImmediate(m_Camera.gameObject);
Object.DestroyImmediate(m_EventSystem.gameObject);
Object.DestroyImmediate(m_Canvas.gameObject);
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e6323ebef616fee4486ee155cd56d191
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,88 @@
using System.Collections;
using System.Collections.Generic;
using NUnit.Framework;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.TestTools;
using UnityEngine.UI;
using UnityEngine.TestTools.Utils;
internal class GraphicRaycasterWorldSpaceCanvasTests
{
Camera m_Camera;
EventSystem m_EventSystem;
Canvas m_Canvas;
RectTransform m_CanvasRectTrans;
[SetUp]
public void TestSetup()
{
m_Camera = new GameObject("GraphicRaycaster Camera").AddComponent<Camera>();
m_Camera.transform.position = Vector3.zero;
m_Camera.transform.LookAt(Vector3.forward);
m_Camera.farClipPlane = 10;
m_EventSystem = new GameObject("Event System").AddComponent<EventSystem>();
m_Canvas = new GameObject("Canvas").AddComponent<Canvas>();
m_Canvas.renderMode = RenderMode.WorldSpace;
m_Canvas.worldCamera = m_Camera;
m_Canvas.gameObject.AddComponent<GraphicRaycaster>();
m_CanvasRectTrans = m_Canvas.GetComponent<RectTransform>();
m_CanvasRectTrans.sizeDelta = new Vector2(100, 100);
var textRectTrans = new GameObject("Text").AddComponent<Text>().rectTransform;
textRectTrans.SetParent(m_Canvas.transform);
textRectTrans.anchorMin = Vector2.zero;
textRectTrans.anchorMax = Vector2.one;
textRectTrans.offsetMin = Vector2.zero;
textRectTrans.offsetMax = Vector2.zero;
}
[UnityTest]
public IEnumerator GraphicRaycasterDoesNotHitGraphicBehindCameraFarClipPlane()
{
m_CanvasRectTrans.anchoredPosition3D = new Vector3(0, 0, 11);
yield return null;
var results = new List<RaycastResult>();
var pointerEvent = new PointerEventData(m_EventSystem)
{
position = new Vector2(Screen.width / 2f, Screen.height / 2f)
};
m_EventSystem.RaycastAll(pointerEvent, results);
Assert.IsEmpty(results, "Expected no results from a raycast against a graphic behind the camera's far clip plane.");
}
[UnityTest]
public IEnumerator GraphicRaycasterReturnsWorldPositionAndWorldNormal()
{
m_CanvasRectTrans.anchoredPosition3D = new Vector3(0, 0, 11);
m_Camera.farClipPlane = 12;
yield return null;
var results = new List<RaycastResult>();
var pointerEvent = new PointerEventData(m_EventSystem)
{
position = new Vector2(Screen.width / 2f, Screen.height / 2f)
};
m_EventSystem.RaycastAll(pointerEvent, results);
// on katana on 10.13 agents world position returned is 0, -0.00952, 11
// it does not reproduce for me localy, so we just tweak the comparison threshold
Assert.That(new Vector3(0, 0, 11), Is.EqualTo(results[0].worldPosition).Using(new Vector3EqualityComparer(0.01f)));
Assert.That(new Vector3(0, 0, -1), Is.EqualTo(results[0].worldNormal).Using(new Vector3EqualityComparer(0.001f)));
}
[TearDown]
public void TearDown()
{
Object.DestroyImmediate(m_Camera.gameObject);
Object.DestroyImmediate(m_EventSystem.gameObject);
Object.DestroyImmediate(m_Canvas.gameObject);
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3d42c4854f9093e409cd90c00ef26de0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,316 @@
using System.Collections;
using System.Reflection;
using NUnit.Framework;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.TestTools;
using UnityEngine.UI;
[UnityPlatform()]
internal class InputModuleTests
{
EventSystem m_EventSystem;
FakeBaseInput m_FakeBaseInput;
StandaloneInputModule m_StandaloneInputModule;
Canvas m_Canvas;
Image m_Image;
Image m_NestedImage;
[SetUp]
public void TestSetup()
{
// Camera | Canvas (Image) | Event System
m_Canvas = new GameObject("Canvas").AddComponent<Canvas>();
m_Canvas.renderMode = RenderMode.ScreenSpaceOverlay;
m_Canvas.gameObject.AddComponent<GraphicRaycaster>();
m_Image = new GameObject("Image").AddComponent<Image>();
m_Image.gameObject.transform.SetParent(m_Canvas.transform);
RectTransform imageRectTransform = m_Image.GetComponent<RectTransform>();
imageRectTransform.sizeDelta = new Vector2(400f, 400f);
imageRectTransform.localPosition = Vector3.zero;
m_NestedImage = new GameObject("NestedImage").AddComponent<Image>();
m_NestedImage.gameObject.transform.SetParent(m_Image.transform);
RectTransform nestedImageRectTransform = m_NestedImage.GetComponent<RectTransform>();
nestedImageRectTransform.sizeDelta = new Vector2(200f, 200f);
nestedImageRectTransform.localPosition = Vector3.zero;
GameObject go = new GameObject("Event System");
m_EventSystem = go.AddComponent<EventSystem>();
m_EventSystem.pixelDragThreshold = 1;
m_StandaloneInputModule = go.AddComponent<StandaloneInputModule>();
m_FakeBaseInput = go.AddComponent<FakeBaseInput>();
// Override input with FakeBaseInput so we can send fake mouse/keyboards button presses and touches
m_StandaloneInputModule.inputOverride = m_FakeBaseInput;
Cursor.lockState = CursorLockMode.None;
}
[UnityTest]
public IEnumerator DragCallbacksDoGetCalled()
{
// While left mouse button is pressed and the mouse is moving, OnBeginDrag and OnDrag callbacks should be called
// Then when the left mouse button is released, OnEndDrag callback should be called
// Add script to EventSystem to update the mouse position
m_EventSystem.gameObject.AddComponent<MouseUpdate>();
// Add script to Image which implements OnBeginDrag, OnDrag & OnEndDrag callbacks
DragCallbackCheck callbackCheck = m_Image.gameObject.AddComponent<DragCallbackCheck>();
// Setting required input.mousePresent to fake mouse presence
m_FakeBaseInput.MousePresent = true;
var canvasRT = m_Canvas.gameObject.transform as RectTransform;
m_FakeBaseInput.MousePosition = new Vector2(Screen.width / 2, Screen.height / 2);
yield return null;
// Left mouse button down simulation
m_FakeBaseInput.MouseButtonDown[0] = true;
yield return null;
// Left mouse button down flag needs to reset in the next frame
m_FakeBaseInput.MouseButtonDown[0] = false;
yield return null;
// Left mouse button up simulation
m_FakeBaseInput.MouseButtonUp[0] = true;
yield return null;
// Left mouse button up flag needs to reset in the next frame
m_FakeBaseInput.MouseButtonUp[0] = false;
yield return null;
Assert.IsTrue(callbackCheck.onBeginDragCalled, "OnBeginDrag not called");
Assert.IsTrue(callbackCheck.onDragCalled, "OnDragCalled not called");
Assert.IsTrue(callbackCheck.onEndDragCalled, "OnEndDragCalled not called");
Assert.IsTrue(callbackCheck.onDropCalled, "OnDrop not called");
}
[UnityTest]
public IEnumerator MouseOutsideMaskRectTransform_WhileInsidePaddedArea_PerformsClick()
{
var mask = new GameObject("Panel").AddComponent<RectMask2D>();
mask.gameObject.transform.SetParent(m_Canvas.transform);
RectTransform panelRectTransform = mask.GetComponent<RectTransform>();
panelRectTransform.sizeDelta = new Vector2(100, 100f);
panelRectTransform.localPosition = Vector3.zero;
m_Image.gameObject.transform.SetParent(mask.transform, true);
mask.padding = new Vector4(-30, -30, -30, -30);
PointerClickCallbackCheck callbackCheck = m_Image.gameObject.AddComponent<PointerClickCallbackCheck>();
var canvasRT = m_Canvas.gameObject.transform as RectTransform;
var screenMiddle = new Vector2(Screen.width / 2, Screen.height / 2);
m_FakeBaseInput.MousePresent = true;
m_FakeBaseInput.MousePosition = screenMiddle;
yield return null;
// Click the center of the screen should hit the middle of the image.
m_FakeBaseInput.MouseButtonDown[0] = true;
yield return null;
m_FakeBaseInput.MouseButtonDown[0] = false;
yield return null;
m_FakeBaseInput.MouseButtonUp[0] = true;
yield return null;
m_FakeBaseInput.MouseButtonUp[0] = false;
yield return null;
Assert.IsTrue(callbackCheck.pointerDown);
//Reset the callbackcheck and click outside the mask but still in the image.
callbackCheck.pointerDown = false;
m_FakeBaseInput.MousePosition = new Vector2(screenMiddle.x - 60, screenMiddle.y);
yield return null;
m_FakeBaseInput.MouseButtonDown[0] = true;
yield return null;
m_FakeBaseInput.MouseButtonDown[0] = false;
yield return null;
m_FakeBaseInput.MouseButtonUp[0] = true;
yield return null;
m_FakeBaseInput.MouseButtonUp[0] = false;
yield return null;
Assert.IsTrue(callbackCheck.pointerDown);
//Reset the callbackcheck and click outside the mask and outside in the image.
callbackCheck.pointerDown = false;
m_FakeBaseInput.MousePosition = new Vector2(screenMiddle.x - 100, screenMiddle.y);
yield return null;
m_FakeBaseInput.MouseButtonDown[0] = true;
yield return null;
m_FakeBaseInput.MouseButtonDown[0] = false;
yield return null;
m_FakeBaseInput.MouseButtonUp[0] = true;
yield return null;
m_FakeBaseInput.MouseButtonUp[0] = false;
yield return null;
Assert.IsFalse(callbackCheck.pointerDown);
}
[UnityTest]
public IEnumerator PointerEnterChildShouldNotFullyExit_NotSendPointerEventToParent()
{
m_StandaloneInputModule.sendPointerHoverToParent = false;
PointerExitCallbackCheck callbackCheck = m_Image.gameObject.AddComponent<PointerExitCallbackCheck>();
m_NestedImage.gameObject.AddComponent<PointerExitCallbackCheck>();
var screenMiddle = new Vector2(Screen.width / 2, Screen.height / 2);
m_FakeBaseInput.MousePresent = true;
m_FakeBaseInput.MousePosition = screenMiddle - new Vector2(150, 150);
yield return null;
m_FakeBaseInput.MousePosition = screenMiddle;
yield return null;
Assert.IsTrue(callbackCheck.pointerData.fullyExited == false);
}
[UnityTest]
public IEnumerator PointerEnterChildShouldNotExit_SendPointerEventToParent()
{
m_StandaloneInputModule.sendPointerHoverToParent = true;
PointerExitCallbackCheck callbackCheck = m_Image.gameObject.AddComponent<PointerExitCallbackCheck>();
m_NestedImage.gameObject.AddComponent<PointerExitCallbackCheck>();
var screenMiddle = new Vector2(Screen.width / 2, Screen.height / 2);
m_FakeBaseInput.MousePresent = true;
m_FakeBaseInput.MousePosition = screenMiddle - new Vector2(150, 150);
yield return null;
m_FakeBaseInput.MousePosition = screenMiddle;
yield return null;
Assert.IsTrue(callbackCheck.pointerData == null);
}
[UnityTest]
public IEnumerator PointerEnterChildShouldNotReenter()
{
PointerEnterCallbackCheck callbackCheck = m_NestedImage.gameObject.AddComponent<PointerEnterCallbackCheck>();
var screenMiddle = new Vector2(Screen.width / 2, Screen.height / 2);
m_FakeBaseInput.MousePresent = true;
m_FakeBaseInput.MousePosition = screenMiddle - new Vector2(150, 150);
yield return null;
m_FakeBaseInput.MousePosition = screenMiddle;
yield return null;
Assert.IsTrue(callbackCheck.pointerData.reentered == false);
}
[UnityTest]
public IEnumerator PointerExitChildShouldReenter_NotSendPointerEventToParent()
{
m_StandaloneInputModule.sendPointerHoverToParent = false;
PointerEnterCallbackCheck callbackCheck = m_Image.gameObject.AddComponent<PointerEnterCallbackCheck>();
var screenMiddle = new Vector2(Screen.width / 2, Screen.height / 2);
m_FakeBaseInput.MousePresent = true;
m_FakeBaseInput.MousePosition = screenMiddle - new Vector2(150, 150);
yield return null;
m_FakeBaseInput.MousePosition = screenMiddle;
yield return null;
m_FakeBaseInput.MousePosition = screenMiddle - new Vector2(150, 150);
yield return null;
Assert.IsTrue(callbackCheck.pointerData.reentered == true);
}
[UnityTest]
public IEnumerator PointerExitChildShouldNotSendEnter_SendPointerEventToParent()
{
m_StandaloneInputModule.sendPointerHoverToParent = true;
m_NestedImage.gameObject.AddComponent<PointerEnterCallbackCheck>();
var screenMiddle = new Vector2(Screen.width / 2, Screen.height / 2);
m_FakeBaseInput.MousePresent = true;
m_FakeBaseInput.MousePosition = screenMiddle;
yield return null;
PointerEnterCallbackCheck callbackCheck = m_Image.gameObject.AddComponent<PointerEnterCallbackCheck>();
m_FakeBaseInput.MousePosition = screenMiddle - new Vector2(150, 150);
yield return null;
Assert.IsTrue(callbackCheck.pointerData == null);
}
[UnityTest]
public IEnumerator PointerExitChildShouldFullyExit()
{
PointerExitCallbackCheck callbackCheck = m_NestedImage.gameObject.AddComponent<PointerExitCallbackCheck>();
var screenMiddle = new Vector2(Screen.width / 2, Screen.height / 2);
m_FakeBaseInput.MousePresent = true;
m_FakeBaseInput.MousePosition = screenMiddle - new Vector2(150, 150);
yield return null;
m_FakeBaseInput.MousePosition = screenMiddle;
yield return null;
m_FakeBaseInput.MousePosition = screenMiddle - new Vector2(150, 150);
yield return null;
Assert.IsTrue(callbackCheck.pointerData.fullyExited == true);
}
[UnityTest]
public IEnumerator AllDragsAreReleasedOnLoseFocus()
{
// When the application loses focus, OnEndDrag and OnDrop should be called for all pointers
// involved in an active drag.
var screenMiddle = new Vector2(Screen.width / 2, Screen.height / 2);
// Add scripts to Image which implement OnBeginDrag, OnDrag & OnEndDrag callbacks
var callbackCheckLmb = m_Image.gameObject.AddComponent<DragCallbackCheck>();
callbackCheckLmb.pointerId = -1;
var callbackCheckRmb = m_Image.gameObject.AddComponent<DragCallbackCheck>();
callbackCheckRmb.pointerId = -2;
// Setting required to fake mouse presence
m_FakeBaseInput.MousePresent = true;
// Press both mouse buttons
m_FakeBaseInput.MouseButtonDown[0] = true;
m_FakeBaseInput.MouseButtonDown[1] = true;
m_FakeBaseInput.MousePosition = screenMiddle;
yield return null;
// Reset mouse down events and move pointer somewhere else -> initiates drag
m_FakeBaseInput.MouseButtonDown[0] = false;
m_FakeBaseInput.MouseButtonDown[1] = false;
m_FakeBaseInput.MousePosition = screenMiddle - new Vector2(150, 150);
yield return null;
// We should be mid-drag for both mouse buttons now
Assert.IsTrue(callbackCheckLmb.onBeginDragCalled, "OnBeginDrag not called for LMB");
Assert.IsTrue(callbackCheckRmb.onBeginDragCalled, "OnBeginDrag not called for RMB");
Assert.IsTrue(callbackCheckLmb.onDragCalled, "OnDrag not called for LMB");
Assert.IsTrue(callbackCheckRmb.onDragCalled, "OnDrag not called for RMB");
Assert.IsFalse(callbackCheckLmb.onEndDragCalled, "OnEndDrag called prematurely for LMB");
Assert.IsFalse(callbackCheckRmb.onEndDragCalled, "OnEndDrag called prematurely for RMB");
Assert.IsFalse(callbackCheckLmb.onDropCalled, "OnDrop called prematurely for LMB");
Assert.IsFalse(callbackCheckRmb.onDropCalled, "OnDrop called prematurely for RMB");
// Fake the application losing focus
typeof(EventSystem)
.GetMethod("OnApplicationFocus", BindingFlags.Instance | BindingFlags.NonPublic)
.Invoke(m_EventSystem, new object[] { false });
yield return null;
// Losing focus should have released the drags for both mouse buttons
Assert.IsTrue(callbackCheckLmb.onEndDragCalled, "OnEndDrag not called for LMB");
Assert.IsTrue(callbackCheckRmb.onEndDragCalled, "OnEndDrag not called for RMB");
Assert.IsTrue(callbackCheckLmb.onDropCalled, "OnDrop not called for LMB");
Assert.IsTrue(callbackCheckRmb.onDropCalled, "OnDrop not called for RMB");
}
[TearDown]
public void TearDown()
{
GameObject.DestroyImmediate(m_EventSystem.gameObject);
GameObject.DestroyImmediate(m_Canvas.gameObject);
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e4e290d31ab7fb54880746bb8f818e0d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 7a892c920c8ad2848b469ec9579c5219
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,54 @@
using System;
using UnityEngine;
using UnityEngine.EventSystems;
internal class DragCallbackCheck : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler, IDropHandler, IPointerDownHandler
{
private bool loggedOnDrag = false;
[NonSerialized] public bool onBeginDragCalled = false;
[NonSerialized] public bool onDragCalled = false;
[NonSerialized] public bool onEndDragCalled = false;
[NonSerialized] public bool onDropCalled = false;
[NonSerialized] public int? pointerId = null;
public void OnBeginDrag(PointerEventData eventData)
{
if (pointerId.HasValue && eventData.pointerId != pointerId)
return;
onBeginDragCalled = true;
}
public void OnDrag(PointerEventData eventData)
{
if (pointerId.HasValue && eventData.pointerId != pointerId)
return;
if (loggedOnDrag)
return;
loggedOnDrag = true;
onDragCalled = true;
}
public void OnEndDrag(PointerEventData eventData)
{
if (pointerId.HasValue && eventData.pointerId != pointerId)
return;
onEndDragCalled = true;
}
public void OnDrop(PointerEventData eventData)
{
if (pointerId.HasValue && eventData.pointerId != pointerId)
return;
onDropCalled = true;
}
public void OnPointerDown(PointerEventData eventData)
{
// Empty to ensure we get the drop if we have a pointer handle as well.
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 76c82729ad712f14bae1a8a279c52ac3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,117 @@
using System;
using UnityEngine;
using UnityEngine.EventSystems;
internal class FakeBaseInput : BaseInput
{
[NonSerialized]
public String CompositionString = "";
private IMECompositionMode m_ImeCompositionMode = IMECompositionMode.Auto;
private Vector2 m_CompositionCursorPos = Vector2.zero;
[NonSerialized]
public bool MousePresent = false;
[NonSerialized]
public bool[] MouseButtonDown = new bool[3];
[NonSerialized]
public bool[] MouseButtonUp = new bool[3];
[NonSerialized]
public bool[] MouseButton = new bool[3];
[NonSerialized]
public Vector2 MousePosition = Vector2.zero;
[NonSerialized]
public Vector2 MouseScrollDelta = Vector2.zero;
[NonSerialized]
public bool TouchSupported = false;
[NonSerialized]
public int TouchCount = 0;
[NonSerialized]
public Touch TouchData;
[NonSerialized]
public float AxisRaw = 0f;
[NonSerialized]
public bool ButtonDown = false;
public override string compositionString
{
get { return CompositionString; }
}
public override IMECompositionMode imeCompositionMode
{
get { return m_ImeCompositionMode; }
set { m_ImeCompositionMode = value; }
}
public override Vector2 compositionCursorPos
{
get { return m_CompositionCursorPos; }
set { m_CompositionCursorPos = value; }
}
public override bool mousePresent
{
get { return MousePresent; }
}
public override bool GetMouseButtonDown(int button)
{
return MouseButtonDown[button];
}
public override bool GetMouseButtonUp(int button)
{
return MouseButtonUp[button];
}
public override bool GetMouseButton(int button)
{
return MouseButton[button];
}
public override Vector2 mousePosition
{
get { return MousePosition; }
}
public override Vector2 mouseScrollDelta
{
get { return MouseScrollDelta; }
}
public override bool touchSupported
{
get { return TouchSupported; }
}
public override int touchCount
{
get { return TouchCount; }
}
public override Touch GetTouch(int index)
{
return TouchData;
}
public override float GetAxisRaw(string axisName)
{
return AxisRaw;
}
public override bool GetButtonDown(string buttonName)
{
return ButtonDown;
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 138dbec4f8742654fbceb0a19d68b9c5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,20 @@
using UnityEngine;
internal class MouseUpdate : MonoBehaviour
{
FakeBaseInput m_FakeBaseInput;
void Awake()
{
m_FakeBaseInput = GetComponent<FakeBaseInput>();
}
void Update()
{
Debug.Assert(m_FakeBaseInput, "FakeBaseInput component has not been added to the EventSystem");
// Update mouse position
m_FakeBaseInput.MousePosition.x += 10f;
m_FakeBaseInput.MousePosition.y += 10f;
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 19c6f364c1e81cb4f829a057824639ad
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,13 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
internal class PointerClickCallbackCheck : MonoBehaviour, IPointerDownHandler
{
public bool pointerDown = false;
public void OnPointerDown(PointerEventData eventData)
{
pointerDown = true;
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0573713975c6b8246b7544ed524ef1dc
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,13 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
internal class PointerEnterCallbackCheck : MonoBehaviour, IPointerEnterHandler
{
public PointerEventData pointerData { get; private set; }
public void OnPointerEnter(PointerEventData eventData)
{
pointerData = eventData;
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: eb4fb871430ff484e9ee3850946c504d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,13 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
internal class PointerExitCallbackCheck : MonoBehaviour, IPointerExitHandler
{
public PointerEventData pointerData { get; private set; }
public void OnPointerExit(PointerEventData eventData)
{
pointerData = eventData;
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: af94bd3a805f27e42ab86647265be74e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,149 @@
using NUnit.Framework;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Rendering;
internal class Physics2DRaycasterTests
{
GameObject m_CamGO;
SpriteRenderer m_RedSprite;
SpriteRenderer m_BlueSprite;
SpriteRenderer m_GreenSprite;
EventSystem m_EventSystem;
[SetUp]
public void TestSetup()
{
m_CamGO = new GameObject("Physics2DRaycaster Camera");
m_CamGO.transform.position = new Vector3(0, 0, -10);
m_CamGO.transform.LookAt(Vector3.zero);
var cam = m_CamGO.AddComponent<Camera>();
cam.orthographic = true;
m_CamGO.AddComponent<Physics2DRaycaster>();
m_EventSystem = m_CamGO.AddComponent<EventSystem>();
var texture = new Texture2D(64, 64);
var sprite = Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height), new Vector2(0, 0));
m_RedSprite = CreateTestSprite("Red", Color.red, sprite);
m_BlueSprite = CreateTestSprite("Blue", Color.blue, sprite);
m_GreenSprite = CreateTestSprite("Green", Color.green, sprite);
}
static SpriteRenderer CreateTestSprite(string name, Color color, Sprite sprite)
{
var go = new GameObject(name);
var sr = go.AddComponent<SpriteRenderer>();
sr.sprite = sprite;
sr.color = color;
go.AddComponent<BoxCollider2D>();
return sr;
}
[TearDown]
public void TearDown()
{
Object.DestroyImmediate(m_CamGO);
Object.DestroyImmediate(m_RedSprite.gameObject);
Object.DestroyImmediate(m_BlueSprite.gameObject);
Object.DestroyImmediate(m_GreenSprite.gameObject);
}
static void AssertRaycastResultsOrder(List<RaycastResult> results, params SpriteRenderer[] expectedOrder)
{
Assert.AreEqual(expectedOrder.Length, results.Count);
for (int i = 0; i < expectedOrder.Length; ++i)
{
Assert.AreSame(expectedOrder[i].gameObject, results[i].gameObject, "Expected {0} at index {1} but got {2}", expectedOrder[i], i, results[i].gameObject);
}
}
List<RaycastResult> PerformRaycast()
{
var results = new List<RaycastResult>();
var pointerEvent = new PointerEventData(m_EventSystem)
{
position = new Vector2(Screen.width / 2f, Screen.height / 2f)
};
m_EventSystem.RaycastAll(pointerEvent, results);
return results;
}
[Test]
public void RaycastAllResultsAreSortedByRendererSortingOrder()
{
m_RedSprite.sortingOrder = -10;
m_BlueSprite.sortingOrder = 0;
m_GreenSprite.sortingOrder = 5;
var results = PerformRaycast();
AssertRaycastResultsOrder(results, m_GreenSprite, m_BlueSprite, m_RedSprite);
}
[Test]
public void RaycastAllResultsAreSortedBySortGroupOrder()
{
var blueSg = m_BlueSprite.gameObject.AddComponent<SortingGroup>();
blueSg.sortingLayerID = 0;
blueSg.sortingOrder = -10;
var redSg = m_RedSprite.gameObject.AddComponent<SortingGroup>();
redSg.sortingLayerID = 0;
redSg.sortingOrder = 10;
SortingGroup.UpdateAllSortingGroups();
var results = PerformRaycast();
AssertRaycastResultsOrder(results, m_RedSprite, m_GreenSprite, m_BlueSprite);
}
[Test]
public void RaycastAllResultsAreSortedBySortGroupOrderAndSortingOrder()
{
m_RedSprite.sortingOrder = -10;
m_BlueSprite.sortingOrder = 0;
m_GreenSprite.sortingOrder = 5;
var sg = m_BlueSprite.gameObject.AddComponent<SortingGroup>();
sg.sortingLayerID = 0;
sg.sortingOrder = 100;
SortingGroup.UpdateAllSortingGroups();
var results = PerformRaycast();
AssertRaycastResultsOrder(results, m_BlueSprite, m_GreenSprite, m_RedSprite);
}
[Test]
public void RaycastAllResultsAreSortedBySortGroupDistanceAlongRay()
{
// Ensure we use the distance along the raycast to sort the results
var sortingGroupRed = new GameObject("Sorting Group Red", typeof(SortingGroup));
var sortingGroupBlue = new GameObject("Sorting Group Blue", typeof(SortingGroup));
var sortingGroupGreen = new GameObject("Sorting Group Green", typeof(SortingGroup));
sortingGroupRed.transform.position = new Vector3(0, 0, -1);
sortingGroupBlue.transform.position = new Vector3(1000, 1000, 0);
sortingGroupGreen.transform.position = new Vector3(0, 0, 1);
m_RedSprite.transform.SetParent(sortingGroupRed.transform, true);
m_BlueSprite.transform.SetParent(sortingGroupBlue.transform, true);
m_GreenSprite.transform.SetParent(sortingGroupGreen.transform, true);
SortingGroup.UpdateAllSortingGroups();
var results = PerformRaycast();
AssertRaycastResultsOrder(results, m_RedSprite, m_BlueSprite, m_GreenSprite);
// Unparent so they can be destroyed by the TearDown
m_RedSprite.transform.parent = null;
m_BlueSprite.transform.parent = null;
m_GreenSprite.transform.parent = null;
// Cleanup
Object.DestroyImmediate(sortingGroupRed);
Object.DestroyImmediate(sortingGroupBlue);
Object.DestroyImmediate(sortingGroupGreen);
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6fb761a492f57d84586a8264df7b3cc0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,46 @@
using UnityEngine;
using NUnit.Framework;
using System.Collections.Generic;
using UnityEngine.EventSystems;
internal class PhysicsRaycasterTests
{
GameObject m_CamGO;
GameObject m_Collider;
[SetUp]
public void TestSetup()
{
m_CamGO = new GameObject("PhysicsRaycaster Camera");
m_Collider = GameObject.CreatePrimitive(PrimitiveType.Cube);
}
[Test]
public void PhysicsRaycasterDoesNotCastOutsideCameraViewRect()
{
m_CamGO.transform.position = new Vector3(0, 0, -10);
m_CamGO.transform.LookAt(Vector3.zero);
var cam = m_CamGO.AddComponent<Camera>();
cam.rect = new Rect(0.5f, 0, 0.5f, 1);
m_CamGO.AddComponent<PhysicsRaycaster>();
var eventSystem = m_CamGO.AddComponent<EventSystem>();
// Create an object that will be hit if a raycast does occur.
m_Collider.transform.localScale = new Vector3(100, 100, 1);
List<RaycastResult> results = new List<RaycastResult>();
var pointerEvent = new PointerEventData(eventSystem)
{
position = new Vector2(0, 0) // Raycast from the left side of the screen which is outside of the camera's view rect.
};
eventSystem.RaycastAll(pointerEvent, results);
Assert.IsEmpty(results, "Expected no results from a raycast that is outside of the camera's viewport.");
}
[TearDown]
public void TearDown()
{
GameObject.DestroyImmediate(m_CamGO);
GameObject.DestroyImmediate(m_Collider);
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 833143c443f979e44ae0b8ed899e3b59
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,126 @@
using System.Collections;
using System.Collections.Generic;
using System.IO;
using NUnit.Framework;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.TestTools;
using UnityEngine.UI;
#if UNITY_EDITOR
using UnityEditor;
using UnityEditor.EventSystems;
#endif
internal class RaycastSortingTests : IPrebuildSetup
{
// Test to check that a a raycast over two canvases will not use hierarchal depth to compare two results
// from different canvases (case 912396 - Raycast hits ignores 2nd Canvas which is drawn in front)
GameObject m_PrefabRoot;
const string kPrefabPath = "Assets/Resources/RaycastSortingPrefab.prefab";
void IPrebuildSetup.Setup()
{
#if UNITY_EDITOR
var rootGO = new GameObject("RootGO");
var cameraGO = new GameObject("Camera", typeof(Camera));
var camera = cameraGO.GetComponent<Camera>();
cameraGO.transform.SetParent(rootGO.transform);
var eventSystemGO = new GameObject("EventSystem", typeof(EventSystem));
InputModuleComponentFactory.AddInputModule(eventSystemGO);
eventSystemGO.transform.SetParent(rootGO.transform);
var backCanvasGO = new GameObject("BackCanvas", typeof(Canvas), typeof(GraphicRaycaster));
backCanvasGO.transform.SetParent(rootGO.transform);
var backCanvas = backCanvasGO.GetComponent<Canvas>();
backCanvas.renderMode = RenderMode.ScreenSpaceCamera;
backCanvas.planeDistance = 100;
backCanvas.worldCamera = camera;
var backCanvasBackground = new GameObject("BackCanvasBackground", typeof(RectTransform), typeof(Image));
var backCanvasBackgroundTransform = backCanvasBackground.GetComponent<RectTransform>();
backCanvasBackgroundTransform.SetParent(backCanvasGO.transform);
backCanvasBackgroundTransform.anchorMin = Vector2.zero;
backCanvasBackgroundTransform.anchorMax = Vector2.one;
backCanvasBackgroundTransform.sizeDelta = Vector2.zero;
backCanvasBackgroundTransform.anchoredPosition3D = Vector3.zero;
backCanvasBackgroundTransform.localScale = Vector3.one;
var backCanvasDeeper = new GameObject("BackCanvasDeeperHierarchy", typeof(RectTransform), typeof(Image));
var backCanvasDeeperTransform = backCanvasDeeper.GetComponent<RectTransform>();
backCanvasDeeperTransform.SetParent(backCanvasBackgroundTransform);
backCanvasDeeperTransform.anchorMin = new Vector2(0.5f, 0);
backCanvasDeeperTransform.anchorMax = Vector2.one;
backCanvasDeeperTransform.sizeDelta = Vector2.zero;
backCanvasDeeperTransform.anchoredPosition3D = Vector3.zero;
backCanvasDeeperTransform.localScale = Vector3.one;
backCanvasDeeper.GetComponent<Image>().color = new Color(0.6985294f, 0.7754564f, 1f);
var frontCanvasGO = new GameObject("FrontCanvas", typeof(Canvas), typeof(GraphicRaycaster));
frontCanvasGO.transform.SetParent(rootGO.transform);
var frontCanvas = frontCanvasGO.GetComponent<Canvas>();
frontCanvas.renderMode = RenderMode.ScreenSpaceCamera;
frontCanvas.planeDistance = 50;
frontCanvas.worldCamera = camera;
var frontCanvasTopLevel = new GameObject("FrontCanvasTopLevel", typeof(RectTransform), typeof(Text));
var frontCanvasTopLevelTransform = frontCanvasTopLevel.GetComponent<RectTransform>();
frontCanvasTopLevelTransform.SetParent(frontCanvasGO.transform);
frontCanvasTopLevelTransform.anchorMin = Vector2.zero;
frontCanvasTopLevelTransform.anchorMax = new Vector2(1, 0.5f);
frontCanvasTopLevelTransform.sizeDelta = Vector2.zero;
frontCanvasTopLevelTransform.anchoredPosition3D = Vector3.zero;
frontCanvasTopLevelTransform.localScale = Vector3.one;
var text = frontCanvasTopLevel.GetComponent<Text>();
text.text = "FrontCanvasTopLevel";
text.color = Color.black;
text.fontSize = 97;
if (!Directory.Exists("Assets/Resources/"))
Directory.CreateDirectory("Assets/Resources/");
UnityEditor.PrefabUtility.SaveAsPrefabAsset(rootGO, kPrefabPath);
GameObject.DestroyImmediate(rootGO);
#endif
}
[SetUp]
public void TestSetup()
{
m_PrefabRoot = Object.Instantiate(Resources.Load("RaycastSortingPrefab")) as GameObject;
}
[UnityTest]
public IEnumerator RaycastResult_Sorting()
{
Camera cam = m_PrefabRoot.GetComponentInChildren<Camera>();
EventSystem eventSystem = m_PrefabRoot.GetComponentInChildren<EventSystem>();
GameObject shouldHit = m_PrefabRoot.GetComponentInChildren<Text>().gameObject;
PointerEventData eventData = new PointerEventData(eventSystem);
//bottom left quadrant
eventData.position = cam.ViewportToScreenPoint(new Vector3(0.75f, 0.25f));
List<RaycastResult> results = new List<RaycastResult>();
eventSystem.RaycastAll(eventData, results);
Assert.IsTrue(results[0].gameObject.name == shouldHit.name);
yield return null;
}
[TearDown]
public void TearDown()
{
Object.DestroyImmediate(m_PrefabRoot);
}
[OneTimeTearDown]
public void OneTimeTearDown()
{
#if UNITY_EDITOR
AssetDatabase.DeleteAsset(kPrefabPath);
#endif
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0ca2545d76d1fb34fa45a9f1e432d259
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,468 @@
using System.Reflection;
using System.Collections;
using NUnit.Framework;
using UnityEngine.EventSystems;
using UnityEngine.TestTools;
namespace UnityEngine.UI.Tests
{
[TestFixture]
class SelectableTests
{
private class SelectableTest : Selectable
{
public bool isStateNormal { get { return currentSelectionState == SelectionState.Normal; } }
public bool isStateHighlighted { get { return currentSelectionState == SelectionState.Highlighted; } }
public bool isStateSelected { get { return currentSelectionState == SelectionState.Selected; } }
public bool isStatePressed { get { return currentSelectionState == SelectionState.Pressed; } }
public bool isStateDisabled { get { return currentSelectionState == SelectionState.Disabled; } }
public Selectable GetSelectableAtIndex(int index)
{
return s_Selectables[index];
}
public int GetSelectableCurrentIndex()
{
return m_CurrentIndex;
}
}
private SelectableTest selectable;
private GameObject m_CanvasRoot;
private GameObject m_EventSystemGO;
private CanvasGroup CreateAndParentGroupTo(string name, GameObject child)
{
GameObject canvasRoot = new GameObject("Canvas", typeof(RectTransform), typeof(Canvas));
GameObject groupGO = new GameObject(name, typeof(RectTransform), typeof(CanvasGroup));
groupGO.transform.SetParent(canvasRoot.transform);
child.transform.SetParent(groupGO.transform);
return groupGO.GetComponent<CanvasGroup>();
}
[SetUp]
public void TestSetup()
{
m_EventSystemGO = new GameObject("EventSystem", typeof(EventSystem));
EventSystem.current = m_EventSystemGO.GetComponent<EventSystem>();
m_CanvasRoot = new GameObject("Canvas", typeof(RectTransform), typeof(Canvas));
GameObject SelectableGO = new GameObject("Selectable", typeof(RectTransform), typeof(CanvasRenderer));
SelectableGO.transform.SetParent(m_CanvasRoot.transform);
selectable = SelectableGO.AddComponent<SelectableTest>();
selectable.targetGraphic = selectable.gameObject.AddComponent<ConcreteGraphic>();
}
[TearDown]
public void TearDown()
{
GameObject.DestroyImmediate(m_CanvasRoot);
GameObject.DestroyImmediate(m_EventSystemGO);
}
[Test] // regression test 1160054
public void SelectableArrayRemovesReferenceUponDisable()
{
int originalSelectableCount = Selectable.allSelectableCount;
selectable.enabled = false;
Assert.AreEqual(originalSelectableCount - 1, Selectable.allSelectableCount, "We have more then originalSelectableCount - 1 selectable objects.");
//ensure the item as the last index is nulled out as it replaced the item that was disabled.
Assert.IsNull(selectable.GetSelectableAtIndex(Selectable.allSelectableCount));
selectable.enabled = true;
}
#region Selected object
[Test]
public void SettingCurrentSelectedSelectableNonInteractableShouldNullifyCurrentSelected()
{
EventSystem.current.SetSelectedGameObject(selectable.gameObject);
selectable.interactable = false;
// it should be unselected now that it is not interactable anymore
Assert.IsNull(EventSystem.current.currentSelectedGameObject);
}
[Test]
public void PointerEnterDownShouldMakeItSelectedGameObject()
{
Assert.IsNull(EventSystem.current.currentSelectedGameObject);
selectable.InvokeOnPointerEnter(new PointerEventData(EventSystem.current));
selectable.InvokeOnPointerDown(new PointerEventData(EventSystem.current));
Assert.AreEqual(selectable.gameObject, EventSystem.current.currentSelectedGameObject);
}
[Test]
public void OnSelectShouldSetSelectedState()
{
Assert.True(selectable.isStateNormal);
selectable.OnSelect(new BaseEventData(EventSystem.current));
Assert.True(selectable.isStateSelected);
}
[Test]
public void OnDeselectShouldUnsetSelectedState()
{
Assert.True(selectable.isStateNormal);
selectable.OnSelect(new BaseEventData(EventSystem.current));
Assert.True(selectable.isStateSelected);
selectable.OnDeselect(new BaseEventData(EventSystem.current));
Assert.True(selectable.isStateNormal);
}
#endregion
#region Interactable
[Test]
public void SettingCanvasGroupNotInteractableShouldMakeSelectableNotInteractable()
{
// Canvas Group on same object
var group = selectable.gameObject.AddComponent<CanvasGroup>();
Assert.IsTrue(selectable.IsInteractable());
group.interactable = false;
// actual call happens on the native side, cause by interactable = false
selectable.InvokeOnCanvasGroupChanged();
Assert.IsFalse(selectable.IsInteractable());
}
[Test]
public void DisablingCanvasGroupShouldMakeSelectableAsInteractable()
{
var group = selectable.gameObject.AddComponent<CanvasGroup>();
Assert.IsTrue(selectable.IsInteractable());
group.enabled = false;
group.interactable = false;
selectable.InvokeOnCanvasGroupChanged();
Assert.IsTrue(selectable.IsInteractable());
}
[Test]
public void SettingParentCanvasGroupNotInteractableShouldMakeSelectableNotInteractable()
{
var canvasGroup = CreateAndParentGroupTo("CanvasGroup", selectable.gameObject);
Assert.IsTrue(selectable.IsInteractable());
canvasGroup.interactable = false;
// actual call happens on the native side, cause by interactable = false
selectable.InvokeOnCanvasGroupChanged();
Assert.IsFalse(selectable.IsInteractable());
}
[Test]
public void SettingParentParentCanvasGroupNotInteractableShouldMakeSelectableNotInteractable()
{
var canvasGroup1 = CreateAndParentGroupTo("CanvasGroup1", selectable.gameObject);
var canvasGroup2 = CreateAndParentGroupTo("CanvasGroup2", canvasGroup1.gameObject);
Assert.IsTrue(selectable.IsInteractable());
canvasGroup2.interactable = false;
// actual call happens on the native side, cause by interactable = false
selectable.InvokeOnCanvasGroupChanged();
Assert.IsFalse(selectable.IsInteractable());
}
[Test]
public void SettingParentParentCanvasGroupInteractableShouldMakeSelectableInteractable()
{
var canvasGroup1 = CreateAndParentGroupTo("CanvasGroup1", selectable.gameObject);
CreateAndParentGroupTo("CanvasGroup2", canvasGroup1.gameObject);
Assert.IsTrue(selectable.IsInteractable());
// actual call happens on the native side, cause by interactable
selectable.InvokeOnCanvasGroupChanged();
Assert.IsTrue(selectable.IsInteractable());
}
[Test]
public void SettingParentParentCanvasGroupNotInteractableShouldNotMakeSelectableNotInteractableIfIgnoreParentGroups()
{
var canvasGroup1 = CreateAndParentGroupTo("CanvasGroup1", selectable.gameObject);
canvasGroup1.ignoreParentGroups = true;
var canvasGroup2 = CreateAndParentGroupTo("CanvasGroup2", canvasGroup1.gameObject);
Assert.IsTrue(selectable.IsInteractable());
canvasGroup2.interactable = false;
// actual call happens on the native side, cause by interactable = false
selectable.InvokeOnCanvasGroupChanged();
Assert.IsTrue(selectable.IsInteractable());
}
[Test]// regression test 861736
public void PointerEnterThenSetNotInteractableThenExitThenSetInteractableShouldSetStateToDefault()
{
Assert.True(selectable.isStateNormal);
selectable.InvokeOnPointerEnter(new PointerEventData(EventSystem.current)
{
pointerEnter = selectable.gameObject
});
Assert.True(selectable.isStateHighlighted);
selectable.interactable = false;
selectable.InvokeOnPointerExit(new PointerEventData(EventSystem.current)
{
pointerEnter = selectable.gameObject
});
selectable.interactable = true;
Assert.False(selectable.isStateHighlighted);
Assert.True(selectable.isStateNormal);
}
[Test]// regression test 861736
public void PointerEnterThenSetNotInteractableThenSetInteractableShouldStayHighlighted()
{
Assert.True(selectable.isStateNormal);
selectable.InvokeOnPointerEnter(new PointerEventData(EventSystem.current)
{
pointerEnter = selectable.gameObject
});
Assert.True(selectable.isStateHighlighted);
selectable.interactable = false;
selectable.interactable = true;
Assert.True(selectable.isStateHighlighted);
}
[Test]
public void InstantiatingSelectableUnderNotInteractableCanvasGroupShouldAlsoNotBeInteractable()
{
var canvasGroup = CreateAndParentGroupTo("ParentGroup", selectable.gameObject);
canvasGroup.interactable = false;
Assert.False(canvasGroup.interactable);
var newSelectable = Object.Instantiate(selectable.gameObject, canvasGroup.transform).GetComponent<SelectableTest>();
Assert.False(newSelectable.IsInteractable());
}
#endregion
#region Tweening
[UnityTest]
public IEnumerator SettingNotInteractableShouldTweenToDisabledColor()
{
var canvasRenderer = selectable.gameObject.GetComponent<CanvasRenderer>();
selectable.InvokeOnEnable();
canvasRenderer.SetColor(selectable.colors.normalColor);
selectable.interactable = false;
yield return new WaitForSeconds(1);
Assert.AreEqual(selectable.colors.disabledColor, canvasRenderer.GetColor());
selectable.interactable = true;
yield return new WaitForSeconds(1);
Assert.AreEqual(selectable.colors.normalColor, canvasRenderer.GetColor());
}
[UnityTest]
public IEnumerator SettingInteractableToFalseTrueFalseShouldTweenToDisabledColor()
{
var canvasRenderer = selectable.gameObject.GetComponent<CanvasRenderer>();
selectable.InvokeOnEnable();
canvasRenderer.SetColor(selectable.colors.normalColor);
selectable.interactable = false;
selectable.interactable = true;
selectable.interactable = false;
yield return new WaitForSeconds(1);
Assert.AreEqual(selectable.colors.disabledColor, canvasRenderer.GetColor());
}
#if PACKAGE_ANIMATION
[Test]
public void TriggerAnimationWithNoAnimator()
{
Assert.Null(selectable.animator);
Assert.DoesNotThrow(() => selectable.InvokeTriggerAnimation("asdasd"));
}
[Test]
public void TriggerAnimationWithDisabledAnimator()
{
var an = selectable.gameObject.AddComponent<Animator>();
an.enabled = false;
Assert.NotNull(selectable.animator);
Assert.DoesNotThrow(() => selectable.InvokeTriggerAnimation("asdasd"));
}
[Test]
public void TriggerAnimationAnimatorWithNoRuntimeController()
{
var an = selectable.gameObject.AddComponent<Animator>();
an.runtimeAnimatorController = null;
Assert.NotNull(selectable.animator);
Assert.DoesNotThrow(() => selectable.InvokeTriggerAnimation("asdasd"));
}
#endif
#endregion
#region Selection state and pointer
[Test]
public void SelectShouldSetSelectedObject()
{
Assert.Null(EventSystem.current.currentSelectedGameObject);
selectable.Select();
Assert.AreEqual(selectable.gameObject, EventSystem.current.currentSelectedGameObject);
}
[Test]
public void SelectWhenAlreadySelectingShouldNotSetSelectedObject()
{
Assert.Null(EventSystem.current.currentSelectedGameObject);
var fieldInfo = typeof(EventSystem).GetField("m_SelectionGuard", BindingFlags.NonPublic | BindingFlags.Instance);
fieldInfo
.SetValue(EventSystem.current, true);
selectable.Select();
Assert.Null(EventSystem.current.currentSelectedGameObject);
}
[Test]
public void PointerEnterShouldHighlight()
{
Assert.True(selectable.isStateNormal);
selectable.InvokeOnPointerEnter(new PointerEventData(EventSystem.current)
{
pointerEnter = selectable.gameObject
});
Assert.True(selectable.isStateHighlighted);
}
[Test]
public void PointerEnterOnSelectedObjectShouldStaySelected()
{
selectable.InvokeOnSelect(null);
Assert.True(selectable.isStateSelected);
selectable.InvokeOnPointerEnter(new PointerEventData(EventSystem.current)
{
pointerEnter = selectable.gameObject
});
Assert.True(selectable.isStateSelected);
}
[Test]
public void PointerEnterAndRightClickShouldHighlightNotPress()
{
Assert.True(selectable.isStateNormal);
selectable.InvokeOnPointerEnter(new PointerEventData(EventSystem.current)
{
pointerEnter = selectable.gameObject
});
selectable.InvokeOnPointerDown(new PointerEventData(EventSystem.current)
{
button = PointerEventData.InputButton.Right
});
Assert.True(selectable.isStateHighlighted);
}
[Test]
public void PointerEnterAndRightClickShouldPress()
{
Assert.True(selectable.isStateNormal);
selectable.InvokeOnPointerEnter(new PointerEventData(EventSystem.current)
{
pointerEnter = selectable.gameObject
});
selectable.InvokeOnPointerDown(new PointerEventData(EventSystem.current));
Assert.True(selectable.isStatePressed);
}
[Test]
public void PointerEnterLeftClickExitShouldPress()
{
Assert.True(selectable.isStateNormal);
selectable.InvokeOnPointerEnter(new PointerEventData(EventSystem.current)
{
pointerEnter = selectable.gameObject
});
selectable.InvokeOnPointerDown(new PointerEventData(EventSystem.current));
selectable.InvokeOnPointerExit(new PointerEventData(EventSystem.current)
{
pointerEnter = selectable.gameObject
});
Assert.True(selectable.isStatePressed);
}
[Test]
public void PointerEnterLeftClickExitReleaseShouldSelect()
{
Assert.True(selectable.isStateNormal);
selectable.InvokeOnPointerEnter(new PointerEventData(EventSystem.current)
{
pointerEnter = selectable.gameObject
});
selectable.InvokeOnPointerDown(new PointerEventData(EventSystem.current));
selectable.InvokeOnPointerExit(new PointerEventData(EventSystem.current)
{
pointerEnter = selectable.gameObject
});
selectable.InvokeOnPointerUp(new PointerEventData(EventSystem.current));
Assert.True(selectable.isStateSelected);
}
[Test]
public void PointerDownShouldSetSelectedObject()
{
Assert.Null(EventSystem.current.currentSelectedGameObject);
selectable.InvokeOnPointerDown(new PointerEventData(EventSystem.current));
Assert.AreEqual(selectable.gameObject, EventSystem.current.currentSelectedGameObject);
}
[Test]
public void PointerLeftDownRightDownRightUpShouldNotChangeState()
{
Assert.True(selectable.isStateNormal);
selectable.InvokeOnPointerEnter(new PointerEventData(EventSystem.current)
{
pointerEnter = selectable.gameObject
});
selectable.InvokeOnPointerDown(new PointerEventData(EventSystem.current) { button = PointerEventData.InputButton.Left });
selectable.InvokeOnPointerDown(new PointerEventData(EventSystem.current) { button = PointerEventData.InputButton.Right });
selectable.InvokeOnPointerUp(new PointerEventData(EventSystem.current) { button = PointerEventData.InputButton.Right });
Assert.True(selectable.isStatePressed);
}
#endregion
#region No event system
[Test] // regression test 787563
public void SettingInteractableWithNoEventSystemShouldNotCrash()
{
EventSystem.current.enabled = false;
selectable.interactable = false;
}
[Test] // regression test 787563
public void OnPointerDownWithNoEventSystemShouldNotCrash()
{
EventSystem.current.enabled = false;
selectable.OnPointerDown(new PointerEventData(EventSystem.current) {button = PointerEventData.InputButton.Left});
}
[Test] // regression test 787563
public void SelectWithNoEventSystemShouldNotCrash()
{
EventSystem.current.enabled = false;
selectable.Select();
}
#endregion
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 39dcddcb5895328489c92214aa73e3bb
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 8313ea704470a264295ec9e09aec6ebc
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,264 @@
using System.Collections.Generic;
using System.IO;
using NUnit.Framework;
using UnityEditor;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.TestTools;
using UnityEngine.UI;
using UnityEngine.UI.Tests;
namespace Graphics
{
class GraphicTests : IPrebuildSetup
{
private GameObject m_PrefabRoot;
private ConcreteGraphic m_graphic;
private Canvas m_canvas;
const string kPrefabPath = "Assets/Resources/GraphicTestsPrefab.prefab";
bool m_dirtyVert;
bool m_dirtyLayout;
bool m_dirtyMaterial;
public void Setup()
{
#if UNITY_EDITOR
var rootGO = new GameObject("rootGo");
var canvasGO = new GameObject("CanvasRoot", typeof(Canvas), typeof(GraphicRaycaster));
canvasGO.transform.SetParent(rootGO.transform);
var graphicGO = new GameObject("Graphic", typeof(RectTransform), typeof(ConcreteGraphic));
graphicGO.transform.SetParent(canvasGO.transform);
var gameObject = new GameObject("EventSystem", typeof(EventSystem));
gameObject.transform.SetParent(rootGO.transform);
if (!Directory.Exists("Assets/Resources/"))
Directory.CreateDirectory("Assets/Resources/");
PrefabUtility.SaveAsPrefabAsset(rootGO, kPrefabPath);
GameObject.DestroyImmediate(rootGO);
#endif
}
[SetUp]
public void TestSetup()
{
m_PrefabRoot = Object.Instantiate(Resources.Load("GraphicTestsPrefab")) as GameObject;
m_canvas = m_PrefabRoot.GetComponentInChildren<Canvas>();
m_graphic = m_PrefabRoot.GetComponentInChildren<ConcreteGraphic>();
m_graphic.RegisterDirtyVerticesCallback(() => m_dirtyVert = true);
m_graphic.RegisterDirtyLayoutCallback(() => m_dirtyLayout = true);
m_graphic.RegisterDirtyMaterialCallback(() => m_dirtyMaterial = true);
ResetDirtyFlags();
}
[TearDown]
public void TearDown()
{
m_graphic = null;
m_canvas = null;
GameObject.DestroyImmediate(m_PrefabRoot);
}
[OneTimeTearDown]
public void OneTimeTearDown()
{
#if UNITY_EDITOR
AssetDatabase.DeleteAsset(kPrefabPath);
#endif
}
private void ResetDirtyFlags()
{
m_dirtyVert = m_dirtyLayout = m_dirtyMaterial = false;
}
[Test]
public void SettingDirtyOnActiveGraphicCallsCallbacks()
{
m_graphic.enabled = false;
m_graphic.enabled = true;
m_graphic.SetAllDirty();
Assert.True(m_dirtyVert, "Vertices have not been dirtied");
Assert.True(m_dirtyLayout, "Layout has not been dirtied");
Assert.True(m_dirtyMaterial, "Material has not been dirtied");
ResetDirtyFlags();
m_graphic.SetLayoutDirty();
Assert.True(m_dirtyLayout, "Layout has not been dirtied");
m_graphic.SetVerticesDirty();
Assert.True(m_dirtyVert, "Vertices have not been dirtied");
m_graphic.SetMaterialDirty();
Assert.True(m_dirtyMaterial, "Material has not been dirtied");
}
private bool ContainsGraphic(IList<Graphic> array, int size, Graphic element)
{
for (int i = 0; i < size; ++i)
{
if (array[i] == element)
return true;
}
return false;
}
private void RefreshGraphicByRaycast()
{
// force refresh by raycast
List<RaycastResult> raycastResultCache = new List<RaycastResult>();
PointerEventData data = new PointerEventData(EventSystem.current);
var raycaster = m_canvas.GetComponent<GraphicRaycaster>();
raycaster.Raycast(data, raycastResultCache);
}
private bool CheckGraphicAddedToGraphicRegistry()
{
var graphicList = GraphicRegistry.GetGraphicsForCanvas(m_canvas);
var graphicListSize = graphicList.Count;
return ContainsGraphic(graphicList, graphicListSize, m_graphic);
}
private bool CheckGraphicAddedToRaycastGraphicRegistry()
{
var graphicList = GraphicRegistry.GetRaycastableGraphicsForCanvas(m_canvas);
var graphicListSize = graphicList.Count;
return ContainsGraphic(graphicList, graphicListSize, m_graphic);
}
private void CheckGraphicRaycastDisableValidity()
{
RefreshGraphicByRaycast();
Assert.False(CheckGraphicAddedToGraphicRegistry(), "Graphic should no longer be registered in m_CanvasGraphics");
Assert.False(CheckGraphicAddedToRaycastGraphicRegistry(), "Graphic should no longer be registered in m_RaycastableGraphics");
}
[Test]
public void OnEnableLeavesGraphicInExpectedState()
{
m_graphic.enabled = false;
m_graphic.enabled = true; // on enable is called directly
Assert.True(CheckGraphicAddedToGraphicRegistry(), "Graphic should be registered in m_CanvasGraphics");
Assert.True(CheckGraphicAddedToRaycastGraphicRegistry(), "Graphic should be registered in m_RaycastableGraphics");
Assert.AreEqual(Texture2D.whiteTexture, m_graphic.mainTexture, "mainTexture should be Texture2D.whiteTexture");
Assert.NotNull(m_graphic.canvas);
Assert.True(m_dirtyVert, "Vertices have not been dirtied");
Assert.True(m_dirtyLayout, "Layout has not been dirtied");
Assert.True(m_dirtyMaterial, "Material has not been dirtied");
}
[Test]
public void OnEnableTwiceLeavesGraphicInExpectedState()
{
m_graphic.enabled = true;
// force onEnable by reflection to call it second time
m_graphic.InvokeOnEnable();
m_graphic.enabled = false;
CheckGraphicRaycastDisableValidity();
}
[Test]
public void OnDisableLeavesGraphicInExpectedState()
{
m_graphic.enabled = true;
m_graphic.enabled = false;
CheckGraphicRaycastDisableValidity();
}
[Test]
public void OnPopulateMeshWorksAsExpected()
{
m_graphic.rectTransform.anchoredPosition = new Vector2(100, 100);
m_graphic.rectTransform.sizeDelta = new Vector2(150, 742);
m_graphic.color = new Color(50, 100, 150, 200);
VertexHelper vh = new VertexHelper();
m_graphic.InvokeOnPopulateMesh(vh);
GraphicTestHelper.TestOnPopulateMeshDefaultBehavior(m_graphic, vh);
}
[Test]
public void OnDidApplyAnimationPropertiesSetsAllDirty()
{
m_graphic.enabled = true; // Usually set through SetEnabled, from Behavior
m_graphic.InvokeOnDidApplyAnimationProperties();
Assert.True(m_dirtyVert, "Vertices have not been dirtied");
Assert.True(m_dirtyLayout, "Layout has not been dirtied");
Assert.True(m_dirtyMaterial, "Material has not been dirtied");
}
[Test]
public void MakingGraphicNonRaycastableRemovesGraphicFromProperLists()
{
Assert.True(CheckGraphicAddedToGraphicRegistry(), "Graphic should be registered in m_CanvasGraphics");
Assert.True(CheckGraphicAddedToRaycastGraphicRegistry(), "Graphic should be registered in m_RaycastableGraphics");
m_graphic.raycastTarget = false;
Assert.True(CheckGraphicAddedToGraphicRegistry(), "Graphic should be registered in m_CanvasGraphics");
Assert.False(CheckGraphicAddedToRaycastGraphicRegistry(), "Graphic should no longer be registered in m_RaycastableGraphics");
}
[Test]
public void OnEnableLeavesNonRaycastGraphicInExpectedState()
{
m_graphic.enabled = false;
m_graphic.raycastTarget = false;
m_graphic.enabled = true; // on enable is called directly
Assert.True(CheckGraphicAddedToGraphicRegistry(), "Graphic should be registered in m_CanvasGraphics");
Assert.False(CheckGraphicAddedToRaycastGraphicRegistry(), "Graphic should no longer be registered in m_RaycastableGraphics");
Assert.AreEqual(Texture2D.whiteTexture, m_graphic.mainTexture, "mainTexture should be Texture2D.whiteTexture");
Assert.NotNull(m_graphic.canvas);
Assert.True(m_dirtyVert, "Vertices have not been dirtied");
Assert.True(m_dirtyLayout, "Layout has not been dirtied");
Assert.True(m_dirtyMaterial, "Material has not been dirtied");
}
[Test]
public void SettingRaycastTargetOnDisabledGraphicDoesntAddItRaycastList()
{
Assert.True(CheckGraphicAddedToGraphicRegistry(), "Graphic should be registered in m_CanvasGraphics");
Assert.True(CheckGraphicAddedToRaycastGraphicRegistry(), "Graphic should be registered in m_RaycastableGraphics");
m_graphic.raycastTarget = false;
Assert.True(CheckGraphicAddedToGraphicRegistry(), "Graphic should be registered in m_CanvasGraphics");
Assert.False(CheckGraphicAddedToRaycastGraphicRegistry(), "Graphic should no longer be registered in m_RaycastableGraphics");
m_graphic.enabled = false;
Assert.False(CheckGraphicAddedToGraphicRegistry(), "Graphic should NOT be registered in m_CanvasGraphics");
Assert.False(CheckGraphicAddedToRaycastGraphicRegistry(), "Graphic should no longer be registered in m_RaycastableGraphics");
m_graphic.raycastTarget = true;
Assert.False(CheckGraphicAddedToGraphicRegistry(), "Graphic should NOT be registered in m_CanvasGraphics");
Assert.False(CheckGraphicAddedToRaycastGraphicRegistry(), "Graphic should no longer be registered in m_RaycastableGraphics");
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e88a766c2b8b47841936c136f4afbba9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,662 @@
using NUnit.Framework;
using System;
using System.Collections.Generic;
namespace UnityEngine.UI.Tests
{
[TestFixture]
internal class ImageTests
{
Image m_Image;
Sprite m_Sprite;
Sprite m_OverrideSprite;
readonly Texture2D texture = new Texture2D(128, 128);
readonly Texture2D overrideTexture = new Texture2D(512, 512);
bool m_dirtyVert;
bool m_dirtyLayout;
bool m_dirtyMaterial;
Camera m_camera;
Canvas m_CanvasRoot;
[SetUp]
public void TestSetup()
{
var canvasGO = new GameObject("Canvas", typeof(RectTransform), typeof(Canvas));
m_CanvasRoot = canvasGO.GetComponent<Canvas>();
GameObject gameObject = new GameObject("Image", typeof(RectTransform), typeof(Image));
gameObject.transform.SetParent(m_CanvasRoot.transform);
m_Image = gameObject.GetComponent<Image>();
m_camera = new GameObject("Camera", typeof(Camera)).GetComponent<Camera>();
Color[] colors = new Color[128 * 128];
for (int y = 0; y < 128; y++)
for (int x = 0; x < 128; x++)
colors[x + 128 * y] = new Color(0, 0, 0, 1 - x / 128f);
texture.SetPixels(colors);
texture.Apply();
Color[] overrideColors = new Color[512 * 512];
for (int y = 0; y < 512; y++)
for (int x = 0; x < 512; x++)
overrideColors[x + 512 * y] = new Color(0, 0, 0, y / 512f);
overrideTexture.SetPixels(overrideColors);
overrideTexture.Apply();
m_Sprite = Sprite.Create(texture, new Rect(0, 0, 128, 128), new Vector2(0.5f, 0.5f), 100);
m_OverrideSprite = Sprite.Create(overrideTexture, new Rect(0, 0, 512, 512), new Vector2(0.5f, 0.5f), 200);
m_Image.rectTransform.anchoredPosition = new Vector2(0, 0);
m_Image.rectTransform.sizeDelta = new Vector2(100, 100);
m_Image.RegisterDirtyVerticesCallback(() => m_dirtyVert = true);
m_Image.RegisterDirtyLayoutCallback(() => m_dirtyLayout = true);
m_Image.RegisterDirtyMaterialCallback(() => m_dirtyMaterial = true);
ResetDirtyFlags();
}
[TearDown]
public void TearDown()
{
Object.DestroyImmediate(m_CanvasRoot.gameObject);
Object.DestroyImmediate(m_camera.gameObject);
m_Image = null;
m_Sprite = null;
m_OverrideSprite = null;
m_CanvasRoot = null;
m_camera = null;
}
private void ResetDirtyFlags()
{
m_dirtyVert = m_dirtyLayout = m_dirtyMaterial = false;
}
[Test]
public void SetTestSprite()
{
m_Image.sprite = m_Sprite;
m_Image.overrideSprite = m_OverrideSprite;
Assert.AreEqual(m_Sprite, m_Image.sprite);
m_Image.sprite = null;
Assert.AreEqual(null, m_Image.sprite);
}
[Test]
public void TestPixelsPerUnit()
{
m_Image.sprite = m_Sprite;
Assert.AreEqual(1.0f, m_Image.pixelsPerUnit);
m_Image.overrideSprite = m_OverrideSprite;
Assert.AreEqual(2.0f, m_Image.pixelsPerUnit);
m_Image.overrideSprite = null;
Assert.AreEqual(1.0f, m_Image.pixelsPerUnit);
}
[Test]
public void RaycastOverImageWithoutASpriteReturnTrue()
{
m_Image.sprite = null;
bool raycast = m_Image.Raycast(new Vector2(10, 10), m_camera);
Assert.AreEqual(true, raycast);
}
[Test]
[TestCase(0.0f, 1000, 1000)]
[TestCase(1.0f, 1000, 1000)]
[TestCase(0.0f, -1000, 1000)]
[TestCase(1.0f, -1000, 1000)]
[TestCase(0.0f, 1000, -1000)]
[TestCase(1.0f, 1000, -1000)]
[TestCase(0.0f, -1000, -1000)]
[TestCase(1.0f, -1000, -1000)]
public void RaycastOverImageWithoutASpriteReturnsTrueWithCoordinatesOutsideTheBoundaries(float alphaThreshold, float x, float y)
{
m_Image.alphaHitTestMinimumThreshold = 1.0f - alphaThreshold;
bool raycast = m_Image.Raycast(new Vector2(x, y), m_camera);
Assert.IsTrue(raycast);
}
[Test]
public void RaycastOverImageWithNonZeroSpritePosition_AlphaHitTestMinimumThreshold()
{
m_CanvasRoot.renderMode = RenderMode.ScreenSpaceCamera;
m_CanvasRoot.worldCamera = m_camera;
// Create a square sprite (64x64) in the middle of a transparent area (128x128).
Color[] colors = new Color[128 * 128];
for (int y = 32; y < 96; y++)
for (int x = 32; x < 96; x++)
colors[x + 128 * y] = Color.red;
texture.SetPixels(colors);
texture.Apply();
// Set it so that the red square fills the entire screen. Clicking anywehere inside the canvas should hit the image and pass.
m_Sprite = Sprite.Create(texture, new Rect(32, 32, 64, 64), new Vector2(0.5f, 0.5f), 100);
m_Image.sprite = m_Sprite;
m_Image.alphaHitTestMinimumThreshold = 0.5f;
m_Image.rectTransform.anchorMin = Vector2.zero;
m_Image.rectTransform.anchorMax = Vector2.one;
Assert.True(m_Image.Raycast(new Vector2(0, 0), m_camera), "Expected raycast to hit when clicking bottom left corner of screen.");
Assert.True(m_Image.Raycast(new Vector2(0, Screen.height), m_camera), "Expected raycast to hit when clicking top left corner of screen.");
Assert.True(m_Image.Raycast(new Vector2(Screen.width, 0), m_camera), "Expected raycast to hit when clicking bottom right corner of screen.");
Assert.True(m_Image.Raycast(new Vector2(Screen.width, Screen.height), m_camera), "Expected raycast to hit when clicking top right corner of screen.");
Assert.True(m_Image.Raycast(new Vector2(Screen.width / 2.0f, Screen.height / 2.0f), m_camera), "Expected raycast to hit when clicking center of screen.");
}
[Test]
public void RaycastOverImageWithTransparentPixels_AlphaHitTestMinimumThreshold()
{
m_CanvasRoot.renderMode = RenderMode.ScreenSpaceCamera;
m_CanvasRoot.worldCamera = m_camera;
// Create a 128x128 sprite that has a red dash "-" which is 32px tall. Remaining pixels are transparent.
Color[] colors = new Color[128 * 128];
for (int y = 48; y < 80; y++)
for (int x = 0; x < 128; x++)
colors[x + 128 * y] = Color.red;
texture.SetPixels(colors);
texture.Apply();
var sprite = Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height), new Vector2(0.5f, 0.5f), 100);
var parentImage = m_Image;
parentImage.sprite = sprite;
parentImage.alphaHitTestMinimumThreshold = 0.5f;
parentImage.SetNativeSize();
// Duplicate Image as a child GO
var childImageGO = Object.Instantiate(parentImage.gameObject, parentImage.transform);
var childImage = childImageGO.GetComponent<Image>();
childImage.alphaHitTestMinimumThreshold = 0.5f; // Also set the threshold on the child, this property is not serialized.
childImage.rectTransform.anchoredPosition = new Vector2(0, 160);
// Parent Image Raycasts
var parentCenter = new Vector2(Screen.width / 2.0f, Screen.height / 2.0f);
Assert.True(parentImage.Raycast(parentCenter, m_camera), "Expected raycast to hit when clicking on a non-transparent pixel of the image.");
Assert.False(parentImage.Raycast(parentCenter + new Vector2(60, 60), m_camera), "Expected raycast to fail when clicking on a transparent pixel of the image.");
Assert.True(parentImage.Raycast(parentCenter + new Vector2(70, 0), m_camera), "Expected raycast to hit when clicking outside the image, next to a non-transparent area.");
Assert.False(parentImage.Raycast(parentCenter + new Vector2(70, 60), m_camera), "Expected raycast to fail when clicking outside the image, next to a transparent area.");
// Child Image Raycasts
var childCenter = parentCenter + childImage.rectTransform.anchoredPosition;
Assert.True(childImage.Raycast(childCenter, m_camera), "Expected raycast to hit when clicking on a non-transparent pixel of the child image.");
Assert.False(childImage.Raycast(childCenter + new Vector2(60, 60), m_camera), "Expected raycast to fail when clicking on a transparent pixel of the child image.");
Assert.True(childImage.Raycast(childCenter + new Vector2(70, 0), m_camera), "Expected raycast to hit when clicking outside the child image, next to a non-transparent area.");
Assert.False(childImage.Raycast(childCenter + new Vector2(70, 60), m_camera), "Expected raycast to fail when clicking outside the child image, next to a transparent area.");
}
[Test]
public void RaycastOverImageWithPreserveAspectRatio_AlphaHitTestMinimumThreshold()
{
m_CanvasRoot.renderMode = RenderMode.ScreenSpaceCamera;
m_CanvasRoot.worldCamera = m_camera;
texture.wrapMode = TextureWrapMode.Clamp;
m_Image.sprite = m_Sprite;
m_Image.alphaHitTestMinimumThreshold = 0.5f;
m_Image.preserveAspect = true;
// Make the image 100 units wider than the sprite's original size.
m_Image.SetNativeSize();
m_Image.rectTransform.sizeDelta += new Vector2(100, 0);
// Parent Image Raycasts
var imageCenter = new Vector2(Screen.width / 2.0f, Screen.height / 2.0f);
Assert.True(m_Image.Raycast(imageCenter + new Vector2(-40, 0), m_camera), "Expected raycast to hit when clicking on a non-transparent pixel of the image.");
Assert.False(m_Image.Raycast(imageCenter + new Vector2(40, 0), m_camera), "Expected raycast to fail when clicking on a transparent pixel of the image.");
Assert.False(m_Image.Raycast(imageCenter + new Vector2(80, 0), m_camera), "Expected raycast to fail when clicking outside the sprite but still inside the image.");
Assert.False(m_Image.Raycast(imageCenter + new Vector2(120, 0), m_camera), "Expected raycast to fail when clicking outside the image, next to a transparent area.");
}
[Test]
public void RaycastOverImage_IgnoresDisabledCanvasGroup()
{
var canvasGroup = m_CanvasRoot.gameObject.AddComponent<CanvasGroup>();
canvasGroup.blocksRaycasts = false;
canvasGroup.enabled = false;
bool raycast = m_Image.Raycast(new Vector2(1000, 1000), m_camera);
Assert.IsTrue(raycast);
}
[TestCase(typeof(Mask), true, false)]
[TestCase(typeof(Mask), false, true)]
[TestCase(typeof(RectMask2D), true, false)]
[TestCase(typeof(RectMask2D), false, true)]
public void RaycastImageOutsideOfMaskWithMaskableSet_ReturnsExpected(Type maskType, bool maskable, bool expectedHit)
{
m_CanvasRoot.renderMode = RenderMode.ScreenSpaceCamera;
m_CanvasRoot.worldCamera = m_camera;
var maskRT = new GameObject("Mask", typeof(RectTransform), maskType).GetComponent<RectTransform>();
maskRT.SetParent(m_CanvasRoot.transform, false);
maskRT.anchoredPosition = Vector2.zero;
maskRT.sizeDelta = new Vector2(100, 100);
m_Image.transform.SetParent(maskRT);
m_Image.rectTransform.anchoredPosition = new Vector2(500, 500);
m_Image.rectTransform.sizeDelta = new Vector2(10, 10);
m_Image.maskable = maskable;
var imagePosition = RectTransformUtility.WorldToScreenPoint(m_camera, m_Image.rectTransform.position);
bool raycast = m_Image.Raycast(imagePosition, m_camera);
Assert.AreEqual(expectedHit, raycast, "Raycast is not working as expected when Image doesn't have a Sprite.");
m_Image.sprite = m_Sprite;
// Image raycast returns true when alphaHitTestMinimumThreshold = 0
m_Image.alphaHitTestMinimumThreshold = 0f;
raycast = m_Image.Raycast(imagePosition, m_camera);
Assert.AreEqual(expectedHit, raycast, "Raycast is not working as expected when Image has a Sprite.");
m_Image.alphaHitTestMinimumThreshold = 0.1f;
raycast = m_Image.Raycast(imagePosition, m_camera);
Assert.AreEqual(expectedHit, raycast, "Raycast is not working as expected when Image has a Sprite and clickin on a more transparent pixel than alphaHitTestMinimumThreshold.");
m_Image.alphaHitTestMinimumThreshold = 0.8f;
raycast = m_Image.Raycast(imagePosition, m_camera);
Assert.False(raycast, "Expected raycast to fail when clicking on a pixel that is more transparent than alphaHitTestMinimumThreshold.");
}
[Test]
public void SettingSpriteMarksAllAsDirty()
{
m_Image.sprite = m_Sprite;
Assert.True(m_dirtyVert, "Vertices have not been dirtied");
Assert.True(m_dirtyLayout, "Layout has not been dirtied");
Assert.True(m_dirtyMaterial, "Material has not been dirtied");
}
[Test]
public void SettingOverrideSpriteMarksAllAsDirty()
{
m_Image.overrideSprite = m_OverrideSprite;
Assert.True(m_dirtyVert, "Vertices have not been dirtied");
Assert.True(m_dirtyLayout, "Layout has not been dirtied");
Assert.True(m_dirtyMaterial, "Material has not been dirtied");
}
[Test]
public void SettingTypeMarksVerticesAsDirty()
{
m_Image.type = Image.Type.Filled;
Assert.True(m_dirtyVert, "Vertices have not been dirtied");
Assert.False(m_dirtyLayout, "Layout has been dirtied");
Assert.False(m_dirtyMaterial, "Material has been dirtied");
}
[Test]
public void SettingPreserveAspectMarksVerticesAsDirty()
{
m_Image.preserveAspect = true;
Assert.True(m_dirtyVert, "Vertices have not been dirtied");
Assert.False(m_dirtyLayout, "Layout has been dirtied");
Assert.False(m_dirtyMaterial, "Material has been dirtied");
}
[Test]
public void SettingFillCenterMarksVerticesAsDirty()
{
m_Image.fillCenter = false;
Assert.True(m_dirtyVert, "Vertices have not been dirtied");
Assert.False(m_dirtyLayout, "Layout has been dirtied");
Assert.False(m_dirtyMaterial, "Material has been dirtied");
}
[Test]
public void SettingFillMethodMarksVerticesAsDirty()
{
m_Image.fillMethod = Image.FillMethod.Horizontal;
Assert.True(m_dirtyVert, "Vertices have not been dirtied");
Assert.False(m_dirtyLayout, "Layout has been dirtied");
Assert.False(m_dirtyMaterial, "Material has been dirtied");
}
[Test]
public void SettingFillAmountMarksVerticesAsDirty()
{
m_Image.fillAmount = 0.5f;
Assert.True(m_dirtyVert, "Vertices have not been dirtied");
Assert.False(m_dirtyLayout, "Layout has been dirtied");
Assert.False(m_dirtyMaterial, "Material has been dirtied");
}
[Test]
public void SettingFillClockwiseMarksVerticesAsDirty()
{
m_Image.fillClockwise = false;
Assert.True(m_dirtyVert, "Vertices have not been dirtied");
Assert.False(m_dirtyLayout, "Layout has been dirtied");
Assert.False(m_dirtyMaterial, "Material has been dirtied");
}
[Test]
public void SettingFillOriginMarksVerticesAsDirty()
{
m_Image.fillOrigin = 1;
Assert.True(m_dirtyVert, "Vertices have not been dirtied");
Assert.False(m_dirtyLayout, "Layout has been dirtied");
Assert.False(m_dirtyMaterial, "Material has been dirtied");
}
[Test]
public void SettingEventAlphaThresholdMarksNothingAsDirty()
{
m_Image.alphaHitTestMinimumThreshold = 0.5f;
Assert.False(m_dirtyVert, "Vertices have been dirtied");
Assert.False(m_dirtyLayout, "Layout has been dirtied");
Assert.False(m_dirtyMaterial, "Material has been dirtied");
}
[Test]
public void OnAfterDeserializeMakeFillOriginZeroIfNotBetweenZeroAndThree()
{
for (int i = -10; i < 0; i++)
{
m_Image.fillOrigin = i;
m_Image.OnAfterDeserialize();
Assert.AreEqual(0, m_Image.fillOrigin);
}
for (int i = 0; i < 4; i++)
{
m_Image.fillOrigin = i;
m_Image.OnAfterDeserialize();
Assert.AreEqual(i, m_Image.fillOrigin);
}
for (int i = 4; i < 10; i++)
{
m_Image.fillOrigin = i;
m_Image.OnAfterDeserialize();
Assert.AreEqual(0, m_Image.fillOrigin);
}
}
[Test]
public void OnAfterDeserializeMakeFillOriginZeroIfFillOriginGreaterThan1AndFillMethodHorizontalOrVertical()
{
m_Image.fillMethod = Image.FillMethod.Horizontal;
Image.FillMethod[] fillMethodsToTest = {Image.FillMethod.Horizontal, Image.FillMethod.Vertical};
foreach (var fillMethod in fillMethodsToTest)
{
m_Image.fillMethod = fillMethod;
for (int i = -10; i < 0; i++)
{
m_Image.fillOrigin = i;
m_Image.OnAfterDeserialize();
Assert.AreEqual(0, m_Image.fillOrigin);
}
for (int i = 0; i < 2; i++)
{
m_Image.fillOrigin = i;
m_Image.OnAfterDeserialize();
Assert.AreEqual(i, m_Image.fillOrigin);
}
for (int i = 2; i < 100; i++)
{
m_Image.fillOrigin = i;
m_Image.OnAfterDeserialize();
Assert.AreEqual(0, m_Image.fillOrigin);
}
}
}
[Test]
public void OnAfterDeserializeClampsFillAmountBetweenZeroAndOne()
{
for (float f = -5; f < 0; f += 0.1f)
{
m_Image.fillAmount = f;
m_Image.OnAfterDeserialize();
Assert.AreEqual(0, m_Image.fillAmount);
}
for (float f = 0; f < 1; f += 0.1f)
{
m_Image.fillAmount = f;
m_Image.OnAfterDeserialize();
Assert.AreEqual(f, m_Image.fillAmount);
}
for (float f = 1; f < 5; f += 0.1f)
{
m_Image.fillAmount = f;
m_Image.OnAfterDeserialize();
Assert.AreEqual(1, m_Image.fillAmount);
}
}
[Test]
public void SetNativeSizeSetsAllAsDirtyAndSetsAnchorMaxAndSizeDeltaWhenOverrideSpriteIsNotNull()
{
m_Image.sprite = m_Sprite;
m_Image.overrideSprite = m_OverrideSprite;
m_Image.rectTransform.anchorMax = new Vector2(100, 100);
m_Image.rectTransform.anchorMin = new Vector2(0, 0);
m_Image.SetNativeSize();
Assert.True(m_dirtyVert, "Vertices have not been dirtied");
Assert.True(m_dirtyLayout, "Layout has not been dirtied");
Assert.True(m_dirtyMaterial, "Material has not been dirtied");
Assert.AreEqual(m_Image.rectTransform.anchorMin, m_Image.rectTransform.anchorMax);
Assert.AreEqual(m_OverrideSprite.rect.size / m_Image.pixelsPerUnit, m_Image.rectTransform.sizeDelta);
}
[Test]
public void OnPopulateMeshWhenNoOverrideSpritePresentDefersToGraphicImplementation()
{
m_OverrideSprite = null;
m_Image.rectTransform.anchoredPosition = new Vector2(100, 452);
m_Image.rectTransform.sizeDelta = new Vector2(881, 593);
m_Image.color = new Color(0.1f, 0.2f, 0.8f, 0);
VertexHelper vh = new VertexHelper();
m_Image.InvokeOnPopulateMesh(vh);
Assert.AreEqual(4, vh.currentVertCount);
List<UIVertex> verts = new List<UIVertex>();
vh.GetUIVertexStream(verts);
// The vertices for the 2 triangles of the canvas
UIVertex[] expectedVertices =
{
new UIVertex
{
color = m_Image.color,
position = m_Image.rectTransform.rect.min,
uv0 = new Vector2(0f, 0f),
normal = new Vector3(0, 0, -1),
tangent = new Vector4(1, 0, 0, -1)
},
new UIVertex
{
color = m_Image.color,
position = new Vector3(m_Image.rectTransform.rect.xMin, m_Image.rectTransform.rect.yMax),
uv0 = new Vector2(0f, 1f),
normal = new Vector3(0, 0, -1),
tangent = new Vector4(1, 0, 0, -1)
},
new UIVertex
{
color = m_Image.color,
position = m_Image.rectTransform.rect.max,
uv0 = new Vector2(1f, 1f),
normal = new Vector3(0, 0, -1),
tangent = new Vector4(1, 0, 0, -1)
},
new UIVertex
{
color = m_Image.color,
position = m_Image.rectTransform.rect.max,
uv0 = new Vector2(1f, 1f),
normal = new Vector3(0, 0, -1),
tangent = new Vector4(1, 0, 0, -1)
},
new UIVertex
{
color = m_Image.color,
position = new Vector3(m_Image.rectTransform.rect.xMax, m_Image.rectTransform.rect.yMin),
uv0 = new Vector2(1f, 0f),
normal = new Vector3(0, 0, -1),
tangent = new Vector4(1, 0, 0, -1)
},
new UIVertex
{
color = m_Image.color,
position = m_Image.rectTransform.rect.min,
uv0 = new Vector2(0f, 0f),
normal = new Vector3(0, 0, -1),
tangent = new Vector4(1, 0, 0, -1)
},
};
for (int i = 0; i < verts.Count; i++)
{
Assert.AreEqual(expectedVertices[i], verts[i]);
}
}
private void TestOnPopulateMeshTypeSimple(VertexHelper vh, Vector4 UVs)
{
List<UIVertex> verts = new List<UIVertex>();
vh.GetUIVertexStream(verts);
Assert.AreEqual(4, vh.currentVertCount);
Assert.AreEqual(6, vh.currentIndexCount);
var imgRect = m_Image.rectTransform.rect;
Vector3[] expectedVertices =
{
imgRect.min,
new Vector3(imgRect.xMin, imgRect.yMax),
imgRect.max,
imgRect.max,
new Vector3(imgRect.xMax, imgRect.yMin),
imgRect.min
};
Vector2[] expectedUV0s =
{
new Vector2(UVs.x, UVs.y),
new Vector2(UVs.x, UVs.w),
new Vector2(UVs.z, UVs.w),
new Vector2(UVs.z, UVs.w),
new Vector2(UVs.z, UVs.y),
new Vector2(UVs.x, UVs.y),
};
var expectedNormal = new Vector3(0, 0, -1);
var expectedTangent = new Vector4(1, 0, 0, -1);
Color32 expectedColor = m_Image.color;
Vector2 expectedUV1 = new Vector2(0, 0);
for (int i = 0; i < 6; i++)
{
Assert.AreEqual(expectedVertices[i], verts[i].position);
Assert.AreEqual(expectedUV0s[i], verts[i].uv0);
Assert.AreEqual(expectedUV1, verts[i].uv1);
Assert.AreEqual(expectedNormal, verts[i].normal);
Assert.AreEqual(expectedTangent, verts[i].tangent);
Assert.AreEqual(expectedColor, verts[i].color);
}
}
[Test]
public void OnPopulateMeshWithTypeTiledNoBorderGeneratesExpectedResults()
{
m_Image.sprite = m_Sprite;
m_Image.sprite.texture.wrapMode = TextureWrapMode.Repeat;
m_Image.type = Image.Type.Tiled;
VertexHelper vh = new VertexHelper();
m_Image.InvokeOnPopulateMesh(vh);
Assert.AreEqual(4, vh.currentVertCount);
Assert.AreEqual(6, vh.currentIndexCount);
}
[Test]
public void MinWidthHeightAreZeroWithNoImage()
{
Assert.AreEqual(0, m_Image.minWidth);
Assert.AreEqual(0, m_Image.minHeight);
}
[Test]
public void FlexibleWidthHeightAreCorrectWithNoImage()
{
Assert.AreEqual(-1, m_Image.flexibleWidth);
Assert.AreEqual(-1, m_Image.flexibleHeight);
}
[Test]
public void PreferredWidthHeightAreCorrectWithNoImage()
{
Assert.AreEqual(0, m_Image.preferredWidth);
Assert.AreEqual(0, m_Image.preferredHeight);
}
[Test]
public void MinWidthHeightAreZeroWithImage()
{
m_Image.sprite = m_Sprite;
Assert.AreEqual(0, m_Image.minWidth);
Assert.AreEqual(0, m_Image.minHeight);
}
[Test]
public void FlexibleWidthHeightAreCorrectWithImage()
{
m_Image.sprite = m_Sprite;
Assert.AreEqual(-1, m_Image.flexibleWidth);
Assert.AreEqual(-1, m_Image.flexibleHeight);
}
[Test]
public void PreferredWidthHeightAreCorrectWithImage()
{
m_Image.sprite = m_Sprite;
Assert.AreEqual(128, m_Image.preferredWidth);
Assert.AreEqual(128, m_Image.preferredHeight);
}
[Test]
public void MinWidthHeightAreZeroWithOverrideImage()
{
m_Image.sprite = m_Sprite;
m_Image.overrideSprite = m_OverrideSprite;
Assert.AreEqual(0, m_Image.minWidth);
Assert.AreEqual(0, m_Image.minHeight);
}
[Test]
public void FlexibleWidthHeightAreCorrectWithOverrideImage()
{
m_Image.sprite = m_Sprite;
m_Image.overrideSprite = m_OverrideSprite;
Assert.AreEqual(-1, m_Image.flexibleWidth);
Assert.AreEqual(-1, m_Image.flexibleHeight);
}
[Test]
public void PreferredWidthHeightAreCorrectWithOverrideImage()
{
m_Image.sprite = m_Sprite;
m_Image.overrideSprite = m_OverrideSprite;
Assert.AreEqual(256, m_Image.preferredWidth);
Assert.AreEqual(256, m_Image.preferredHeight);
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a0bae4ff8f5baf64aad3b92b8aea0603
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,163 @@
using System.Collections;
using System.Collections.Generic;
using System.IO;
using NUnit.Framework;
using UnityEditor;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.TestTools;
using UnityEngine.UI;
namespace Graphics
{
class MaskTests : IPrebuildSetup
{
GameObject m_PrefabRoot;
const string kPrefabPath = "Assets/Resources/MaskTestsPrefab.prefab";
Mask m_mask;
public void Setup()
{
#if UNITY_EDITOR
var rootGO = new GameObject("rootGo");
GameObject canvasGO = new GameObject("Canvas", typeof(RectTransform), typeof(Canvas));
canvasGO.transform.SetParent(rootGO.transform);
var canvas = canvasGO.GetComponent<Canvas>();
canvas.referencePixelsPerUnit = 100;
var gameObject = new GameObject("Mask", typeof(RectTransform), typeof(Mask), typeof(Image));
gameObject.transform.SetParent(canvasGO.transform);
if (!Directory.Exists("Assets/Resources/"))
Directory.CreateDirectory("Assets/Resources/");
PrefabUtility.SaveAsPrefabAsset(rootGO, kPrefabPath);
GameObject.DestroyImmediate(rootGO);
#endif
}
[SetUp]
public void TestSetup()
{
m_PrefabRoot = Object.Instantiate(Resources.Load("MaskTestsPrefab")) as GameObject;
m_mask = m_PrefabRoot.GetComponentInChildren<Mask>();
}
[TearDown]
public void TearDown()
{
m_mask = null;
Object.DestroyImmediate(m_PrefabRoot);
}
[OneTimeTearDown]
public void OneTimeTearDown()
{
#if UNITY_EDITOR
AssetDatabase.DeleteAsset(kPrefabPath);
#endif
}
[UnityTest]
public IEnumerator GetModifiedMaterialReturnsOriginalMaterialWhenNoGraphicComponentIsAttached()
{
Object.DestroyImmediate(m_mask.gameObject.GetComponent<Image>());
yield return null;
Material material = new Material(Graphic.defaultGraphicMaterial);
Material modifiedMaterial = m_mask.GetModifiedMaterial(material);
Assert.AreEqual(material, modifiedMaterial);
}
private Dictionary<string, GameObject> CreateMaskHierarchy(string suffix, int hierarchyDepth, out GameObject root)
{
var nameToObjectMapping = new Dictionary<string, GameObject>();
root = new GameObject("root", typeof(RectTransform), typeof(Canvas));
nameToObjectMapping["root"] = root;
GameObject current = root;
for (int i = 0; i < hierarchyDepth; i++)
{
string name = suffix + (i + 1);
var gameObject = new GameObject(name, typeof(RectTransform), typeof(Mask), typeof(Image));
gameObject.transform.SetParent(current.transform);
nameToObjectMapping[name] = gameObject;
current = gameObject;
}
return nameToObjectMapping;
}
[Test]
public void GetModifiedMaterialReturnsOriginalMaterialWhenDepthIsEightOrMore()
{
GameObject root;
var objectsMap = CreateMaskHierarchy("subMask", 9, out root);
Mask mask = objectsMap["subMask" + 9].GetComponent<Mask>();
Material material = new Material(Graphic.defaultGraphicMaterial);
Material modifiedMaterial = mask.GetModifiedMaterial(material);
Assert.AreEqual(material, modifiedMaterial);
GameObject.DestroyImmediate(root);
}
[Test]
public void GetModifiedMaterialReturnsDesiredMaterialWithSingleMask()
{
Material material = new Material(Graphic.defaultGraphicMaterial);
Material modifiedMaterial = m_mask.GetModifiedMaterial(material);
Assert.AreNotEqual(material, modifiedMaterial);
Assert.AreEqual(1, modifiedMaterial.GetInt("_Stencil"));
Assert.AreEqual(StencilOp.Replace, (StencilOp)modifiedMaterial.GetInt("_StencilOp"));
Assert.AreEqual(CompareFunction.Always, (CompareFunction)modifiedMaterial.GetInt("_StencilComp"));
Assert.AreEqual(255, modifiedMaterial.GetInt("_StencilReadMask"));
Assert.AreEqual(255, modifiedMaterial.GetInt("_StencilWriteMask"));
Assert.AreEqual(ColorWriteMask.All, (ColorWriteMask)modifiedMaterial.GetInt("_ColorMask"));
Assert.AreEqual(1, modifiedMaterial.GetInt("_UseUIAlphaClip"));
Assert.IsTrue(modifiedMaterial.IsKeywordEnabled("UNITY_UI_ALPHACLIP"));
}
[Test]
public void GetModifiedMaterialReturnsDesiredMaterialWithMultipleMasks()
{
for (int i = 2; i < 8; i++)
{
GameObject root;
var objectsMap = CreateMaskHierarchy("subMask", i, out root);
Mask mask = objectsMap["subMask" + i].GetComponent<Mask>();
int stencilDepth = MaskUtilities.GetStencilDepth(mask.transform, objectsMap["root"].transform);
int desiredStencilBit = 1 << stencilDepth;
Material material = new Material(Graphic.defaultGraphicMaterial);
Material modifiedMaterial = mask.GetModifiedMaterial(material);
int stencil = modifiedMaterial.GetInt("_Stencil");
Assert.AreNotEqual(material, modifiedMaterial);
Assert.AreEqual(desiredStencilBit | (desiredStencilBit - 1), stencil);
Assert.AreEqual(StencilOp.Replace, (StencilOp)modifiedMaterial.GetInt("_StencilOp"));
Assert.AreEqual(CompareFunction.Equal, (CompareFunction)modifiedMaterial.GetInt("_StencilComp"));
Assert.AreEqual(desiredStencilBit - 1, modifiedMaterial.GetInt("_StencilReadMask"));
Assert.AreEqual(desiredStencilBit | (desiredStencilBit - 1), modifiedMaterial.GetInt("_StencilWriteMask"));
Assert.AreEqual(ColorWriteMask.All, (ColorWriteMask)modifiedMaterial.GetInt("_ColorMask"));
Assert.AreEqual(1, modifiedMaterial.GetInt("_UseUIAlphaClip"));
Assert.IsTrue(modifiedMaterial.IsKeywordEnabled("UNITY_UI_ALPHACLIP"));
GameObject.DestroyImmediate(root);
}
}
[Test]
public void GraphicComponentWithMaskIsMarkedAsIsMaskingGraphicWhenEnabled()
{
var graphic = m_PrefabRoot.GetComponentInChildren<Image>();
Assert.AreEqual(true, graphic.isMaskingGraphic);
m_mask.enabled = false;
Assert.AreEqual(false, graphic.isMaskingGraphic);
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b611b0a598aeada419afa9737807c598
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,169 @@
using System.Reflection;
using System.Collections;
using NUnit.Framework;
using UnityEngine.EventSystems;
using UnityEngine.TestTools;
namespace UnityEngine.UI.Tests
{
[TestFixture]
class NavigationTests
{
GameObject canvasRoot;
Selectable topLeftSelectable;
Selectable bottomLeftSelectable;
Selectable topRightSelectable;
Selectable bottomRightSelectable;
[SetUp]
public void TestSetup()
{
canvasRoot = new GameObject("Canvas", typeof(RectTransform), typeof(Canvas));
GameObject topLeftGO = new GameObject("topLeftGO", typeof(RectTransform), typeof(CanvasRenderer), typeof(Selectable));
topLeftGO.transform.SetParent(canvasRoot.transform);
(topLeftGO.transform as RectTransform).anchoredPosition = new Vector2(50, 200);
topLeftSelectable = topLeftGO.GetComponent<Selectable>();
GameObject bottomLeftGO = new GameObject("bottomLeftGO", typeof(RectTransform), typeof(CanvasRenderer), typeof(Selectable));
bottomLeftGO.transform.SetParent(canvasRoot.transform);
(bottomLeftGO.transform as RectTransform).anchoredPosition = new Vector2(50, 50);
bottomLeftSelectable = bottomLeftGO.GetComponent<Selectable>();
GameObject topRightGO = new GameObject("topRightGO", typeof(RectTransform), typeof(CanvasRenderer), typeof(Selectable));
topRightGO.transform.SetParent(canvasRoot.transform);
(topRightGO.transform as RectTransform).anchoredPosition = new Vector2(200, 200);
topRightSelectable = topRightGO.GetComponent<Selectable>();
GameObject bottomRightGO = new GameObject("bottomRightGO", typeof(RectTransform), typeof(CanvasRenderer), typeof(Selectable));
bottomRightGO.transform.SetParent(canvasRoot.transform);
(bottomRightGO.transform as RectTransform).anchoredPosition = new Vector2(200, 50);
bottomRightSelectable = bottomRightGO.GetComponent<Selectable>();
}
[TearDown]
public void TearDown()
{
GameObject.DestroyImmediate(canvasRoot);
}
[Test]
public void FindSelectableOnRight_ReturnsNextSelectableRightOfTarget()
{
Selectable selectableRightOfTopLeft = topLeftSelectable.FindSelectableOnRight();
Selectable selectableRightOfBottomLeft = bottomLeftSelectable.FindSelectableOnRight();
Assert.AreEqual(topRightSelectable, selectableRightOfTopLeft, "Wrong selectable to right of Top Left Selectable");
Assert.AreEqual(bottomRightSelectable, selectableRightOfBottomLeft, "Wrong selectable to right of Bottom Left Selectable");
}
[Test]
public void FindSelectableOnLeft_ReturnsNextSelectableLeftOfTarget()
{
Selectable selectableLeftOfTopRight = topRightSelectable.FindSelectableOnLeft();
Selectable selectableLeftOfBottomRight = bottomRightSelectable.FindSelectableOnLeft();
Assert.AreEqual(topLeftSelectable, selectableLeftOfTopRight, "Wrong selectable to left of Top Right Selectable");
Assert.AreEqual(bottomLeftSelectable, selectableLeftOfBottomRight, "Wrong selectable to left of Bottom Right Selectable");
}
[Test]
public void FindSelectableOnRDown_ReturnsNextSelectableBelowTarget()
{
Selectable selectableDownOfTopLeft = topLeftSelectable.FindSelectableOnDown();
Selectable selectableDownOfTopRight = topRightSelectable.FindSelectableOnDown();
Assert.AreEqual(bottomLeftSelectable, selectableDownOfTopLeft, "Wrong selectable to Bottom of Top Left Selectable");
Assert.AreEqual(bottomRightSelectable, selectableDownOfTopRight, "Wrong selectable to Bottom of top Right Selectable");
}
[Test]
public void FindSelectableOnUp_ReturnsNextSelectableAboveTarget()
{
Selectable selectableUpOfBottomLeft = bottomLeftSelectable.FindSelectableOnUp();
Selectable selectableUpOfBottomRight = bottomRightSelectable.FindSelectableOnUp();
Assert.AreEqual(topLeftSelectable, selectableUpOfBottomLeft, "Wrong selectable to Up of bottom Left Selectable");
Assert.AreEqual(topRightSelectable, selectableUpOfBottomRight, "Wrong selectable to Up of bottom Right Selectable");
}
[Test]
public void FindSelectableOnRight__WrappingEnabled_ReturnsFurthestSelectableOnLeft()
{
Navigation nav = topRightSelectable.navigation;
nav.wrapAround = true;
nav.mode = Navigation.Mode.Horizontal;
topRightSelectable.navigation = nav;
nav = bottomRightSelectable.navigation;
nav.wrapAround = true;
nav.mode = Navigation.Mode.Horizontal;
bottomRightSelectable.navigation = nav;
Selectable selectableRightOfTopRight = topRightSelectable.FindSelectableOnRight();
Selectable selectableRightOfBottomRight = bottomRightSelectable.FindSelectableOnRight();
Assert.AreEqual(bottomLeftSelectable, selectableRightOfTopRight, "Wrong selectable to right of Top Right Selectable");
Assert.AreEqual(topLeftSelectable, selectableRightOfBottomRight, "Wrong selectable to right of Bottom Right Selectable");
}
[Test]
public void FindSelectableOnLeft_WrappingEnabled_ReturnsFurthestSelectableOnRight()
{
Navigation nav = topLeftSelectable.navigation;
nav.wrapAround = true;
nav.mode = Navigation.Mode.Horizontal;
topLeftSelectable.navigation = nav;
nav = bottomLeftSelectable.navigation;
nav.wrapAround = true;
nav.mode = Navigation.Mode.Horizontal;
bottomLeftSelectable.navigation = nav;
Selectable selectableLeftOfTopLeft = topLeftSelectable.FindSelectableOnLeft();
Selectable selectableLeftOfBottomLeft = bottomLeftSelectable.FindSelectableOnLeft();
Assert.AreEqual(bottomRightSelectable, selectableLeftOfTopLeft, "Wrong selectable to left of Top Left Selectable");
Assert.AreEqual(topRightSelectable, selectableLeftOfBottomLeft, "Wrong selectable to left of Bottom Left Selectable");
}
[Test]
public void FindSelectableOnDown_WrappingEnabled_ReturnsFurthestSelectableAbove()
{
Navigation nav = bottomLeftSelectable.navigation;
nav.wrapAround = true;
nav.mode = Navigation.Mode.Vertical;
bottomLeftSelectable.navigation = nav;
nav = bottomRightSelectable.navigation;
nav.wrapAround = true;
nav.mode = Navigation.Mode.Vertical;
bottomRightSelectable.navigation = nav;
Selectable selectableDownOfBottomLeft = bottomLeftSelectable.FindSelectableOnDown();
Selectable selectableDownOfBottomRight = bottomRightSelectable.FindSelectableOnDown();
Assert.AreEqual(topRightSelectable, selectableDownOfBottomLeft, "Wrong selectable to Bottom of Bottom Left Selectable");
Assert.AreEqual(topLeftSelectable, selectableDownOfBottomRight, "Wrong selectable to Bottom of Bottom Right Selectable");
}
[Test]
public void FindSelectableOnUp_WrappingEnabled_ReturnsFurthestSelectableBelow()
{
Navigation nav = topLeftSelectable.navigation;
nav.wrapAround = true;
nav.mode = Navigation.Mode.Vertical;
topLeftSelectable.navigation = nav;
nav = topRightSelectable.navigation;
nav.wrapAround = true;
nav.mode = Navigation.Mode.Vertical;
topRightSelectable.navigation = nav;
Selectable selectableUpOfTopLeft = topLeftSelectable.FindSelectableOnUp();
Selectable selectableUpOfTopRight = topRightSelectable.FindSelectableOnUp();
Assert.AreEqual(bottomRightSelectable, selectableUpOfTopLeft, "Wrong selectable to Up of Top Left Selectable");
Assert.AreEqual(bottomLeftSelectable, selectableUpOfTopRight, "Wrong selectable to Up of Top Right Selectable");
}
}
}

Some files were not shown because too many files have changed in this diff Show More