public async Task StartAsync_CommandThrowsInvalidOperationException_RaisesErrorOccuredEvent() { // ARRANGE var contextMock = new Mock <ICommandLoopContext>(); contextMock.SetupGet(x => x.HasNextIteration).Returns(true); contextMock.SetupGet(x => x.CanPlayerGiveCommand).Returns(true); var context = contextMock.Object; var tcs = new TaskCompletionSource <bool>(TaskCreationOptions.RunContinuationsAsynchronously); var eventWasInvokedTask = tcs.Task; var commandMock = new Mock <ICommand>(); commandMock.Setup(x => x.Execute()).Throws <InvalidOperationException>(); var command = commandMock.Object; var commandPool = new TestCommandPool(); commandPool.Push(command); var commandLoopUpdater = new CommandLoopUpdater(context, commandPool); ErrorOccuredEventArgs?expectedRaisedErrorArgs = null; var raiseCount = 0; commandLoopUpdater.ErrorOccured += (s, e) => { expectedRaisedErrorArgs = e; // Count raises to prevent errors with multiple events. raiseCount++; tcs.SetResult(true); }; // ACT using var monitor = commandLoopUpdater.Monitor(); #pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed commandLoopUpdater.StartAsync(CancellationToken.None).ConfigureAwait(false); #pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed var expectedEventWasRaised = await eventWasInvokedTask.ConfigureAwait(false); // ASSERT expectedEventWasRaised.Should().BeTrue(); expectedRaisedErrorArgs.Should().BeOfType <CommandErrorOccuredEventArgs>(); raiseCount.Should().Be(1); }
public async Task StartAsync_CommandIsRepeatableOnce_ExecutesCommandTwice() { // ARRANGE var contextMock = new Mock <ICommandLoopContext>(); contextMock.SetupGet(x => x.HasNextIteration).Returns(true); contextMock.SetupGet(x => x.CanPlayerGiveCommand).Returns(true); var context = contextMock.Object; var tcs = new TaskCompletionSource <bool>(TaskCreationOptions.RunContinuationsAsynchronously); var waitCommandExecutedTask = tcs.Task; var repeatIteration = 0; var commandMock = new Mock <IRepeatableCommand>(); commandMock.Setup(x => x.Execute()).Callback(() => { if (repeatIteration >= 1) { tcs.SetResult(true); } }); commandMock.Setup(x => x.CanRepeat()).Callback(() => { repeatIteration++; }) .Returns(() => repeatIteration <= 1); commandMock.Setup(x => x.CanExecute()).Returns(new CanExecuteCheckResult { IsSuccess = true }); var command = commandMock.Object; var commandPool = new TestCommandPool(); commandPool.Push(command); var commandLoopUpdater = new CommandLoopUpdater(context, commandPool); // ACT #pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed commandLoopUpdater.StartAsync(CancellationToken.None).ConfigureAwait(false); #pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed await waitCommandExecutedTask.ConfigureAwait(false); // ASSERT commandMock.Verify(x => x.Execute(), Times.Exactly(2)); }
public async Task StopAsync_StopAfterStart_CommandsAreNotRequestedAfterStop() { // ARRANGE var contextMock = new Mock <ICommandLoopContext>(); contextMock.SetupGet(x => x.HasNextIteration).Returns(true); contextMock.SetupGet(x => x.CanPlayerGiveCommand).Returns(true); var context = contextMock.Object; var command = Mock.Of <ICommand>(); var commandPoolMock = new Mock <ICommandPool>(); var semaphore = new SemaphoreSlim(0, 1); commandPoolMock.Setup(x => x.Pop()).Returns(() => { Task.Delay(100).Wait(); semaphore.Release(); return(command); }); var commandPool = commandPoolMock.Object; var commandLoop = new CommandLoopUpdater(context, commandPool); // ACT var commandLoopTask = commandLoop.StartAsync(CancellationToken.None); AggregateException?commandLoopTaskException = null; await semaphore.WaitAsync().ConfigureAwait(false); await commandLoop.StopAsync().ConfigureAwait(false); Func <Task> act = async() => { await commandLoopTask.ContinueWith(task => commandLoopTaskException = task.Exception, TaskContinuationOptions.OnlyOnFaulted).ConfigureAwait(false); }; // ASSERT commandPoolMock.Verify(x => x.Pop(), Times.AtMost(2)); await act.Should().ThrowAsync <TaskCanceledException>().ConfigureAwait(false); commandLoopTaskException.Should().BeNull(); }
public async Task StartAsync_CommandInPoolAfterStart_ExecutesCommand() { // ARRANGE var contextMock = new Mock <ICommandLoopContext>(); contextMock.SetupGet(x => x.HasNextIteration).Returns(true); contextMock.SetupGet(x => x.CanPlayerGiveCommand).Returns(true); var context = contextMock.Object; var tcs = new TaskCompletionSource <bool>(TaskCreationOptions.RunContinuationsAsynchronously); var testTask = tcs.Task; var commandMock = new Mock <ICommand>(); commandMock.Setup(x => x.Execute()).Callback(() => tcs.SetResult(true)); var command = commandMock.Object; var commandPool = new TestCommandPool(); var commandLoopUpdater = new CommandLoopUpdater(context, commandPool); // ACT #pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed commandLoopUpdater.StartAsync(CancellationToken.None).ConfigureAwait(false); #pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed commandPool.Push(command); // Delay to take some time to command loop updater to perform some iterations. await testTask.ConfigureAwait(false); // ASSERT commandMock.Verify(x => x.Execute(), Times.Once); }
public async Task StartAsync_CommandInPoolBeforeStart_RaisesCompleteCommandEvent() { // ARRANGE var contextMock = new Mock <ICommandLoopContext>(); contextMock.SetupGet(x => x.HasNextIteration).Returns(true); contextMock.SetupGet(x => x.CanPlayerGiveCommand).Returns(true); var context = contextMock.Object; var tcs = new TaskCompletionSource <bool>(TaskCreationOptions.RunContinuationsAsynchronously); var eventTask = tcs.Task; var commandMock = new Mock <ICommand>(); var command = commandMock.Object; var commandPool = new TestCommandPool(); commandPool.Push(command); var commandLoopUpdater = new CommandLoopUpdater(context, commandPool); commandLoopUpdater.CommandProcessed += (s, e) => { tcs.SetResult(true); }; // ACT #pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed commandLoopUpdater.StartAsync(CancellationToken.None).ConfigureAwait(false); #pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed // ASSERT var expectedEventWasRaised = await eventTask.ConfigureAwait(false); expectedEventWasRaised.Should().BeTrue(); }
/// <inheritdoc /> public async Task <GameScreen> StartProcessingAsync(GameState gameState) { var serviceScope = gameState.ServiceScope; var player = serviceScope.ServiceProvider.GetRequiredService <IPlayer>(); var commandPool = serviceScope.ServiceProvider.GetRequiredService <ICommandPool>(); var animationBlockerService = serviceScope.ServiceProvider.GetRequiredService <IAnimationBlockerService>(); var humanTaskSource = serviceScope.ServiceProvider .GetRequiredService <IActorTaskSource <ISectorTaskSourceContext> >(); var playerState = serviceScope.ServiceProvider.GetRequiredService <ISectorUiState>(); var inventoryState = serviceScope.ServiceProvider.GetRequiredService <IInventoryState>(); var globeLoopUpdateContext = new GlobeLoopContext(player); var globeLoop = new GlobeLoopUpdater(globeLoopUpdateContext, animationBlockerService); var humanTaskSourceCasted = (IHumanActorTaskSource <ISectorTaskSourceContext>)humanTaskSource; var commandLoopContext = new CommandLoopContext(player, humanTaskSourceCasted, animationBlockerService); var commandLoop = new CommandLoopUpdater(commandLoopContext, commandPool); var globe = player.Globe; // Play using var cancellationTokenSource = new CancellationTokenSource(); var cancellationToken = cancellationTokenSource.Token; globeLoop.ErrorOccured += (s, e) => { Console.WriteLine(e.Exception); }; globeLoop.Start(); commandLoop.ErrorOccured += (s, e) => { Console.WriteLine(e.Exception); }; commandLoop.CommandAutoExecuted += (s, e) => { Console.WriteLine("Auto execute last command"); }; commandLoop.CommandProcessed += (s, e) => { inventoryState.SelectedProp = null; playerState.SelectedViewModel = null; }; var commandLoopTask = commandLoop.StartAsync(cancellationToken); #pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed commandLoopTask.ContinueWith(task => Console.WriteLine(task.Exception), TaskContinuationOptions.OnlyOnFaulted); commandLoopTask.ContinueWith(task => Console.WriteLine("Game loop stopped."), TaskContinuationOptions.OnlyOnCanceled); #pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed do { var playerActor = (from sectorNode in globe.SectorNodes from actor in sectorNode.Sector.ActorManager.Items where actor.Person == player.MainPerson select actor).SingleOrDefault(); playerState.ActiveActor = new ActorViewModel { Actor = playerActor }; PrintState(playerState.ActiveActor.Actor); PrintCommandDescriptions(); Console.WriteLine($"{UiResource.InputPrompt}:"); var inputText = Console.ReadLine(); if (inputText.StartsWith("m", StringComparison.InvariantCultureIgnoreCase)) { var playerActorSectorNode = GetPlayerSectorNode(player, globe); HandleMoveCommand(serviceScope, playerState, commandPool, playerActorSectorNode, inputText); } else if (inputText.StartsWith("look", StringComparison.InvariantCultureIgnoreCase)) { var playerActorSectorNode = GetPlayerSectorNode(player, globe); HandleLookCommand(playerState, playerActorSectorNode, inputText); } else if (inputText.StartsWith("idle", StringComparison.InvariantCultureIgnoreCase)) { HandleIdleCommand(serviceScope, commandPool); } else if (inputText.StartsWith("dead", StringComparison.InvariantCultureIgnoreCase)) { HandleDeadCommand(player); } else if (inputText.StartsWith("transit", StringComparison.InvariantCultureIgnoreCase)) { HandleSectorTransitCommand(serviceScope, commandPool); } else if (inputText.StartsWith("attack", StringComparison.InvariantCultureIgnoreCase)) { var playerActorSectorNode = GetPlayerSectorNode(player, globe); HandleAttackCommand(inputText, serviceScope, playerState, playerActorSectorNode, commandPool); } else if (inputText.StartsWith("exit", StringComparison.InvariantCultureIgnoreCase)) { break; } await Task.Run(() => { while (true) { if (((IHumanActorTaskSource <ISectorTaskSourceContext>)humanTaskSource).CanIntent() && !commandLoop.HasPendingCommands()) { break; } } }).ConfigureAwait(false); await animationBlockerService.WaitBlockersAsync().ConfigureAwait(false); } while (!player.MainPerson.GetModule <ISurvivalModule>().IsDead); cancellationTokenSource.Cancel(); return(GameScreen.Scores); }