/WorkspaceServer.Tests/PipelineStepTests.cs

https://github.com/dotnet/try · C# · 200 lines · 159 code · 36 blank · 5 comment · 3 complexity · 23007d033f7c07c11502a4925733d79a MD5 · raw file

  1. // Copyright (c) .NET Foundation and contributors. All rights reserved.
  2. // Licensed under the MIT license. See LICENSE file in the project root for full license information.
  3. using System;
  4. using System.Collections.Concurrent;
  5. using System.Linq;
  6. using System.Threading;
  7. using System.Threading.Tasks;
  8. using Clockwise;
  9. using FluentAssertions;
  10. using FluentAssertions.Extensions;
  11. using WorkspaceServer.Packaging;
  12. using Xunit;
  13. namespace WorkspaceServer.Tests
  14. {
  15. public class PipelineStepTests
  16. {
  17. [Fact]
  18. public async Task It_produces_a_new_value_when_there_is_none()
  19. {
  20. var producer = new PipelineStep<string>(() => Task.FromResult("something"));
  21. var value = await producer.GetLatestAsync();
  22. value.Should().Be("something");
  23. }
  24. [Fact]
  25. public async Task It_produces_a_new_value_when_invalidated()
  26. {
  27. var seed = 0;
  28. var producer = new PipelineStep<int>(() => Task.FromResult(Interlocked.Increment(ref seed)));
  29. var value = await producer.GetLatestAsync();
  30. value.Should().Be(1);
  31. producer.Invalidate();
  32. var newValue = await producer.GetLatestAsync();
  33. newValue.Should().Be(2);
  34. }
  35. [Fact]
  36. public async Task It_propagates_exception()
  37. {
  38. var producer = new PipelineStep<int>(() => throw new InvalidOperationException());
  39. producer.Awaiting(p => p.GetLatestAsync())
  40. .Should()
  41. .Throw<InvalidOperationException>();
  42. }
  43. [Fact]
  44. public async Task It_remains_invalid_if_exceptions_are_thrown()
  45. {
  46. var seed = 0;
  47. var producer = new PipelineStep<int>(() =>
  48. {
  49. var next = Interlocked.Increment(ref seed);
  50. if (next == 1)
  51. {
  52. throw new InvalidOperationException();
  53. }
  54. return Task.FromResult(next);
  55. });
  56. producer.Awaiting(p => p.GetLatestAsync())
  57. .Should()
  58. .Throw<InvalidOperationException>();
  59. var value = await producer.GetLatestAsync();
  60. value.Should().Be(2);
  61. }
  62. [Fact]
  63. public async Task It_does_not_produces_a_new_value_when_invalidated_until_asked_for_latest_value()
  64. {
  65. var seed = 0;
  66. var producer = new PipelineStep<int>(() => Task.FromResult(Interlocked.Increment(ref seed)));
  67. var value = await producer.GetLatestAsync();
  68. value.Should().Be(1);
  69. producer.Invalidate();
  70. seed.Should().Be(1);
  71. }
  72. [Fact]
  73. public async Task It_retains_the_latest_value()
  74. {
  75. var seed = 0;
  76. var producer = new PipelineStep<int>(() => Task.FromResult(Interlocked.Increment(ref seed)));
  77. var value1 = await producer.GetLatestAsync();
  78. var value2 = await producer.GetLatestAsync();
  79. var value3 = await producer.GetLatestAsync();
  80. value1.Should().Be(1);
  81. value2.Should().Be(1);
  82. value3.Should().Be(1);
  83. }
  84. [Fact]
  85. public async Task It_returns_same_value_to_concurrent_requests()
  86. {
  87. var seed = 0;
  88. var barrier = new Barrier(3);
  89. var producer = new PipelineStep<int>(() => Task.FromResult(++seed));
  90. var firstConsumer = Task.Run(() =>
  91. {
  92. barrier.SignalAndWait();
  93. return producer.GetLatestAsync();
  94. });
  95. var secondConsumer = Task.Run(() =>
  96. {
  97. barrier.SignalAndWait();
  98. return producer.GetLatestAsync();
  99. });
  100. var thirdConsumer = Task.Run(() =>
  101. {
  102. barrier.SignalAndWait();
  103. return producer.GetLatestAsync();
  104. });
  105. var values = await Task.WhenAll(firstConsumer, secondConsumer, thirdConsumer);
  106. values.Should().HaveCount(3).And.OnlyContain(i => i == 1);
  107. }
  108. [Fact(Skip="flaky")]
  109. public async Task When_invalidated_while_producing_a_value_the_consumer_waiting_will_wait_for_latest_production_to_be_finished()
  110. {
  111. var seed = 0;
  112. var barrier = new Barrier(2);
  113. PipelineStep<int> producer = null;
  114. producer = new PipelineStep<int>(() =>
  115. {
  116. // will require all consumer to reach this point to move on
  117. barrier.SignalAndWait(3.Seconds());
  118. barrier.RemoveParticipant();
  119. producer.Invalidate();
  120. return Task.FromResult(Interlocked.Increment(ref seed));
  121. });
  122. var values= await Task.WhenAll(
  123. producer.GetLatestAsync(),
  124. producer.GetLatestAsync());
  125. values.Should().BeEquivalentTo(2, 2);
  126. // var values = await Task.WhenAll(firstConsumer, secondConsumer);
  127. // values.Should().HaveCount(2).And.OnlyContain(i => i == 2);
  128. }
  129. [Fact]
  130. public async Task Sequence_of_steps_produce_a_value()
  131. {
  132. var step1 = new PipelineStep<int>(() => Task.FromResult(1));
  133. var step2 = step1.Then((number) => Task.FromResult($"{number} {number}"));
  134. var value1 = await step2.GetLatestAsync();
  135. value1.Should().Be("1 1");
  136. }
  137. [Fact]
  138. public async Task Invalidating_a_step_in_a_sequence_causes_only_that_step_to_re_evaluate()
  139. {
  140. var seed1 = 0;
  141. var step1 = new PipelineStep<int>(() => Task.FromResult(Interlocked.Increment(ref seed1)));
  142. var seed2 = 0;
  143. var step2 = step1.Then((number) => Task.FromResult($"{number} {Interlocked.Increment(ref seed2)}"));
  144. await step2.GetLatestAsync();
  145. step2.Invalidate();
  146. var value = await step2.GetLatestAsync();
  147. value.Should().Be("1 2");
  148. }
  149. [Fact]
  150. public async Task Invalidating_a_step_in_a_sequence_causes_all_successor_to_evaluate()
  151. {
  152. var seed1 = 0;
  153. var seed2 = 0;
  154. var seed3 = 0;
  155. var step1 = new PipelineStep<int>(() => Task.FromResult(Interlocked.Increment(ref seed1)));
  156. var step2 = step1.Then((number) => Task.FromResult($"{number} {Interlocked.Increment(ref seed2)}"));
  157. var step3 = step2.Then((text) => Task.FromResult($"{text} {Interlocked.Increment(ref seed3)}"));
  158. await step3.GetLatestAsync();
  159. step2.Invalidate();
  160. var value = await step3.GetLatestAsync();
  161. value.Should().Be("1 2 2");
  162. }
  163. }
  164. }