You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

LifecycleObjectTests.cs 8.5 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. // Copyright (c) Microsoft Corporation. All rights reserved.
  2. // LifecycleObjectTests.cs
  3. using FluentAssertions;
  4. using Microsoft.AutoGen.AgentChat.GroupChat;
  5. using Xunit;
  6. namespace Microsoft.AutoGen.AgentChat.Tests;
  7. internal sealed class LifecycleObjectFixture : LifecycleObject
  8. {
  9. public enum LifecycleState
  10. {
  11. Deinitialized,
  12. Initialized
  13. }
  14. public LifecycleState State { get; private set; }
  15. public Func<ValueTask> DeinitializeOverride { get; set; } = () => ValueTask.CompletedTask;
  16. public Func<ValueTask> InitializeOverride { get; set; } = () => ValueTask.CompletedTask;
  17. public Action InitializeErrorOverride { get; set; }
  18. public Action DeinitializeErrorOverride { get; set; }
  19. private int initializeCallCount;
  20. private int deinitializeCallCount;
  21. private int initializeErrorCount;
  22. private int deinitializeErrorCount;
  23. public int InitializeCallCount => this.initializeCallCount;
  24. public int DeinitializeCallCount => this.deinitializeCallCount;
  25. public int InitializeErrorCount => this.initializeErrorCount;
  26. public int DeinitializeErrorCount => this.deinitializeErrorCount;
  27. public LifecycleObjectFixture()
  28. {
  29. this.State = LifecycleState.Deinitialized;
  30. this.InitializeErrorOverride = base.OnInitializeError;
  31. this.DeinitializeErrorOverride = base.OnDeinitializeError;
  32. }
  33. protected override void OnInitializeError()
  34. {
  35. Interlocked.Increment(ref this.initializeErrorCount);
  36. this.InitializeErrorOverride();
  37. }
  38. protected override void OnDeinitializeError()
  39. {
  40. Interlocked.Increment(ref this.deinitializeErrorCount);
  41. this.DeinitializeErrorOverride();
  42. }
  43. protected sealed override ValueTask DeinitializeCore()
  44. {
  45. Interlocked.Increment(ref this.deinitializeCallCount);
  46. this.State = LifecycleState.Deinitialized;
  47. return DeinitializeOverride();
  48. }
  49. protected sealed override ValueTask InitializeCore()
  50. {
  51. Interlocked.Increment(ref this.initializeCallCount);
  52. this.State = LifecycleState.Initialized;
  53. return InitializeOverride();
  54. }
  55. }
  56. [Trait("Category", "UnitV2")]
  57. public class LifecycleObjectTests
  58. {
  59. /*
  60. We should be testing the following conditions:
  61. - SmokeTest: Happy path: Initialize, Deinitialize, Initialize, Deinitialize, validate states and call counts
  62. - Error handling: Initialize, Initialize; Deinitialize; Initialize, Deinitialize, Deinitialize
  63. */
  64. [Fact]
  65. public async Task InitializeAndDeinitialize_SucceedsTwice()
  66. {
  67. // Arrange
  68. LifecycleObjectFixture fixture = new();
  69. // Validate preconditions
  70. fixture.State.Should().Be(LifecycleObjectFixture.LifecycleState.Deinitialized, "LifecycleObject should be in Deinitialized state initially");
  71. fixture.InitializeCallCount.Should().Be(0, "Initialize should not have been called yet");
  72. fixture.DeinitializeCallCount.Should().Be(0, "Deinitialize should not have been called yet");
  73. fixture.InitializeErrorCount.Should().Be(0, "there should be no initialization errors");
  74. fixture.DeinitializeErrorCount.Should().Be(0, "there should be no deinitialization errors");
  75. // Act
  76. await fixture.InitializeAsync();
  77. // Validate postconditions 1
  78. fixture.State.Should().Be(LifecycleObjectFixture.LifecycleState.Initialized, "LifecycleObject should be in Initialized state after Initialize");
  79. fixture.InitializeCallCount.Should().Be(1, "Initialize should have been called once");
  80. fixture.DeinitializeCallCount.Should().Be(0, "Deinitialize should not have been called yet");
  81. fixture.InitializeErrorCount.Should().Be(0, "there should be no initialization errors");
  82. fixture.DeinitializeErrorCount.Should().Be(0, "there should be no deinitialization errors");
  83. // Act 2
  84. await fixture.DeinitializeAsync();
  85. // Validate postconditions 2
  86. fixture.State.Should().Be(LifecycleObjectFixture.LifecycleState.Deinitialized, "LifecycleObject should be in Deinitialized state after Deinitialize");
  87. fixture.InitializeCallCount.Should().Be(1, "Initialize should have been called once");
  88. fixture.DeinitializeCallCount.Should().Be(1, "Deinitialize should have been called once");
  89. fixture.InitializeErrorCount.Should().Be(0, "there should be no initialization errors");
  90. fixture.DeinitializeErrorCount.Should().Be(0, "there should be no deinitialization errors");
  91. // Act 3
  92. await fixture.InitializeAsync();
  93. // Validate postconditions 3
  94. fixture.State.Should().Be(LifecycleObjectFixture.LifecycleState.Initialized, "LifecycleObject should be in Initialized state after Initialize");
  95. fixture.InitializeCallCount.Should().Be(2, "Initialize should have been called twice");
  96. fixture.DeinitializeCallCount.Should().Be(1, "Deinitialize should have been called once");
  97. fixture.InitializeErrorCount.Should().Be(0, "there should be no initialization errors");
  98. fixture.DeinitializeErrorCount.Should().Be(0, "there should be no deinitialization errors");
  99. // Act 4
  100. await fixture.DeinitializeAsync();
  101. // Validate postconditions 4
  102. fixture.State.Should().Be(LifecycleObjectFixture.LifecycleState.Deinitialized, "LifecycleObject should be in Deinitialized state after Deinitialize");
  103. fixture.InitializeCallCount.Should().Be(2, "Initialize should have been called twice");
  104. fixture.DeinitializeCallCount.Should().Be(2, "Deinitialize should have been called twice");
  105. fixture.InitializeErrorCount.Should().Be(0, "there should be no initialization errors");
  106. fixture.DeinitializeErrorCount.Should().Be(0, "there should be no deinitialization errors");
  107. }
  108. [Fact]
  109. public async Task Initialize_FailsWhenInitialized()
  110. {
  111. // Testing two things: We should expect InvalidOperationException by default, and that we called into the override
  112. // Arrange
  113. LifecycleObjectFixture fixture = new();
  114. await fixture.InitializeAsync();
  115. // Act
  116. Func<Task> secondInitialization = async () => await fixture.InitializeAsync();
  117. // Assert
  118. await secondInitialization.Should().ThrowAsync<InvalidOperationException>("LifecycleObject.InitializeAsync should throw InvalidOperationException when initialized");
  119. fixture.InitializeCallCount.Should().Be(1, "Initialize should have been called once successfully");
  120. fixture.InitializeErrorCount.Should().Be(1, "there should be one initialization error");
  121. fixture.DeinitializeCallCount.Should().Be(0, "Deinitialize should not have been called yet");
  122. fixture.DeinitializeErrorCount.Should().Be(0, "there should be no deinitialization errors");
  123. }
  124. [Fact]
  125. public async Task Deinitialize_FailsWhenNotInitialized()
  126. {
  127. // Arrange
  128. LifecycleObjectFixture fixture = new();
  129. // Act
  130. Func<Task> deinitialization = async () => await fixture.DeinitializeAsync();
  131. // Assert
  132. await deinitialization.Should().ThrowAsync<InvalidOperationException>("LifecycleObject.DeinitializeAsync should throw InvalidOperationException when not initialized");
  133. fixture.InitializeCallCount.Should().Be(0, "Initialize should not have been called yet");
  134. fixture.InitializeErrorCount.Should().Be(0, "there should be no initialization errors");
  135. fixture.DeinitializeCallCount.Should().Be(0, "Deinitialize should not have been called successfully yet");
  136. fixture.DeinitializeErrorCount.Should().Be(1, "there should be one deinitialization error");
  137. // Act 2
  138. await fixture.InitializeAsync();
  139. await fixture.DeinitializeAsync();
  140. Func<Task> secondDeinitialization = async () => await fixture.DeinitializeAsync();
  141. // Assert 2
  142. await secondDeinitialization.Should().ThrowAsync<InvalidOperationException>("LifecycleObject.DeinitializeAsync should throw InvalidOperationException when not initialized");
  143. fixture.InitializeCallCount.Should().Be(1, "Initialize should have been called once successfully");
  144. fixture.InitializeErrorCount.Should().Be(0, "there should be no initialization errors");
  145. fixture.DeinitializeCallCount.Should().Be(1, "Deinitialize should have been called successfully once");
  146. fixture.DeinitializeErrorCount.Should().Be(2, "there should be two deinitialization errors");
  147. }
  148. }