using System;
using System.Buffers;
using System.ComponentModel;
using System.Threading.Tasks;
using NewLife;
using NewLife.Data;
using Xunit;
namespace XUnitTest.Data;
/// <summary>é™é•¿è¯»å–å™¨æµ‹è¯•ã€‚ç®¡é“æµå¼æ¨¡å¼ç”± PipeTests çš„ Limit 用例覆盖,æ¤å¤„è¡¥å†…å˜æ¨¡å¼ï¼ˆæ•´å¸§è§£æžï¼‰çš„窗å£ã€é¢„ç®—ã€å¤ä½ä¸Žå–值è¯ä¹‰</summary>
public class LimitedReaderTests
{
private static Byte[] B(params Int32[] values)
{
var buf = new Byte[values.Length];
for (var i = 0; i < values.Length; i++) buf[i] = (Byte)values[i];
return buf;
}
[Fact]
[DisplayName("é™é•¿è¯»å–_å†…å˜æ¨¡å¼_窗å£è£å‰ªåˆ°é¢„算内")]
public async Task MemoryMode_BufferWithinBudget()
{
var packet = new ArrayPacket(B(1, 2, 3, 4, 5));
var body = new LimitedReader(packet, 1, 3);
Assert.False(body.IsStreaming);
Assert.Equal(3, body.Remaining);
Assert.Equal(B(2, 3, 4), body.Buffer.ToArray());
// æ•°æ®å·²åœ¨å†…å˜ï¼šè¯»å–ç«‹å³å®Œæˆï¼Œä¸”带“结æŸâ€æ ‡è®°
var rr = await body.ReadAsync();
Assert.True(rr.IsCompleted);
Assert.False(rr.IsCanceled);
Assert.Equal(B(2, 3, 4), rr.Buffer.ToArray());
// 读å–䏿¶ˆè´¹ï¼šé¢„ç®—ä¸å˜
Assert.Equal(3, body.Remaining);
}
[Fact]
[DisplayName("é™é•¿è¯»å–_å†…å˜æ¨¡å¼_推进扣å‡é¢„ç®—")]
public async Task MemoryMode_AdvanceDecrementsBudget()
{
var packet = new ArrayPacket(B(1, 2, 3, 4, 5));
var body = new LimitedReader(packet, 1, 3);
body.AdvanceTo(1);
Assert.Equal(2, body.Remaining);
Assert.Equal(B(3, 4), body.Buffer.ToArray());
var rr = await body.ReadAsync();
Assert.Equal(B(3, 4), rr.Buffer.ToArray());
}
[Fact]
[DisplayName("é™é•¿è¯»å–_推进超出预算_æŠ›å‚æ•°å¼‚常")]
public void Advance_BeyondBudget_Throws()
{
var packet = new ArrayPacket(B(1, 2, 3, 4, 5));
var body = new LimitedReader(packet, 1, 2);
Assert.Throws<ArgumentOutOfRangeException>(() => body.AdvanceTo(3));
Assert.Throws<ArgumentOutOfRangeException>(() => body.AdvanceTo(-1));
}
[Fact]
[DisplayName("é™é•¿è¯»å–_预算耗尽_读å–返回已结æŸç©ºç»“æžœ")]
public async Task BudgetExhausted_ReturnsCompletedEmpty()
{
var packet = new ArrayPacket(B(1, 2, 3));
var body = new LimitedReader(packet, 0, 2);
body.AdvanceTo(2);
Assert.Equal(0, body.Remaining);
Assert.Null(body.AsPacket());
var rr = await body.ReadAsync();
Assert.True(rr.IsCompleted);
Assert.True(rr.Buffer.IsEmpty);
// åŒæ¥å½¢æ€ä¸Žå¼‚æ¥ä¸€è‡´
Assert.True(body.TryRead(out var sync));
Assert.True(sync.IsCompleted);
Assert.True(sync.Buffer.IsEmpty);
}
[Fact]
[DisplayName("é™é•¿è¯»å–_å†…å˜æ¨¡å¼_AsPacketä¸ºå‰©ä½™ä½“è§†å›¾ä¸”ä¸æ¶ˆè´¹")]
public void MemoryMode_AsPacketView()
{
var packet = new ArrayPacket(B(1, 2, 3, 4, 5));
var body = new LimitedReader(packet, 1, 4);
var view = body.AsPacket();
Assert.NotNull(view);
Assert.Equal(B(2, 3, 4, 5), view!.ToArray());
// å–è§†å›¾ä¸æ¶ˆè´¹
body.AdvanceTo(2);
Assert.Equal(2, body.Remaining);
Assert.Equal(B(4, 5), body.AsPacket()!.ToArray());
}
[Fact]
[DisplayName("é™é•¿è¯»å–_å†…å˜æ¨¡å¼_ReadAllAsync读满并推进到帧尾")]
public async Task MemoryMode_ReadAll()
{
var packet = new ArrayPacket(B(1, 2, 3, 4, 5));
var body = new LimitedReader(packet, 1, 3);
var all = await body.ReadAllAsync();
Assert.Equal(B(2, 3, 4), all.ToArray());
Assert.Equal(0, body.Remaining);
}
[Fact]
[DisplayName("é™é•¿è¯»å–_å†…å˜æ¨¡å¼_ReadAllAsyncåŽä¸å¯é‡å¤è¯»")]
public async Task MemoryMode_ReadAllConsumes()
{
var packet = new ArrayPacket(B(1, 2, 3));
var body = new LimitedReader(packet, 0, 3);
var first = await body.ReadAllAsync();
Assert.Equal(B(1, 2, 3), first.ToArray());
// 已推进到帧尾:å†è¯»åªå¾—空包
var second = await body.ReadAllAsync();
Assert.Equal(0, second.Length);
}
[Fact]
[DisplayName("é™é•¿è¯»å–_å†…å˜æ¨¡å¼_å¤ä½åŽå¯é‡å¤è¯»")]
public async Task MemoryMode_Reset()
{
var packet = new ArrayPacket(B(1, 2, 3, 4, 5));
var body = new LimitedReader(packet, 1, 3);
var all = await body.ReadAllAsync();
Assert.Equal(B(2, 3, 4), all.ToArray());
// 事件链消费完消æ¯ä½“åŽã€äº¤ä»˜ç‰å¾…æ–¹å‰å¤ä½ï¼šå†…容å¯è¢«é‡å¤è¯»å–
body.Reset();
Assert.Equal(3, body.Remaining);
var again = await body.ReadAllAsync();
Assert.Equal(B(2, 3, 4), again.ToArray());
}
[Fact]
[DisplayName("é™é•¿è¯»å–_å†…å˜æ¨¡å¼_Drain丢弃余é‡")]
public async Task MemoryMode_Drain()
{
var packet = new ArrayPacket(B(1, 2, 3, 4, 5));
var body = new LimitedReader(packet, 1, 4);
await body.DrainAsync();
Assert.Equal(0, body.Remaining);
Assert.Null(body.AsPacket());
}
[Fact]
[DisplayName("é™é•¿è¯»å–_æµå¼æ¨¡å¼_å–包与å¤ä½è¢«æ‹’ç»")]
public void StreamingMode_AsPacketAndResetRejected()
{
using var pipe = new Pipe();
var body = pipe.Reader.Limit(4);
Assert.True(body.IsStreaming);
Assert.Equal(4, body.Remaining);
// æµå¼ä½“的数æ®ç”±ç®¡é“承载ã€ä¸å¯é‡æ”¾ï¼Œä¹Ÿä¸å…许直接å–包
Assert.Throws<InvalidOperationException>(() => body.AsPacket());
Assert.Throws<InvalidOperationException>(() => body.Reset());
}
#region æµå¼çŸè¯»
[Fact]
[DisplayName("é™é•¿è¯»å–_æµå¼æ¨¡å¼_读满返回完整数æ®")]
public async Task StreamingMode_ReadAllFull()
{
using var pipe = new Pipe();
pipe.Writer.Append(new ArrayPacket(B(1, 2, 3, 4)));
var body = pipe.Reader.Limit(4);
var all = await body.ReadAllAsync();
Assert.Equal(B(1, 2, 3, 4), all.ToArray());
Assert.Equal(0, body.Remaining);
}
[Fact]
[DisplayName("é™é•¿è¯»å–_æµå¼æ¨¡å¼_跨轮补é½åŽè¯»æ»¡")]
public async Task StreamingMode_ReadAllAcrossRounds()
{
using var pipe = new Pipe();
pipe.Writer.Append(new ArrayPacket(B(1, 2)));
var body = pipe.Reader.Limit(4);
// é¦–è½®åªæœ‰ 2 å—èŠ‚ï¼šè¯»å–æŒ‚èµ·ç‰å¾…åŽç»æ•°æ®
var task = body.ReadAllAsync().AsTask();
Assert.False(task.IsCompleted);
pipe.Writer.Append(new ArrayPacket(B(3, 4)));
var all = await task.WaitAsync(TimeSpan.FromSeconds(5));
Assert.Equal(B(1, 2, 3, 4), all.ToArray());
Assert.Equal(0, body.Remaining);
}
[Fact]
[DisplayName("é™é•¿è¯»å–_æµå¼æ¨¡å¼_å¯¹ç«¯å…³é—æœªè¯»æ»¡_抛æµç»“æŸå¼‚常")]
public async Task StreamingMode_Truncated_ThrowsEndOfStream()
{
using var pipe = new Pipe();
pipe.Writer.Append(new ArrayPacket(B(1, 2, 3)));
pipe.Writer.Complete();
var body = pipe.Reader.Limit(5);
var ex = await Assert.ThrowsAsync<EndOfStreamException>(() => body.ReadAllAsync().AsTask());
Assert.Contains("未读满", ex.Message);
Assert.Equal(2, body.Remaining);
}
[Fact]
[DisplayName("é™é•¿è¯»å–_æµå¼æ¨¡å¼_管é“带错误结æŸ_é€ä¼ 原异常")]
public async Task StreamingMode_PipeError_Propagates()
{
using var pipe = new Pipe();
pipe.Writer.Append(new ArrayPacket(B(1, 2)));
pipe.Writer.Complete(new InvalidOperationException("ç®¡é“æ•…éšœ"));
var body = pipe.Reader.Limit(4);
var ex = await Assert.ThrowsAsync<InvalidOperationException>(() => body.ReadAllAsync().AsTask());
Assert.Equal("ç®¡é“æ•…éšœ", ex.Message);
}
[Fact]
[DisplayName("é™é•¿è¯»å–_æµå¼æ¨¡å¼_读å–è¢«å–æ¶ˆ_æŠ›å–æ¶ˆå¼‚常")]
public async Task StreamingMode_CancelPendingRead_Throws()
{
using var pipe = new Pipe();
var body = pipe.Reader.Limit(4);
var task = body.ReadAllAsync().AsTask();
Assert.False(task.IsCompleted);
// å–æ¶ˆè¢«é™é»˜åžæŽ‰ä¼šè®©â€œå–消å³å¤±è´¥â€çš„è°ƒç”¨æ–¹æ‹¿åˆ°åŠæˆªä½“çš„å‡æˆåŠŸ
pipe.Reader.CancelPendingRead();
await Assert.ThrowsAnyAsync<OperationCanceledException>(() => task.WaitAsync(TimeSpan.FromSeconds(5)));
Assert.Equal(4, body.Remaining);
}
[Fact]
[DisplayName("é™é•¿è¯»å–_æµå¼æ¨¡å¼_å–æ¶ˆä»¤ç‰Œå–消挂起读_æŠ›å–æ¶ˆå¼‚常")]
public async Task StreamingMode_TokenCanceled_Throws()
{
using var pipe = new Pipe();
using var cts = new CancellationTokenSource();
var body = pipe.Reader.Limit(4);
var task = body.ReadAllAsync(cts.Token).AsTask();
Assert.False(task.IsCompleted);
cts.Cancel();
await Assert.ThrowsAnyAsync<OperationCanceledException>(() => task.WaitAsync(TimeSpan.FromSeconds(5)));
}
#endregion
[Fact]
[DisplayName("é™é•¿è¯»å–_æž„é€ å‡½æ•°_éžæ³•傿•°è¢«æ‹’ç»")]
public void Ctor_InvalidArguments_Throws()
{
var packet = new ArrayPacket(B(1, 2, 3));
Assert.Throws<ArgumentNullException>(() => new LimitedReader((IPacket)null!, 0, 1));
Assert.Throws<ArgumentOutOfRangeException>(() => new LimitedReader(packet, -1, 1));
Assert.Throws<ArgumentOutOfRangeException>(() => new LimitedReader(packet, 0, -1));
}
}
|