Skip to content

Commit 5ddcb76

Browse files
committed
test: add test for SpeechSynthesizerSession
1 parent 718e017 commit 5ddcb76

File tree

8 files changed

+267
-24
lines changed

8 files changed

+267
-24
lines changed

src/Cnblogs.DashScope.Core/DashScopeClientWebSocket.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,8 @@ public async Task CloseAsync(CancellationToken cancellationToken = default)
239239
{
240240
await _socket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closing", cancellationToken);
241241
State = DashScopeWebSocketState.Closed;
242+
_binaryOutput?.Writer.TryComplete();
243+
_jsonOutput?.Writer.TryComplete();
242244
}
243245

244246
private void Dispose(bool disposing)

src/Cnblogs.DashScope.Core/DashScopeClientWebSocketWrapper.cs

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -52,14 +52,5 @@ public Task SendMessageAsync<TInput, TParameter>(
5252
public void Dispose()
5353
{
5454
Pool.ReturnSocket(Socket);
55-
GC.SuppressFinalize(this);
56-
}
57-
58-
/// <summary>
59-
/// Finalizer.
60-
/// </summary>
61-
~DashScopeClientWebSocketWrapper()
62-
{
63-
Dispose();
6455
}
6556
}

src/Cnblogs.DashScope.Core/SpeechSynthesizerOutput.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,5 @@
33
/// <summary>
44
/// Output for TTS task.
55
/// </summary>
6-
/// <param name="Sentences">The output sentences.</param>
6+
/// <param name="Sentence">The output sentences.</param>
77
public record SpeechSynthesizerOutput(SpeechSynthesizerOutputSentences? Sentence);

src/Cnblogs.DashScope.Core/SpeechSynthesizerSocketSession.cs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,9 @@ public async Task ContinueTaskAsync(string taskId, string input, CancellationTok
8484
{
8585
Header = new DashScopeWebSocketRequestHeader
8686
{
87-
Action = "continue-task", TaskId = taskId,
87+
Action = "continue-task",
88+
TaskId = taskId,
89+
Streaming = null
8890
},
8991
Payload = new DashScopeWebSocketRequestPayload<SpeechSynthesizerInput, SpeechSynthesizerParameters>
9092
{
@@ -103,7 +105,12 @@ public async Task FinishTaskAsync(string taskId, CancellationToken cancellationT
103105
{
104106
var command = new DashScopeWebSocketRequest<SpeechSynthesizerInput, SpeechSynthesizerParameters>
105107
{
106-
Header = new DashScopeWebSocketRequestHeader { TaskId = taskId, Action = "finish-task" },
108+
Header = new DashScopeWebSocketRequestHeader
109+
{
110+
TaskId = taskId,
111+
Action = "finish-task",
112+
Streaming = null
113+
},
107114
Payload = new DashScopeWebSocketRequestPayload<SpeechSynthesizerInput, SpeechSynthesizerParameters>
108115
{
109116
Input = new SpeechSynthesizerInput()
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
using Cnblogs.DashScope.Core;
2+
using Cnblogs.DashScope.Tests.Shared.Utils;
3+
using NSubstitute;
4+
using NSubstitute.Extensions;
5+
using Xunit.Abstractions;
6+
7+
namespace Cnblogs.DashScope.Sdk.UnitTests;
8+
9+
public class DashScopeClientWebSocketWrapperTests
10+
{
11+
[Fact]
12+
public async Task Dispose_CallDispose_ReturnSocketToPoolAsync()
13+
{
14+
// Arrange
15+
var option = new DashScopeOptions();
16+
var fakeSocket = new FakeClientWebSocket();
17+
var factory = Substitute.For<IDashScopeClientWebSocketFactory>();
18+
factory.Configure().GetClientWebSocket(Arg.Any<string>(), Arg.Any<string>())
19+
.Returns(new DashScopeClientWebSocket(fakeSocket));
20+
var pool = new DashScopeClientWebSocketPool(factory, option);
21+
22+
// Act
23+
var socket = await pool.RentSocketAsync();
24+
socket.Dispose();
25+
26+
// Assert
27+
Assert.Equal(1, pool.AvailableSocketCount);
28+
Assert.Equal(0, pool.ActiveSocketCount);
29+
Assert.False(fakeSocket.DisposeCalled);
30+
}
31+
}
Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
using Cnblogs.DashScope.Tests.Shared.Utils;
2+
3+
namespace Cnblogs.DashScope.Sdk.UnitTests;
4+
5+
public class SpeechSynthesizerSerializationTests
6+
{
7+
[Fact]
8+
public async Task RunTask_SpecifyTaskId_SuccessAsync()
9+
{
10+
// Arrange
11+
var (client, _, server) = await Sut.GetSocketTestClientAsync();
12+
var snapshot = Snapshots.SpeechSynthesizer.RunTask;
13+
var taskStartedEvent = Snapshots.SpeechSynthesizer.TaskStarted;
14+
server.Playlist.Enqueue(async s => await s.WriteServerMessageAsync(taskStartedEvent.GetMessageJson()));
15+
16+
// Act
17+
using var session = await client.CreateSpeechSynthesizerSocketSessionAsync(snapshot.Message.Payload.Model!);
18+
var taskId = await session.RunTaskAsync(snapshot.Message.Header.TaskId, snapshot.Message.Payload.Parameters!);
19+
20+
// Assert
21+
Assert.Equal(snapshot.Message.Header.TaskId, taskId);
22+
Assert.True(Checkers.IsJsonEquivalent(server.ServerReceivedMessages.First(), snapshot.GetMessageJson()));
23+
}
24+
25+
[Fact]
26+
public async Task RunTask_GenerateTaskId_SuccessAsync()
27+
{
28+
// Arrange
29+
var (client, _, server) = await Sut.GetSocketTestClientAsync();
30+
var snapshot = Snapshots.SpeechSynthesizer.RunTask;
31+
var taskStartedEvent = Snapshots.SpeechSynthesizer.TaskStarted;
32+
server.Playlist.Enqueue(async s => await s.WriteServerMessageAsync(taskStartedEvent.GetMessageJson()));
33+
34+
// Act
35+
using var session = await client.CreateSpeechSynthesizerSocketSessionAsync(snapshot.Message.Payload.Model!);
36+
var taskId = await session.RunTaskAsync(snapshot.Message.Payload.Parameters!);
37+
38+
// Assert
39+
var json = snapshot.GetMessageJson().Replace(snapshot.Message.Header.TaskId, taskId);
40+
Assert.True(Checkers.IsJsonEquivalent(server.ServerReceivedMessages.First(), json));
41+
}
42+
43+
[Fact]
44+
public async Task ContinueTask_WithInput_SuccessAsync()
45+
{
46+
// Arrange
47+
var (client, _, server) = await Sut.GetSocketTestClientAsync();
48+
var runTask = Snapshots.SpeechSynthesizer.RunTask;
49+
var continueTask = Snapshots.SpeechSynthesizer.ContinueTask;
50+
var taskStartedEvent = Snapshots.SpeechSynthesizer.TaskStarted;
51+
server.Playlist.Enqueue(async s => await s.WriteServerMessageAsync(taskStartedEvent.GetMessageJson()));
52+
53+
// Act
54+
using var session = await client.CreateSpeechSynthesizerSocketSessionAsync(runTask.Message.Payload.Model!);
55+
await session.RunTaskAsync(runTask.Message.Header.TaskId, runTask.Message.Payload.Parameters!);
56+
await session.ContinueTaskAsync(continueTask.Message.Header.TaskId, continueTask.Message.Payload.Input.Text!);
57+
58+
// Assert
59+
Assert.True(Checkers.IsJsonEquivalent(server.ServerReceivedMessages.Last(), continueTask.GetMessageJson()));
60+
}
61+
62+
[Fact]
63+
public async Task FinishTask_NoPayload_SuccessAsync()
64+
{
65+
// Arrange
66+
var (client, _, server) = await Sut.GetSocketTestClientAsync();
67+
var runTask = Snapshots.SpeechSynthesizer.RunTask;
68+
var continueTask = Snapshots.SpeechSynthesizer.ContinueTask;
69+
var finishTask = Snapshots.SpeechSynthesizer.FinishTask;
70+
var taskStartedEvent = Snapshots.SpeechSynthesizer.TaskStarted;
71+
server.Playlist.Enqueue(async s => await s.WriteServerMessageAsync(taskStartedEvent.GetMessageJson()));
72+
73+
// Act
74+
using var session = await client.CreateSpeechSynthesizerSocketSessionAsync(runTask.Message.Payload.Model!);
75+
await session.RunTaskAsync(runTask.Message.Header.TaskId, runTask.Message.Payload.Parameters!);
76+
await session.ContinueTaskAsync(continueTask.Message.Header.TaskId, continueTask.Message.Payload.Input.Text!);
77+
await session.FinishTaskAsync(finishTask.Message.Header.TaskId);
78+
79+
// Assert
80+
Assert.True(Checkers.IsJsonEquivalent(server.ServerReceivedMessages.Last(), finishTask.GetMessageJson()));
81+
}
82+
83+
[Fact]
84+
public async Task ResultGenerated_WithBinary_SuccessAsync()
85+
{
86+
// Arrange
87+
var (client, _, server) = await Sut.GetSocketTestClientAsync();
88+
var runTask = Snapshots.SpeechSynthesizer.RunTask;
89+
var finishTask = Snapshots.SpeechSynthesizer.FinishTask;
90+
var resultGenerated = Snapshots.SpeechSynthesizer.ResultGenerated;
91+
var ttsBinary = Snapshots.SpeechSynthesizer.AudioTts;
92+
var taskStartedEvent = Snapshots.SpeechSynthesizer.TaskStarted;
93+
server.Playlist.Enqueue(async s => await s.WriteServerMessageAsync(taskStartedEvent.GetMessageJson()));
94+
server.Playlist.Enqueue(async s =>
95+
{
96+
await s.WriteServerMessageAsync(resultGenerated.GetMessageJson());
97+
await s.WriteServerMessageAsync(ttsBinary);
98+
await s.WriteServerCloseAsync();
99+
});
100+
101+
// Act
102+
using var session = await client.CreateSpeechSynthesizerSocketSessionAsync(runTask.Message.Payload.Model!);
103+
await session.RunTaskAsync(runTask.Message.Header.TaskId, runTask.Message.Payload.Parameters!);
104+
await session.FinishTaskAsync(finishTask.Message.Header.TaskId);
105+
var jsonEvents = await session.GetMessagesAsync().ToListAsync();
106+
var binaryContent = await session.GetAudioAsync().ToArrayAsync();
107+
108+
// Assert
109+
Assert.Equivalent(ttsBinary, binaryContent);
110+
Assert.Equal(2, jsonEvents.Count); // task-started, result-generated
111+
Assert.Equivalent(resultGenerated.Message, jsonEvents.Last());
112+
}
113+
114+
[Fact]
115+
public async Task TaskFinished_ServerClose_SuccessAsync()
116+
{
117+
// Arrange
118+
var (client, _, server) = await Sut.GetSocketTestClientAsync();
119+
var runTask = Snapshots.SpeechSynthesizer.RunTask;
120+
var finishTask = Snapshots.SpeechSynthesizer.FinishTask;
121+
var resultGenerated = Snapshots.SpeechSynthesizer.ResultGenerated;
122+
var taskFinished = Snapshots.SpeechSynthesizer.TaskFinished;
123+
var ttsBinary = Snapshots.SpeechSynthesizer.AudioTts;
124+
var taskStartedEvent = Snapshots.SpeechSynthesizer.TaskStarted;
125+
server.Playlist.Enqueue(async s => await s.WriteServerMessageAsync(taskStartedEvent.GetMessageJson()));
126+
server.Playlist.Enqueue(async s =>
127+
{
128+
await s.WriteServerMessageAsync(resultGenerated.GetMessageJson());
129+
await s.WriteServerMessageAsync(ttsBinary);
130+
await s.WriteServerMessageAsync(taskFinished.GetMessageJson());
131+
await s.WriteServerCloseAsync();
132+
});
133+
134+
// Act
135+
using var session = await client.CreateSpeechSynthesizerSocketSessionAsync(runTask.Message.Payload.Model!);
136+
await session.RunTaskAsync(runTask.Message.Header.TaskId, runTask.Message.Payload.Parameters!);
137+
await session.FinishTaskAsync(finishTask.Message.Header.TaskId);
138+
var jsonEvents = await session.GetMessagesAsync().ToListAsync();
139+
var binaryContent = await session.GetAudioAsync().ToArrayAsync();
140+
141+
// Assert
142+
Assert.Equivalent(ttsBinary, binaryContent);
143+
Assert.Equal(3, jsonEvents.Count); // task-started, result-generated, task-finished
144+
Assert.Equivalent(taskFinished.Message, jsonEvents.Last());
145+
}
146+
147+
[Fact]
148+
public async Task TaskFailed_ServerClose_SuccessAsync()
149+
{
150+
// Arrange
151+
var (client, _, server) = await Sut.GetSocketTestClientAsync();
152+
var runTask = Snapshots.SpeechSynthesizer.RunTask;
153+
var finishTask = Snapshots.SpeechSynthesizer.FinishTask;
154+
var taskFailed = Snapshots.SpeechSynthesizer.TaskFailed;
155+
var taskStarted = Snapshots.SpeechSynthesizer.TaskStarted;
156+
server.Playlist.Enqueue(async s => await s.WriteServerMessageAsync(taskStarted.GetMessageJson()));
157+
server.Playlist.Enqueue(async s =>
158+
{
159+
await s.WriteServerMessageAsync(taskFailed.GetMessageJson());
160+
await s.WriteServerCloseAsync();
161+
});
162+
163+
// Act
164+
using var session = await client.CreateSpeechSynthesizerSocketSessionAsync(runTask.Message.Payload.Model!);
165+
await session.RunTaskAsync(runTask.Message.Header.TaskId, runTask.Message.Payload.Parameters!);
166+
await session.FinishTaskAsync(finishTask.Message.Header.TaskId);
167+
var jsonEvents = await session.GetMessagesAsync().ToListAsync();
168+
var binaryContent = await session.GetAudioAsync().ToArrayAsync();
169+
170+
// Assert
171+
Assert.Empty(binaryContent);
172+
Assert.Equal(2, jsonEvents.Count); // task-started, task-failed
173+
Assert.Equivalent(taskFailed.Message, jsonEvents.Last());
174+
}
175+
176+
[Fact]
177+
public async Task Dispose_DisposedByUsings_ReturnSocketAsync()
178+
{
179+
// Arrange
180+
var (client, _, server) = await Sut.GetSocketTestClientAsync();
181+
var runTask = Snapshots.SpeechSynthesizer.RunTask;
182+
var finishTask = Snapshots.SpeechSynthesizer.FinishTask;
183+
var resultGenerated = Snapshots.SpeechSynthesizer.ResultGenerated;
184+
var taskFinished = Snapshots.SpeechSynthesizer.TaskFinished;
185+
var ttsBinary = Snapshots.SpeechSynthesizer.AudioTts;
186+
var taskStartedEvent = Snapshots.SpeechSynthesizer.TaskStarted;
187+
server.Playlist.Enqueue(async s => await s.WriteServerMessageAsync(taskStartedEvent.GetMessageJson()));
188+
server.Playlist.Enqueue(async s =>
189+
{
190+
await s.WriteServerMessageAsync(resultGenerated.GetMessageJson());
191+
await s.WriteServerMessageAsync(ttsBinary);
192+
await s.WriteServerMessageAsync(taskFinished.GetMessageJson());
193+
});
194+
195+
// Act
196+
using (var session = await client.CreateSpeechSynthesizerSocketSessionAsync(runTask.Message.Payload.Model!))
197+
{
198+
await session.RunTaskAsync(runTask.Message.Header.TaskId, runTask.Message.Payload.Parameters!);
199+
await session.FinishTaskAsync(finishTask.Message.Header.TaskId);
200+
}
201+
202+
// Assert
203+
Assert.False(server.DisposeCalled);
204+
}
205+
}

test/Cnblogs.DashScope.Tests.Shared/Cnblogs.DashScope.Tests.Shared.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
<ItemGroup>
1111
<PackageReference Include="FluentAssertions" Version="8.3.0" />
1212
<PackageReference Include="NSubstitute" Version="5.3.0"/>
13-
<PackageReference Include="System.Linq.Async" Version="6.0.1" />
13+
<PackageReference Include="System.Linq.Async" Version="6.0.3" />
1414
<PackageReference Include="JsonSchema.Net.Generation" Version="4.6.0" />
1515
</ItemGroup>
1616

test/Cnblogs.DashScope.Tests.Shared/Utils/FakeClientWebSocket.cs

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,22 +7,22 @@ namespace Cnblogs.DashScope.Tests.Shared.Utils;
77

88
public sealed class FakeClientWebSocket : IClientWebSocket
99
{
10-
public List<ArraySegment<byte>> ReceivedMessages { get; } = new();
10+
public List<ArraySegment<byte>> ServerReceivedMessages { get; } = new();
1111

1212
public Channel<WebSocketReceiveResult> Server { get; } =
1313
Channel.CreateUnbounded<WebSocketReceiveResult>();
1414

1515
public Channel<byte[]> ServerBuffer { get; } = Channel.CreateUnbounded<byte[]>();
1616

17+
public Queue<Func<FakeClientWebSocket, Task>> Playlist { get; } = new();
18+
1719
public bool DisposeCalled { get; private set; }
1820

1921
public async Task WriteServerCloseAsync()
2022
{
2123
var close = new WebSocketReceiveResult(1, WebSocketMessageType.Close, true);
2224
await Server.Writer.WriteAsync(close);
23-
await ServerBuffer.Writer.WriteAsync(new byte[] { 1 });
2425
await Server.Reader.WaitToReadAsync();
25-
await ServerBuffer.Reader.WaitToReadAsync();
2626
await Task.Delay(50);
2727
}
2828

@@ -78,27 +78,34 @@ public Task ConnectAsync(Uri uri, CancellationToken cancellation)
7878
}
7979

8080
/// <inheritdoc />
81-
public Task SendAsync(
81+
public async Task SendAsync(
8282
ArraySegment<byte> buffer,
8383
WebSocketMessageType messageType,
8484
bool endOfMessage,
8585
CancellationToken cancellationToken)
8686
{
87-
ReceivedMessages.Add(buffer);
88-
return Task.CompletedTask;
87+
ServerReceivedMessages.Add(buffer);
88+
if (Playlist.Count > 0)
89+
{
90+
await Playlist.Dequeue().Invoke(this);
91+
}
8992
}
9093

9194
/// <inheritdoc />
9295
public async Task<WebSocketReceiveResult> ReceiveAsync(
9396
ArraySegment<byte> buffer,
9497
CancellationToken cancellationToken)
9598
{
96-
await Server.Reader.WaitToReadAsync(cancellationToken);
97-
await ServerBuffer.Reader.WaitToReadAsync(cancellationToken);
98-
var binary = await ServerBuffer.Reader.ReadAsync(cancellationToken);
99-
for (var i = 0; i < binary.Length; i++)
99+
var jsonTask = Server.Reader.WaitToReadAsync(cancellationToken);
100+
var binaryTask = ServerBuffer.Reader.WaitToReadAsync(cancellationToken);
101+
await jsonTask;
102+
if (binaryTask.IsCompleted)
100103
{
101-
buffer[i] = binary[i];
104+
var binary = await ServerBuffer.Reader.ReadAsync(cancellationToken);
105+
for (var i = 0; i < binary.Length; i++)
106+
{
107+
buffer[i] = binary[i];
108+
}
102109
}
103110

104111
return await Server.Reader.ReadAsync(cancellationToken);

0 commit comments

Comments
 (0)