A game about forced loneliness, made by TACStudios
1using System.Reflection;
2using System.Collections;
3using NUnit.Framework;
4using UnityEngine.EventSystems;
5using UnityEngine.TestTools;
6
7namespace UnityEngine.UI.Tests
8{
9 [TestFixture]
10 class SelectableTests
11 {
12 private class SelectableTest : Selectable
13 {
14 public bool isStateNormal { get { return currentSelectionState == SelectionState.Normal; } }
15 public bool isStateHighlighted { get { return currentSelectionState == SelectionState.Highlighted; } }
16 public bool isStateSelected { get { return currentSelectionState == SelectionState.Selected; } }
17 public bool isStatePressed { get { return currentSelectionState == SelectionState.Pressed; } }
18 public bool isStateDisabled { get { return currentSelectionState == SelectionState.Disabled; } }
19
20 public Selectable GetSelectableAtIndex(int index)
21 {
22 return s_Selectables[index];
23 }
24
25 public int GetSelectableCurrentIndex()
26 {
27 return m_CurrentIndex;
28 }
29 }
30
31 private SelectableTest selectable;
32 private GameObject m_CanvasRoot;
33 private GameObject m_EventSystemGO;
34
35 private CanvasGroup CreateAndParentGroupTo(string name, GameObject child)
36 {
37 GameObject canvasRoot = new GameObject("Canvas", typeof(RectTransform), typeof(Canvas));
38 GameObject groupGO = new GameObject(name, typeof(RectTransform), typeof(CanvasGroup));
39 groupGO.transform.SetParent(canvasRoot.transform);
40 child.transform.SetParent(groupGO.transform);
41 return groupGO.GetComponent<CanvasGroup>();
42 }
43
44 [SetUp]
45 public void TestSetup()
46 {
47 m_EventSystemGO = new GameObject("EventSystem", typeof(EventSystem));
48 EventSystem.current = m_EventSystemGO.GetComponent<EventSystem>();
49 m_CanvasRoot = new GameObject("Canvas", typeof(RectTransform), typeof(Canvas));
50 GameObject SelectableGO = new GameObject("Selectable", typeof(RectTransform), typeof(CanvasRenderer));
51
52 SelectableGO.transform.SetParent(m_CanvasRoot.transform);
53 selectable = SelectableGO.AddComponent<SelectableTest>();
54 selectable.targetGraphic = selectable.gameObject.AddComponent<ConcreteGraphic>();
55 }
56
57 [TearDown]
58 public void TearDown()
59 {
60 GameObject.DestroyImmediate(m_CanvasRoot);
61 GameObject.DestroyImmediate(m_EventSystemGO);
62 }
63
64 [Test] // regression test 1160054
65 public void SelectableArrayRemovesReferenceUponDisable()
66 {
67 int originalSelectableCount = Selectable.allSelectableCount;
68
69 selectable.enabled = false;
70
71 Assert.AreEqual(originalSelectableCount - 1, Selectable.allSelectableCount, "We have more then originalSelectableCount - 1 selectable objects.");
72 //ensure the item as the last index is nulled out as it replaced the item that was disabled.
73 Assert.IsNull(selectable.GetSelectableAtIndex(Selectable.allSelectableCount));
74
75 selectable.enabled = true;
76 }
77
78 #region Selected object
79
80 [Test]
81 public void SettingCurrentSelectedSelectableNonInteractableShouldNullifyCurrentSelected()
82 {
83 EventSystem.current.SetSelectedGameObject(selectable.gameObject);
84 selectable.interactable = false;
85
86 // it should be unselected now that it is not interactable anymore
87 Assert.IsNull(EventSystem.current.currentSelectedGameObject);
88 }
89
90 [Test]
91 public void PointerEnterDownShouldMakeItSelectedGameObject()
92 {
93 Assert.IsNull(EventSystem.current.currentSelectedGameObject);
94 selectable.InvokeOnPointerEnter(new PointerEventData(EventSystem.current));
95 selectable.InvokeOnPointerDown(new PointerEventData(EventSystem.current));
96 Assert.AreEqual(selectable.gameObject, EventSystem.current.currentSelectedGameObject);
97 }
98
99 [Test]
100 public void OnSelectShouldSetSelectedState()
101 {
102 Assert.True(selectable.isStateNormal);
103 selectable.OnSelect(new BaseEventData(EventSystem.current));
104 Assert.True(selectable.isStateSelected);
105 }
106
107 [Test]
108 public void OnDeselectShouldUnsetSelectedState()
109 {
110 Assert.True(selectable.isStateNormal);
111 selectable.OnSelect(new BaseEventData(EventSystem.current));
112 Assert.True(selectable.isStateSelected);
113 selectable.OnDeselect(new BaseEventData(EventSystem.current));
114 Assert.True(selectable.isStateNormal);
115 }
116
117 #endregion
118
119 #region Interactable
120
121 [Test]
122 public void SettingCanvasGroupNotInteractableShouldMakeSelectableNotInteractable()
123 {
124 // Canvas Group on same object
125 var group = selectable.gameObject.AddComponent<CanvasGroup>();
126 Assert.IsTrue(selectable.IsInteractable());
127
128 group.interactable = false;
129 // actual call happens on the native side, cause by interactable = false
130 selectable.InvokeOnCanvasGroupChanged();
131
132 Assert.IsFalse(selectable.IsInteractable());
133 }
134
135 [Test]
136 public void DisablingCanvasGroupShouldMakeSelectableAsInteractable()
137 {
138 var group = selectable.gameObject.AddComponent<CanvasGroup>();
139 Assert.IsTrue(selectable.IsInteractable());
140
141 group.enabled = false;
142 group.interactable = false;
143
144 selectable.InvokeOnCanvasGroupChanged();
145 Assert.IsTrue(selectable.IsInteractable());
146 }
147
148 [Test]
149 public void SettingParentCanvasGroupNotInteractableShouldMakeSelectableNotInteractable()
150 {
151 var canvasGroup = CreateAndParentGroupTo("CanvasGroup", selectable.gameObject);
152 Assert.IsTrue(selectable.IsInteractable());
153
154 canvasGroup.interactable = false;
155 // actual call happens on the native side, cause by interactable = false
156 selectable.InvokeOnCanvasGroupChanged();
157
158 Assert.IsFalse(selectable.IsInteractable());
159 }
160
161 [Test]
162 public void SettingParentParentCanvasGroupNotInteractableShouldMakeSelectableNotInteractable()
163 {
164 var canvasGroup1 = CreateAndParentGroupTo("CanvasGroup1", selectable.gameObject);
165 var canvasGroup2 = CreateAndParentGroupTo("CanvasGroup2", canvasGroup1.gameObject);
166 Assert.IsTrue(selectable.IsInteractable());
167
168 canvasGroup2.interactable = false;
169 // actual call happens on the native side, cause by interactable = false
170 selectable.InvokeOnCanvasGroupChanged();
171
172 Assert.IsFalse(selectable.IsInteractable());
173 }
174
175 [Test]
176 public void SettingParentParentCanvasGroupInteractableShouldMakeSelectableInteractable()
177 {
178 var canvasGroup1 = CreateAndParentGroupTo("CanvasGroup1", selectable.gameObject);
179 CreateAndParentGroupTo("CanvasGroup2", canvasGroup1.gameObject);
180 Assert.IsTrue(selectable.IsInteractable());
181
182 // actual call happens on the native side, cause by interactable
183 selectable.InvokeOnCanvasGroupChanged();
184
185 Assert.IsTrue(selectable.IsInteractable());
186 }
187
188 [Test]
189 public void SettingParentParentCanvasGroupNotInteractableShouldNotMakeSelectableNotInteractableIfIgnoreParentGroups()
190 {
191 var canvasGroup1 = CreateAndParentGroupTo("CanvasGroup1", selectable.gameObject);
192 canvasGroup1.ignoreParentGroups = true;
193 var canvasGroup2 = CreateAndParentGroupTo("CanvasGroup2", canvasGroup1.gameObject);
194 Assert.IsTrue(selectable.IsInteractable());
195
196 canvasGroup2.interactable = false;
197 // actual call happens on the native side, cause by interactable = false
198 selectable.InvokeOnCanvasGroupChanged();
199
200 Assert.IsTrue(selectable.IsInteractable());
201 }
202
203 [Test]// regression test 861736
204 public void PointerEnterThenSetNotInteractableThenExitThenSetInteractableShouldSetStateToDefault()
205 {
206 Assert.True(selectable.isStateNormal);
207 selectable.InvokeOnPointerEnter(new PointerEventData(EventSystem.current)
208 {
209 pointerEnter = selectable.gameObject
210 });
211 Assert.True(selectable.isStateHighlighted);
212 selectable.interactable = false;
213 selectable.InvokeOnPointerExit(new PointerEventData(EventSystem.current)
214 {
215 pointerEnter = selectable.gameObject
216 });
217 selectable.interactable = true;
218 Assert.False(selectable.isStateHighlighted);
219 Assert.True(selectable.isStateNormal);
220 }
221
222 [Test]// regression test 861736
223 public void PointerEnterThenSetNotInteractableThenSetInteractableShouldStayHighlighted()
224 {
225 Assert.True(selectable.isStateNormal);
226 selectable.InvokeOnPointerEnter(new PointerEventData(EventSystem.current)
227 {
228 pointerEnter = selectable.gameObject
229 });
230 Assert.True(selectable.isStateHighlighted);
231 selectable.interactable = false;
232 selectable.interactable = true;
233 Assert.True(selectable.isStateHighlighted);
234 }
235
236 [Test]
237 public void InstantiatingSelectableUnderNotInteractableCanvasGroupShouldAlsoNotBeInteractable()
238 {
239 var canvasGroup = CreateAndParentGroupTo("ParentGroup", selectable.gameObject);
240 canvasGroup.interactable = false;
241 Assert.False(canvasGroup.interactable);
242 var newSelectable = Object.Instantiate(selectable.gameObject, canvasGroup.transform).GetComponent<SelectableTest>();
243 Assert.False(newSelectable.IsInteractable());
244 }
245
246 #endregion
247
248 #region Tweening
249
250 [UnityTest]
251 public IEnumerator SettingNotInteractableShouldTweenToDisabledColor()
252 {
253 var canvasRenderer = selectable.gameObject.GetComponent<CanvasRenderer>();
254 selectable.InvokeOnEnable();
255 canvasRenderer.SetColor(selectable.colors.normalColor);
256
257 selectable.interactable = false;
258
259 yield return new WaitForSeconds(1);
260
261 Assert.AreEqual(selectable.colors.disabledColor, canvasRenderer.GetColor());
262
263 selectable.interactable = true;
264
265 yield return new WaitForSeconds(1);
266
267 Assert.AreEqual(selectable.colors.normalColor, canvasRenderer.GetColor());
268 }
269
270 [UnityTest][Ignore("Fails")] // regression test 742140
271 public IEnumerator SettingNotInteractableThenInteractableShouldNotTweenToDisabledColor()
272 {
273 var canvasRenderer = selectable.gameObject.GetComponent<CanvasRenderer>();
274 selectable.enabled = false;
275 selectable.enabled = true;
276 canvasRenderer.SetColor(selectable.colors.normalColor);
277
278 selectable.interactable = false;
279 selectable.interactable = true;
280 Color c = canvasRenderer.GetColor();
281
282 for (int i = 0; i < 30; i++)
283 {
284 yield return null;
285 Color c2 = canvasRenderer.GetColor();
286 Assert.AreNotEqual(c2, c);
287 }
288 Assert.AreEqual(selectable.colors.normalColor, canvasRenderer.GetColor());
289 }
290
291 [UnityTest]
292 public IEnumerator SettingInteractableToFalseTrueFalseShouldTweenToDisabledColor()
293 {
294 var canvasRenderer = selectable.gameObject.GetComponent<CanvasRenderer>();
295 selectable.InvokeOnEnable();
296 canvasRenderer.SetColor(selectable.colors.normalColor);
297
298 selectable.interactable = false;
299 selectable.interactable = true;
300 selectable.interactable = false;
301
302 yield return new WaitForSeconds(1);
303
304 Assert.AreEqual(selectable.colors.disabledColor, canvasRenderer.GetColor());
305 }
306
307#if PACKAGE_ANIMATION
308 [Test]
309 public void TriggerAnimationWithNoAnimator()
310 {
311 Assert.Null(selectable.animator);
312 Assert.DoesNotThrow(() => selectable.InvokeTriggerAnimation("asdasd"));
313 }
314
315 [Test]
316 public void TriggerAnimationWithDisabledAnimator()
317 {
318 var an = selectable.gameObject.AddComponent<Animator>();
319 an.enabled = false;
320 Assert.NotNull(selectable.animator);
321 Assert.DoesNotThrow(() => selectable.InvokeTriggerAnimation("asdasd"));
322 }
323
324 [Test]
325 public void TriggerAnimationAnimatorWithNoRuntimeController()
326 {
327 var an = selectable.gameObject.AddComponent<Animator>();
328 an.runtimeAnimatorController = null;
329 Assert.NotNull(selectable.animator);
330 Assert.DoesNotThrow(() => selectable.InvokeTriggerAnimation("asdasd"));
331 }
332
333#endif
334 #endregion
335
336 #region Selection state and pointer
337
338 [Test]
339 public void SelectShouldSetSelectedObject()
340 {
341 Assert.Null(EventSystem.current.currentSelectedGameObject);
342 selectable.Select();
343 Assert.AreEqual(selectable.gameObject, EventSystem.current.currentSelectedGameObject);
344 }
345
346 [Test]
347 public void SelectWhenAlreadySelectingShouldNotSetSelectedObject()
348 {
349 Assert.Null(EventSystem.current.currentSelectedGameObject);
350 var fieldInfo = typeof(EventSystem).GetField("m_SelectionGuard", BindingFlags.NonPublic | BindingFlags.Instance);
351 fieldInfo
352 .SetValue(EventSystem.current, true);
353 selectable.Select();
354 Assert.Null(EventSystem.current.currentSelectedGameObject);
355 }
356
357 [Test]
358 public void PointerEnterShouldHighlight()
359 {
360 Assert.True(selectable.isStateNormal);
361 selectable.InvokeOnPointerEnter(new PointerEventData(EventSystem.current)
362 {
363 pointerEnter = selectable.gameObject
364 });
365 Assert.True(selectable.isStateHighlighted);
366 }
367
368 [Test]
369 public void PointerEnterOnSelectedObjectShouldStaySelected()
370 {
371 selectable.InvokeOnSelect(null);
372 Assert.True(selectable.isStateSelected);
373 selectable.InvokeOnPointerEnter(new PointerEventData(EventSystem.current)
374 {
375 pointerEnter = selectable.gameObject
376 });
377 Assert.True(selectable.isStateSelected);
378 }
379
380 [Test]
381 public void PointerEnterAndRightClickShouldHighlightNotPress()
382 {
383 Assert.True(selectable.isStateNormal);
384 selectable.InvokeOnPointerEnter(new PointerEventData(EventSystem.current)
385 {
386 pointerEnter = selectable.gameObject
387 });
388 selectable.InvokeOnPointerDown(new PointerEventData(EventSystem.current)
389 {
390 button = PointerEventData.InputButton.Right
391 });
392 Assert.True(selectable.isStateHighlighted);
393 }
394
395 [Test]
396 public void PointerEnterAndRightClickShouldPress()
397 {
398 Assert.True(selectable.isStateNormal);
399 selectable.InvokeOnPointerEnter(new PointerEventData(EventSystem.current)
400 {
401 pointerEnter = selectable.gameObject
402 });
403 selectable.InvokeOnPointerDown(new PointerEventData(EventSystem.current));
404 Assert.True(selectable.isStatePressed);
405 }
406
407 [Test]
408 public void PointerEnterLeftClickExitShouldPress()
409 {
410 Assert.True(selectable.isStateNormal);
411 selectable.InvokeOnPointerEnter(new PointerEventData(EventSystem.current)
412 {
413 pointerEnter = selectable.gameObject
414 });
415 selectable.InvokeOnPointerDown(new PointerEventData(EventSystem.current));
416 selectable.InvokeOnPointerExit(new PointerEventData(EventSystem.current)
417 {
418 pointerEnter = selectable.gameObject
419 });
420 Assert.True(selectable.isStatePressed);
421 }
422
423 [Test]
424 public void PointerEnterLeftClickExitReleaseShouldSelect()
425 {
426 Assert.True(selectable.isStateNormal);
427 selectable.InvokeOnPointerEnter(new PointerEventData(EventSystem.current)
428 {
429 pointerEnter = selectable.gameObject
430 });
431 selectable.InvokeOnPointerDown(new PointerEventData(EventSystem.current));
432 selectable.InvokeOnPointerExit(new PointerEventData(EventSystem.current)
433 {
434 pointerEnter = selectable.gameObject
435 });
436 selectable.InvokeOnPointerUp(new PointerEventData(EventSystem.current));
437 Assert.True(selectable.isStateSelected);
438 }
439
440 [Test]
441 public void PointerDownShouldSetSelectedObject()
442 {
443 Assert.Null(EventSystem.current.currentSelectedGameObject);
444 selectable.InvokeOnPointerDown(new PointerEventData(EventSystem.current));
445 Assert.AreEqual(selectable.gameObject, EventSystem.current.currentSelectedGameObject);
446 }
447
448 [Test]
449 public void PointerLeftDownRightDownRightUpShouldNotChangeState()
450 {
451 Assert.True(selectable.isStateNormal);
452 selectable.InvokeOnPointerEnter(new PointerEventData(EventSystem.current)
453 {
454 pointerEnter = selectable.gameObject
455 });
456 selectable.InvokeOnPointerDown(new PointerEventData(EventSystem.current) { button = PointerEventData.InputButton.Left });
457 selectable.InvokeOnPointerDown(new PointerEventData(EventSystem.current) { button = PointerEventData.InputButton.Right });
458 selectable.InvokeOnPointerUp(new PointerEventData(EventSystem.current) { button = PointerEventData.InputButton.Right });
459 Assert.True(selectable.isStatePressed);
460 }
461
462 [Test, Ignore("No disabled state assigned ? Investigate")]
463 public void SettingNotInteractableShouldDisable()
464 {
465 Assert.True(selectable.isStateNormal);
466 selectable.interactable = false;
467 selectable.InvokeOnCanvasGroupChanged();
468 Assert.True(selectable.isStateDisabled);
469 }
470
471 #endregion
472
473 #region No event system
474
475 [Test] // regression test 787563
476 public void SettingInteractableWithNoEventSystemShouldNotCrash()
477 {
478 EventSystem.current.enabled = false;
479 selectable.interactable = false;
480 }
481
482 [Test] // regression test 787563
483 public void OnPointerDownWithNoEventSystemShouldNotCrash()
484 {
485 EventSystem.current.enabled = false;
486 selectable.OnPointerDown(new PointerEventData(EventSystem.current) {button = PointerEventData.InputButton.Left});
487 }
488
489 [Test] // regression test 787563
490 public void SelectWithNoEventSystemShouldNotCrash()
491 {
492 EventSystem.current.enabled = false;
493 selectable.Select();
494 }
495
496 #endregion
497 }
498}