MainLoopCoordinatorTests.cs 3.8 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091
  1. using System.Collections.Concurrent;
  2. using Microsoft.Extensions.Logging;
  3. using Moq;
  4. namespace UnitTests.ConsoleDrivers.V2;
  5. public class MainLoopCoordinatorTests
  6. {
  7. [Fact]
  8. public void TestMainLoopCoordinator_InputCrashes_ExceptionSurfacesMainThread ()
  9. {
  10. var mockLogger = new Mock<ILogger> ();
  11. var beforeLogger = Logging.Logger;
  12. Logging.Logger = mockLogger.Object;
  13. var c = new MainLoopCoordinator<char> (new TimedEvents (),
  14. // Runs on a separate thread (input thread)
  15. () => throw new Exception ("Crash on boot"),
  16. // Rest runs on main thread
  17. new ConcurrentQueue<char> (),
  18. Mock.Of <IInputProcessor>(),
  19. ()=>Mock.Of<IConsoleOutput>(),
  20. Mock.Of<IMainLoop<char>>());
  21. // StartAsync boots the main loop and the input thread. But if the input class bombs
  22. // on startup it is important that the exception surface at the call site and not lost
  23. var ex = Assert.ThrowsAsync<AggregateException>(c.StartAsync).Result;
  24. Assert.Equal ("Crash on boot", ex.InnerExceptions [0].Message);
  25. // Restore the original null logger to be polite to other tests
  26. Logging.Logger = beforeLogger;
  27. // Logs should explicitly call out that input loop crashed.
  28. mockLogger.Verify (
  29. l => l.Log (LogLevel.Critical,
  30. It.IsAny<EventId> (),
  31. It.Is<It.IsAnyType> ((v, t) => v.ToString () == "Input loop crashed"),
  32. It.IsAny<Exception> (),
  33. It.IsAny<Func<It.IsAnyType, Exception, string>> ())
  34. , Times.Once);
  35. }
  36. /*
  37. [Fact]
  38. public void TestMainLoopCoordinator_InputExitsImmediately_ExceptionRaisedInMainThread ()
  39. {
  40. // Runs on a separate thread (input thread)
  41. // But because it's just a mock it immediately exists
  42. var mockInputFactoryMethod = () => Mock.Of<IConsoleInput<char>> ();
  43. var mockOutput = Mock.Of<IConsoleOutput> ();
  44. var mockInputProcessor = Mock.Of<IInputProcessor> ();
  45. var inputQueue = new ConcurrentQueue<char> ();
  46. var timedEvents = new TimedEvents ();
  47. var mainLoop = new MainLoop<char> ();
  48. mainLoop.Initialize (timedEvents,
  49. inputQueue,
  50. mockInputProcessor,
  51. mockOutput
  52. );
  53. var c = new MainLoopCoordinator<char> (timedEvents,
  54. mockInputFactoryMethod,
  55. inputQueue,
  56. mockInputProcessor,
  57. ()=>mockOutput,
  58. mainLoop
  59. );
  60. // TODO: This test has race condition
  61. //
  62. // * When the input loop exits it can happen
  63. // * - During boot
  64. // * - After boot
  65. // *
  66. // * If it happens in boot you get input exited
  67. // * If it happens after you get "Input loop exited early (stop not called)"
  68. //
  69. // Because the console input class does not block - i.e. breaks contract
  70. // We need to let the user know input has silently exited and all has gone bad.
  71. var ex = Assert.ThrowsAsync<Exception> (c.StartAsync).Result;
  72. Assert.Equal ("Input loop exited during startup instead of entering read loop properly (i.e. and blocking)", ex.Message);
  73. }*/
  74. }