ApplicationStressTests.cs 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. using Terminal.Gui;
  2. using UnitTests;
  3. using Xunit.Abstractions;
  4. namespace StressTests;
  5. public class ApplicationStressTests : TestsAllViews
  6. {
  7. public ApplicationStressTests (ITestOutputHelper output)
  8. {
  9. ConsoleDriver.RunningUnitTests = true;
  10. ConfigurationManager.Locations = ConfigLocations.Default;
  11. }
  12. private static volatile int _tbCounter;
  13. #pragma warning disable IDE1006 // Naming Styles
  14. private static readonly ManualResetEventSlim _wakeUp = new (false);
  15. #pragma warning restore IDE1006 // Naming Styles
  16. private const int NUM_PASSES = 50;
  17. private const int NUM_INCREMENTS = 500;
  18. private const int POLL_MS = 100;
  19. [Theory]
  20. [InlineData (typeof (FakeDriver))]
  21. [InlineData (typeof (NetDriver), Skip = "System.IO.IOException: The handle is invalid")]
  22. //[InlineData (typeof (ANSIDriver))]
  23. [InlineData (typeof (WindowsDriver))]
  24. [InlineData (typeof (CursesDriver), Skip = "Unable to load DLL 'libc' or one of its dependencies: The specified module could not be found. (0x8007007E)")]
  25. public async Task InvokeLeakTest (Type driverType)
  26. {
  27. Application.Init (driverName: driverType.Name);
  28. Random r = new ();
  29. TextField tf = new ();
  30. var top = new Toplevel ();
  31. top.Add (tf);
  32. _tbCounter = 0;
  33. Task task = Task.Run (() => RunTest (r, tf, NUM_PASSES, NUM_INCREMENTS, POLL_MS));
  34. // blocks here until the RequestStop is processed at the end of the test
  35. Application.Run (top);
  36. await task; // Propagate exception if any occurred
  37. Assert.Equal (NUM_INCREMENTS * NUM_PASSES, _tbCounter);
  38. top.Dispose ();
  39. Application.Shutdown ();
  40. return;
  41. static void RunTest (Random r, TextField tf, int numPasses, int numIncrements, int pollMs)
  42. {
  43. for (var j = 0; j < numPasses; j++)
  44. {
  45. _wakeUp.Reset ();
  46. for (var i = 0; i < numIncrements; i++)
  47. {
  48. Launch (r, tf, (j + 1) * numIncrements);
  49. }
  50. while (_tbCounter != (j + 1) * numIncrements) // Wait for tbCounter to reach expected value
  51. {
  52. int tbNow = _tbCounter;
  53. _wakeUp.Wait (pollMs);
  54. if (_tbCounter != tbNow)
  55. {
  56. continue;
  57. }
  58. // No change after wait: Idle handlers added via Application.Invoke have gone missing
  59. Application.Invoke (() => Application.RequestStop ());
  60. throw new TimeoutException (
  61. $"Timeout: Increment lost. _tbCounter ({_tbCounter}) didn't "
  62. + $"change after waiting {pollMs} ms. Failed to reach {(j + 1) * numIncrements} on pass {j + 1}"
  63. );
  64. }
  65. ;
  66. }
  67. Application.Invoke (() => Application.RequestStop ());
  68. }
  69. static void Launch (Random r, TextField tf, int target)
  70. {
  71. Task.Run (
  72. () =>
  73. {
  74. Thread.Sleep (r.Next (2, 4));
  75. Application.Invoke (
  76. () =>
  77. {
  78. tf.Text = $"index{r.Next ()}";
  79. Interlocked.Increment (ref _tbCounter);
  80. if (target == _tbCounter)
  81. {
  82. // On last increment wake up the check
  83. _wakeUp.Set ();
  84. }
  85. }
  86. );
  87. }
  88. );
  89. }
  90. }
  91. }