RangeBaseTests.cs 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. using System;
  2. using System.ComponentModel;
  3. using Avalonia.Controls.Primitives;
  4. using Avalonia.Controls.Templates;
  5. using Avalonia.Data;
  6. using Avalonia.Layout;
  7. using Avalonia.Markup.Data;
  8. using Avalonia.Styling;
  9. using Avalonia.UnitTests;
  10. using Xunit;
  11. namespace Avalonia.Controls.UnitTests.Primitives
  12. {
  13. public class RangeBaseTests
  14. {
  15. [Fact]
  16. public void Maximum_Should_Be_Coerced_To_Minimum()
  17. {
  18. var target = new TestRange
  19. {
  20. Minimum = 100,
  21. Maximum = 50,
  22. };
  23. var root = new TestRoot(target);
  24. Assert.Equal(100, target.Minimum);
  25. Assert.Equal(100, target.Maximum);
  26. }
  27. [Fact]
  28. public void Value_Should_Be_Coerced_To_Range()
  29. {
  30. var target = new TestRange
  31. {
  32. Minimum = 0,
  33. Maximum = 50,
  34. Value = 100,
  35. };
  36. var root = new TestRoot(target);
  37. Assert.Equal(0, target.Minimum);
  38. Assert.Equal(50, target.Maximum);
  39. Assert.Equal(50, target.Value);
  40. }
  41. [Fact]
  42. public void Changing_Minimum_Should_Coerce_Value_And_Maximum()
  43. {
  44. var target = new TestRange
  45. {
  46. Minimum = 0,
  47. Maximum = 100,
  48. Value = 50,
  49. };
  50. var root = new TestRoot(target);
  51. target.Minimum = 200;
  52. Assert.Equal(200, target.Minimum);
  53. Assert.Equal(200, target.Maximum);
  54. Assert.Equal(200, target.Value);
  55. }
  56. [Fact]
  57. public void Changing_Maximum_Should_Coerce_Value()
  58. {
  59. var target = new TestRange
  60. {
  61. Minimum = 0,
  62. Maximum = 100,
  63. Value = 100,
  64. };
  65. var root = new TestRoot(target);
  66. target.Maximum = 50;
  67. Assert.Equal(0, target.Minimum);
  68. Assert.Equal(50, target.Maximum);
  69. Assert.Equal(50, target.Value);
  70. }
  71. [Theory]
  72. [InlineData(true)]
  73. [InlineData(false)]
  74. public void SetValue_Should_Not_Cause_StackOverflow(bool useXamlBinding)
  75. {
  76. var viewModel = new TestStackOverflowViewModel()
  77. {
  78. Value = 50
  79. };
  80. Track track = null;
  81. var target = new TestRange()
  82. {
  83. Template = new FuncControlTemplate<RangeBase>((c, scope) =>
  84. {
  85. track = new Track()
  86. {
  87. Width = 100,
  88. Orientation = Orientation.Horizontal,
  89. [~~Track.MinimumProperty] = c[~~RangeBase.MinimumProperty],
  90. [~~Track.MaximumProperty] = c[~~RangeBase.MaximumProperty],
  91. Name = "PART_Track",
  92. Thumb = new Thumb()
  93. }.RegisterInNameScope(scope);
  94. if (useXamlBinding)
  95. {
  96. track.Bind(Track.ValueProperty, new Binding("Value")
  97. {
  98. Mode = BindingMode.TwoWay,
  99. Source = c,
  100. Priority = BindingPriority.Style
  101. });
  102. }
  103. else
  104. {
  105. track[~~Track.ValueProperty] = c[~~RangeBase.ValueProperty];
  106. }
  107. return track;
  108. }),
  109. Minimum = 0,
  110. Maximum = 100,
  111. DataContext = viewModel
  112. };
  113. target.Bind(TestRange.ValueProperty, new Binding("Value") { Mode = BindingMode.TwoWay });
  114. target.ApplyTemplate();
  115. track.Measure(new Size(100, 0));
  116. track.Arrange(new Rect(0, 0, 100, 0));
  117. Assert.Equal(1, viewModel.SetterInvokedCount);
  118. // Issues #855 and #824 were causing a StackOverflowException at this point.
  119. target.Value = 51.001;
  120. Assert.Equal(2, viewModel.SetterInvokedCount);
  121. double expected = 51;
  122. Assert.Equal(expected, viewModel.Value);
  123. Assert.Equal(expected, target.Value);
  124. Assert.Equal(expected, track.Value);
  125. }
  126. [Fact]
  127. public void Coercion_Should_Not_Be_Done_During_Initialization()
  128. {
  129. var target = new TestRange();
  130. target.BeginInit();
  131. var root = new TestRoot(target);
  132. target.Minimum = 1;
  133. Assert.Equal(0, target.Value);
  134. target.Value = 50;
  135. target.EndInit();
  136. Assert.Equal(50, target.Value);
  137. }
  138. [Fact]
  139. public void Coercion_Should_Be_Done_After_Initialization()
  140. {
  141. var target = new TestRange();
  142. target.BeginInit();
  143. var root = new TestRoot(target);
  144. target.Minimum = 1;
  145. target.EndInit();
  146. Assert.Equal(1, target.Value);
  147. }
  148. private class TestRange : RangeBase
  149. {
  150. }
  151. private class TestStackOverflowViewModel : INotifyPropertyChanged
  152. {
  153. public int SetterInvokedCount { get; private set; }
  154. public const int MaxInvokedCount = 1000;
  155. private double _value;
  156. public event PropertyChangedEventHandler PropertyChanged;
  157. public double Value
  158. {
  159. get { return _value; }
  160. set
  161. {
  162. if (_value != value)
  163. {
  164. SetterInvokedCount++;
  165. if (SetterInvokedCount < MaxInvokedCount)
  166. {
  167. _value = (int)value;
  168. if (_value > 75) _value = 75;
  169. if (_value < 25) _value = 25;
  170. }
  171. else
  172. {
  173. _value = value;
  174. }
  175. PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Value)));
  176. }
  177. }
  178. }
  179. }
  180. }
  181. }