SpinnerStyleTests.cs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522
  1. #nullable enable
  2. namespace UnitTests_Parallelizable.ViewsTests;
  3. /// <summary>
  4. /// Parallelizable tests for <see cref="SpinnerStyle"/> and its concrete implementations.
  5. /// Tests the abstract properties and behavior of all spinner style variants.
  6. /// </summary>
  7. public class SpinnerStyleTests
  8. {
  9. #region Abstract Properties Tests
  10. [Fact]
  11. public void Custom_HasExpectedDefaults ()
  12. {
  13. SpinnerStyle style = new SpinnerStyle.Custom ();
  14. Assert.False (style.HasSpecialCharacters);
  15. Assert.Empty (style.Sequence);
  16. Assert.False (style.SpinBounce);
  17. Assert.Equal (80, style.SpinDelay);
  18. }
  19. [Fact]
  20. public void Dots_HasExpectedProperties ()
  21. {
  22. SpinnerStyle style = new SpinnerStyle.Dots ();
  23. Assert.False (style.HasSpecialCharacters);
  24. Assert.Equal (10, style.Sequence.Length);
  25. Assert.False (style.SpinBounce);
  26. Assert.Equal (80, style.SpinDelay);
  27. Assert.Equal ("⠋", style.Sequence [0]);
  28. }
  29. [Fact]
  30. public void Line_HasExpectedProperties ()
  31. {
  32. SpinnerStyle style = new SpinnerStyle.Line ();
  33. Assert.False (style.HasSpecialCharacters);
  34. Assert.Equal (4, style.Sequence.Length);
  35. Assert.False (style.SpinBounce);
  36. Assert.Equal (130, style.SpinDelay);
  37. Assert.Equal (["-", @"\", "|", "/"], style.Sequence);
  38. }
  39. #endregion
  40. #region SpinBounce Tests
  41. [Theory]
  42. [InlineData (typeof (SpinnerStyle.Dots4), true)]
  43. [InlineData (typeof (SpinnerStyle.Dots6), true)]
  44. [InlineData (typeof (SpinnerStyle.Dots7), true)]
  45. [InlineData (typeof (SpinnerStyle.GrowVertical), true)]
  46. [InlineData (typeof (SpinnerStyle.GrowHorizontal), true)]
  47. [InlineData (typeof (SpinnerStyle.Balloon2), true)]
  48. [InlineData (typeof (SpinnerStyle.Bounce), true)]
  49. [InlineData (typeof (SpinnerStyle.BouncingBar), true)]
  50. [InlineData (typeof (SpinnerStyle.BouncingBall), true)]
  51. [InlineData (typeof (SpinnerStyle.Pong), true)]
  52. [InlineData (typeof (SpinnerStyle.SoccerHeader), true)]
  53. [InlineData (typeof (SpinnerStyle.Speaker), true)]
  54. [InlineData (typeof (SpinnerStyle.Dots), false)]
  55. [InlineData (typeof (SpinnerStyle.Line), false)]
  56. [InlineData (typeof (SpinnerStyle.SimpleDots), false)]
  57. public void SpinBounce_ReturnsExpectedValue (Type styleType, bool expectedBounce)
  58. {
  59. SpinnerStyle? style = Activator.CreateInstance (styleType) as SpinnerStyle;
  60. Assert.NotNull (style);
  61. Assert.Equal (expectedBounce, style.SpinBounce);
  62. }
  63. #endregion
  64. #region HasSpecialCharacters Tests
  65. [Theory]
  66. [InlineData (typeof (SpinnerStyle.Arrow2), true)]
  67. [InlineData (typeof (SpinnerStyle.Smiley), true)]
  68. [InlineData (typeof (SpinnerStyle.Monkey), true)]
  69. [InlineData (typeof (SpinnerStyle.Hearts), true)]
  70. [InlineData (typeof (SpinnerStyle.Clock), true)]
  71. [InlineData (typeof (SpinnerStyle.Earth), true)]
  72. [InlineData (typeof (SpinnerStyle.Moon), true)]
  73. [InlineData (typeof (SpinnerStyle.Runner), true)]
  74. [InlineData (typeof (SpinnerStyle.Weather), true)]
  75. [InlineData (typeof (SpinnerStyle.Christmas), true)]
  76. [InlineData (typeof (SpinnerStyle.Grenade), true)]
  77. [InlineData (typeof (SpinnerStyle.FingerDance), true)]
  78. [InlineData (typeof (SpinnerStyle.FistBump), true)]
  79. [InlineData (typeof (SpinnerStyle.SoccerHeader), true)]
  80. [InlineData (typeof (SpinnerStyle.MindBlown), true)]
  81. [InlineData (typeof (SpinnerStyle.Speaker), true)]
  82. [InlineData (typeof (SpinnerStyle.OrangePulse), true)]
  83. [InlineData (typeof (SpinnerStyle.BluePulse), true)]
  84. [InlineData (typeof (SpinnerStyle.OrangeBluePulse), true)]
  85. [InlineData (typeof (SpinnerStyle.TimeTravelClock), true)]
  86. [InlineData (typeof (SpinnerStyle.Dots), false)]
  87. [InlineData (typeof (SpinnerStyle.Line), false)]
  88. [InlineData (typeof (SpinnerStyle.SimpleDots), false)]
  89. [InlineData (typeof (SpinnerStyle.Star), false)]
  90. [InlineData (typeof (SpinnerStyle.Arc), false)]
  91. public void HasSpecialCharacters_ReturnsExpectedValue (Type styleType, bool expectedHasSpecial)
  92. {
  93. SpinnerStyle? style = Activator.CreateInstance (styleType) as SpinnerStyle;
  94. Assert.NotNull (style);
  95. Assert.Equal (expectedHasSpecial, style.HasSpecialCharacters);
  96. }
  97. #endregion
  98. #region Sequence Tests
  99. [Fact]
  100. public void Sequence_AllStyles_ReturnsNonEmptyArray ()
  101. {
  102. Type [] allStyles = typeof (SpinnerStyle)
  103. .GetNestedTypes ()
  104. .Where (t => !t.IsAbstract && t.IsSubclassOf (typeof (SpinnerStyle)))
  105. .ToArray ();
  106. foreach (Type styleType in allStyles)
  107. {
  108. if (styleType == typeof (SpinnerStyle.Custom))
  109. {
  110. continue; // Custom has empty sequence by design
  111. }
  112. SpinnerStyle? style = Activator.CreateInstance (styleType) as SpinnerStyle;
  113. Assert.NotNull (style);
  114. Assert.NotEmpty (style.Sequence);
  115. }
  116. }
  117. [Fact]
  118. public void Sequence_AllStyles_ContainsOnlyNonNullStrings ()
  119. {
  120. Type [] allStyles = typeof (SpinnerStyle)
  121. .GetNestedTypes ()
  122. .Where (t => !t.IsAbstract && t.IsSubclassOf (typeof (SpinnerStyle)))
  123. .ToArray ();
  124. foreach (Type styleType in allStyles)
  125. {
  126. SpinnerStyle? style = Activator.CreateInstance (styleType) as SpinnerStyle;
  127. Assert.NotNull (style);
  128. Assert.All (style.Sequence, frame => Assert.NotNull (frame));
  129. }
  130. }
  131. [Theory]
  132. [InlineData (typeof (SpinnerStyle.Dots), 10)]
  133. [InlineData (typeof (SpinnerStyle.Dots2), 8)]
  134. [InlineData (typeof (SpinnerStyle.Line), 4)]
  135. [InlineData (typeof (SpinnerStyle.SimpleDots), 4)]
  136. [InlineData (typeof (SpinnerStyle.Star), 6)]
  137. [InlineData (typeof (SpinnerStyle.Toggle), 2)]
  138. [InlineData (typeof (SpinnerStyle.Arrow), 8)]
  139. [InlineData (typeof (SpinnerStyle.Smiley), 2)]
  140. [InlineData (typeof (SpinnerStyle.Hearts), 5)]
  141. [InlineData (typeof (SpinnerStyle.Clock), 12)]
  142. [InlineData (typeof (SpinnerStyle.Earth), 3)]
  143. public void Sequence_SpecificStyles_HasExpectedLength (Type styleType, int expectedLength)
  144. {
  145. SpinnerStyle? style = Activator.CreateInstance (styleType) as SpinnerStyle;
  146. Assert.NotNull (style);
  147. Assert.Equal (expectedLength, style.Sequence.Length);
  148. }
  149. [Fact]
  150. public void Sequence_Dots8Bit_Has256Frames ()
  151. {
  152. SpinnerStyle style = new SpinnerStyle.Dots8Bit ();
  153. Assert.Equal (256, style.Sequence.Length);
  154. }
  155. [Fact]
  156. public void Sequence_Material_HasProgressBarFrames ()
  157. {
  158. SpinnerStyle style = new SpinnerStyle.Material ();
  159. Assert.NotEmpty (style.Sequence);
  160. // Material style uses both filled (█) and empty (▁) block characters
  161. // to create a progress bar animation
  162. Assert.All (style.Sequence, frame =>
  163. Assert.True (
  164. frame.Contains ("█") || frame.Contains ("▁"),
  165. $"Frame should contain either filled (█) or empty (▁) blocks, but was: {frame}"
  166. )
  167. );
  168. // Verify that at least some frames contain the filled block
  169. Assert.Contains (style.Sequence, frame => frame.Contains ("█"));
  170. // Verify that the sequence shows progression (some frames are all empty at the end)
  171. Assert.Contains (style.Sequence, frame => frame == "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁");
  172. }
  173. #endregion
  174. #region SpinDelay Tests
  175. [Theory]
  176. [InlineData (typeof (SpinnerStyle.Custom), 80)]
  177. [InlineData (typeof (SpinnerStyle.Dots), 80)]
  178. [InlineData (typeof (SpinnerStyle.Line), 130)]
  179. [InlineData (typeof (SpinnerStyle.SimpleDots), 400)]
  180. [InlineData (typeof (SpinnerStyle.Star), 70)]
  181. [InlineData (typeof (SpinnerStyle.GrowVertical), 120)]
  182. [InlineData (typeof (SpinnerStyle.Balloon), 140)]
  183. [InlineData (typeof (SpinnerStyle.Triangle), 50)]
  184. [InlineData (typeof (SpinnerStyle.Arc), 100)]
  185. [InlineData (typeof (SpinnerStyle.Material), 17)]
  186. public void SpinDelay_SpecificStyles_HasExpectedValue (Type styleType, int expectedDelay)
  187. {
  188. SpinnerStyle? style = Activator.CreateInstance (styleType) as SpinnerStyle;
  189. Assert.NotNull (style);
  190. Assert.Equal (expectedDelay, style.SpinDelay);
  191. }
  192. [Fact]
  193. public void SpinDelay_AllStyles_IsPositive ()
  194. {
  195. Type [] allStyles = typeof (SpinnerStyle)
  196. .GetNestedTypes ()
  197. .Where (t => !t.IsAbstract && t.IsSubclassOf (typeof (SpinnerStyle)))
  198. .ToArray ();
  199. foreach (Type styleType in allStyles)
  200. {
  201. SpinnerStyle? style = Activator.CreateInstance (styleType) as SpinnerStyle;
  202. Assert.NotNull (style);
  203. Assert.True (style.SpinDelay > 0, $"{styleType.Name} should have positive SpinDelay");
  204. }
  205. }
  206. #endregion
  207. #region Specific Style Behavior Tests
  208. [Fact]
  209. public void SimpleDots_SequenceStartsWithDot ()
  210. {
  211. SpinnerStyle style = new SpinnerStyle.SimpleDots ();
  212. Assert.StartsWith (".", style.Sequence [0]);
  213. }
  214. [Fact]
  215. public void SimpleDotsScrolling_SequenceShowsProgression ()
  216. {
  217. SpinnerStyle style = new SpinnerStyle.SimpleDotsScrolling ();
  218. Assert.Equal ([". ", ".. ", "...", " ..", " .", " "], style.Sequence);
  219. }
  220. [Fact]
  221. public void Arrow_SequenceContainsAllDirections ()
  222. {
  223. SpinnerStyle style = new SpinnerStyle.Arrow ();
  224. Assert.Contains ("←", style.Sequence);
  225. Assert.Contains ("↑", style.Sequence);
  226. Assert.Contains ("→", style.Sequence);
  227. Assert.Contains ("↓", style.Sequence);
  228. }
  229. [Fact]
  230. public void BouncingBar_SequenceContainsBrackets ()
  231. {
  232. SpinnerStyle style = new SpinnerStyle.BouncingBar ();
  233. Assert.All (style.Sequence, frame => Assert.Contains ("[", frame));
  234. Assert.All (style.Sequence, frame => Assert.Contains ("]", frame));
  235. }
  236. [Fact]
  237. public void BouncingBall_SequenceContainsParentheses ()
  238. {
  239. SpinnerStyle style = new SpinnerStyle.BouncingBall ();
  240. Assert.All (style.Sequence, frame => Assert.Contains ("(", frame));
  241. Assert.All (style.Sequence, frame => Assert.Contains (")", frame));
  242. Assert.All (style.Sequence, frame => Assert.Contains ("●", frame));
  243. }
  244. [Fact]
  245. public void Pong_SequenceContainsVerticalBars ()
  246. {
  247. SpinnerStyle style = new SpinnerStyle.Pong ();
  248. Assert.All (style.Sequence, frame => Assert.StartsWith ("▐", frame));
  249. Assert.All (style.Sequence, frame => Assert.EndsWith ("▌", frame));
  250. }
  251. [Fact]
  252. public void Clock_SequenceContains12Frames ()
  253. {
  254. SpinnerStyle style = new SpinnerStyle.Clock ();
  255. Assert.Equal (12, style.Sequence.Length);
  256. // Verify it contains the 12 hour positions (on-the-hour clock faces)
  257. Assert.Contains ("🕛 ", style.Sequence); // 12 o'clock
  258. Assert.Contains ("🕐 ", style.Sequence); // 1 o'clock
  259. Assert.Contains ("🕑 ", style.Sequence); // 2 o'clock
  260. Assert.Contains ("🕒 ", style.Sequence); // 3 o'clock
  261. Assert.Contains ("🕓 ", style.Sequence); // 4 o'clock
  262. Assert.Contains ("🕔 ", style.Sequence); // 5 o'clock
  263. Assert.Contains ("🕕 ", style.Sequence); // 6 o'clock
  264. Assert.Contains ("🕖 ", style.Sequence); // 7 o'clock
  265. Assert.Contains ("🕗 ", style.Sequence); // 8 o'clock
  266. Assert.Contains ("🕘 ", style.Sequence); // 9 o'clock
  267. Assert.Contains ("🕙 ", style.Sequence); // 10 o'clock
  268. Assert.Contains ("🕚 ", style.Sequence); // 11 o'clock
  269. }
  270. [Fact]
  271. public void Earth_SequenceContainsGlobeEmojis ()
  272. {
  273. SpinnerStyle style = new SpinnerStyle.Earth ();
  274. Assert.Contains ("🌍 ", style.Sequence);
  275. Assert.Contains ("🌎 ", style.Sequence);
  276. Assert.Contains ("🌏 ", style.Sequence);
  277. }
  278. [Fact]
  279. public void Weather_SequenceShowsWeatherProgression ()
  280. {
  281. SpinnerStyle style = new SpinnerStyle.Weather ();
  282. Assert.Contains ("☀️ ", style.Sequence);
  283. Assert.Contains ("⛅️ ", style.Sequence);
  284. Assert.Contains ("🌧 ", style.Sequence);
  285. Assert.Contains ("⛈ ", style.Sequence);
  286. }
  287. [Fact]
  288. public void Shark_SequenceShowsSharkAnimation ()
  289. {
  290. SpinnerStyle style = new SpinnerStyle.Shark ();
  291. // Check that frames show movement
  292. Assert.All (style.Sequence, frame => Assert.Contains ("|", frame));
  293. Assert.All (style.Sequence, frame => Assert.Contains ("_", frame));
  294. }
  295. [Fact]
  296. public void Christmas_SequenceContainsTreeEmojis ()
  297. {
  298. SpinnerStyle style = new SpinnerStyle.Christmas ();
  299. Assert.Contains ("🌲", style.Sequence);
  300. Assert.Contains ("🎄", style.Sequence);
  301. }
  302. [Fact]
  303. public void MindBlown_SequenceShowsProgressiveExpression ()
  304. {
  305. SpinnerStyle style = new SpinnerStyle.MindBlown ();
  306. Assert.Contains ("😐 ", style.Sequence);
  307. Assert.Contains ("😮 ", style.Sequence);
  308. Assert.Contains ("🤯 ", style.Sequence);
  309. Assert.Contains ("💥 ", style.Sequence);
  310. }
  311. #endregion
  312. #region Toggle Style Tests
  313. [Theory]
  314. [InlineData (typeof (SpinnerStyle.Toggle), 2)]
  315. [InlineData (typeof (SpinnerStyle.Toggle2), 2)]
  316. [InlineData (typeof (SpinnerStyle.Toggle3), 2)]
  317. [InlineData (typeof (SpinnerStyle.Toggle4), 4)]
  318. [InlineData (typeof (SpinnerStyle.Toggle5), 2)]
  319. [InlineData (typeof (SpinnerStyle.Toggle6), 2)]
  320. [InlineData (typeof (SpinnerStyle.Toggle7), 2)]
  321. [InlineData (typeof (SpinnerStyle.Toggle8), 2)]
  322. [InlineData (typeof (SpinnerStyle.Toggle9), 2)]
  323. [InlineData (typeof (SpinnerStyle.Toggle10), 3)]
  324. [InlineData (typeof (SpinnerStyle.Toggle11), 2)]
  325. [InlineData (typeof (SpinnerStyle.Toggle12), 2)]
  326. [InlineData (typeof (SpinnerStyle.Toggle13), 3)]
  327. public void ToggleStyles_HaveExpectedFrameCount (Type styleType, int expectedFrames)
  328. {
  329. SpinnerStyle? style = Activator.CreateInstance (styleType) as SpinnerStyle;
  330. Assert.NotNull (style);
  331. Assert.Equal (expectedFrames, style.Sequence.Length);
  332. }
  333. #endregion
  334. #region Dots Style Variant Tests
  335. [Theory]
  336. [InlineData (typeof (SpinnerStyle.Dots))]
  337. [InlineData (typeof (SpinnerStyle.Dots2))]
  338. [InlineData (typeof (SpinnerStyle.Dots3))]
  339. [InlineData (typeof (SpinnerStyle.Dots4))]
  340. [InlineData (typeof (SpinnerStyle.Dots5))]
  341. [InlineData (typeof (SpinnerStyle.Dots6))]
  342. [InlineData (typeof (SpinnerStyle.Dots7))]
  343. [InlineData (typeof (SpinnerStyle.Dots8))]
  344. [InlineData (typeof (SpinnerStyle.Dots9))]
  345. [InlineData (typeof (SpinnerStyle.Dots10))]
  346. [InlineData (typeof (SpinnerStyle.Dots11))]
  347. [InlineData (typeof (SpinnerStyle.Dots12))]
  348. public void DotsStyles_DoNotHaveSpecialCharacters (Type styleType)
  349. {
  350. SpinnerStyle? style = Activator.CreateInstance (styleType) as SpinnerStyle;
  351. Assert.NotNull (style);
  352. Assert.False (style.HasSpecialCharacters);
  353. }
  354. [Fact]
  355. public void Dots8Bit_SequenceCoversAllBrailleCharacters ()
  356. {
  357. SpinnerStyle style = new SpinnerStyle.Dots8Bit ();
  358. // Braille patterns from U+2800 to U+28FF (256 characters)
  359. Assert.Equal (256, style.Sequence.Length);
  360. // Should start with blank braille
  361. Assert.Equal ("⠀", style.Sequence [0]);
  362. // Should end with full braille
  363. Assert.Equal ("⣿", style.Sequence [^1]);
  364. }
  365. #endregion
  366. #region Pulse Style Tests
  367. [Fact]
  368. public void OrangePulse_SequenceShowsPulseEffect ()
  369. {
  370. SpinnerStyle style = new SpinnerStyle.OrangePulse ();
  371. Assert.Contains ("🔸 ", style.Sequence);
  372. Assert.Contains ("🔶 ", style.Sequence);
  373. Assert.Contains ("🟠 ", style.Sequence);
  374. }
  375. [Fact]
  376. public void BluePulse_SequenceShowsPulseEffect ()
  377. {
  378. SpinnerStyle style = new SpinnerStyle.BluePulse ();
  379. Assert.Contains ("🔹 ", style.Sequence);
  380. Assert.Contains ("🔷 ", style.Sequence);
  381. Assert.Contains ("🔵 ", style.Sequence);
  382. }
  383. [Fact]
  384. public void OrangeBluePulse_CombinesBothPulses ()
  385. {
  386. SpinnerStyle style = new SpinnerStyle.OrangeBluePulse ();
  387. // Should contain frames from both orange and blue pulses
  388. Assert.Contains ("🔸 ", style.Sequence);
  389. Assert.Contains ("🟠 ", style.Sequence);
  390. Assert.Contains ("🔹 ", style.Sequence);
  391. Assert.Contains ("🔵 ", style.Sequence);
  392. }
  393. #endregion
  394. #region Edge Cases
  395. [Fact]
  396. public void AllStyles_CanBeInstantiated ()
  397. {
  398. Type [] allStyles = typeof (SpinnerStyle)
  399. .GetNestedTypes ()
  400. .Where (t => !t.IsAbstract && t.IsSubclassOf (typeof (SpinnerStyle)))
  401. .ToArray ();
  402. foreach (Type styleType in allStyles)
  403. {
  404. Exception? exception = Record.Exception (() => Activator.CreateInstance (styleType));
  405. Assert.Null (exception);
  406. }
  407. }
  408. [Fact]
  409. public void AllStyles_PropertiesAreImmutable ()
  410. {
  411. Type [] allStyles = typeof (SpinnerStyle)
  412. .GetNestedTypes ()
  413. .Where (t => !t.IsAbstract && t.IsSubclassOf (typeof (SpinnerStyle)))
  414. .ToArray ();
  415. foreach (Type styleType in allStyles)
  416. {
  417. SpinnerStyle? style1 = Activator.CreateInstance (styleType) as SpinnerStyle;
  418. SpinnerStyle? style2 = Activator.CreateInstance (styleType) as SpinnerStyle;
  419. Assert.NotNull (style1);
  420. Assert.NotNull (style2);
  421. // Same type should have same property values
  422. Assert.Equal (style1.HasSpecialCharacters, style2.HasSpecialCharacters);
  423. Assert.Equal (style1.SpinBounce, style2.SpinBounce);
  424. Assert.Equal (style1.SpinDelay, style2.SpinDelay);
  425. Assert.Equal (style1.Sequence, style2.Sequence);
  426. }
  427. }
  428. #endregion
  429. }