public async Task CopyToAppliesStreamScheduling(int blocks, int blockSize) { var clock = new ManualClock(); var src = new MemoryStream(Enumerable.Range(0, blockSize * blocks).Select(x => (byte)x).ToArray()); var scheduler = new ShapeStreamAverage(BandwidthStreamCost.Default, blockSize, averageSpan: TimeSpan.FromSeconds(1), clock: clock); var dst = new MemoryStream(); var bw = new ReadMaxStream <Stream>(src.Shape(scheduler), blockSize); var cpTask = bw.CopyToAsync(dst, blockSize, CancellationToken); for (var i = 0; i < blocks; ++i) { await Waiter.TimesOut(cpTask.WaitAsync, cancellationToken : CancellationToken) .ConfigureAwait(false); dst.Length.Should().BeLessOrEqualTo((i + 1) * blockSize); await Task.WhenAll(clock.Advance(TimeSpan.FromSeconds(1))).ConfigureAwait(false); } await Waiter.WaitAsync(cpTask.WaitAsync, cancellationToken : CancellationToken) .ConfigureAwait(false); dst.Length.Should().Be(blocks * blockSize); dst.ToArray() .Should() .BeEquivalentTo(src.ToArray(), cfg => cfg.WithStrictOrdering()); }
public async Task ManualClockShouldSupportMultiplePendingOperationsAndSynchroneousOperations(int concurCount) { var clock = new ManualClock(); var step = TimeSpan.FromSeconds(1); Skip.If(concurCount < 4); var concurrent = Enumerable.Range(0, concurCount) .Select(i => { if (i % 2 == 0) // Do async { return new { i, Task = clock.Delay(i <= 1 ? TimeSpan.Zero : step, CancellationToken).OnCompletion(i) } } ; // Do blocking in separate thread var tcs = new TaskCompletionSource <int>(); var thread = new Thread(() => { clock.Sleep(i <= 1 ? TimeSpan.Zero : step); tcs.SetResult(i); }); thread.Start(); return (new { i, tcs.Task }); }) .ToDictionary(x => x.i, x => x.Task); // Only 0 should be able to run await Task.WhenAll(concurrent.Where(x => x.Key <= 1).Select(x => x.Value)).ConfigureAwait(false); concurrent.Where(x => x.Key > 1).Should().OnlyContain(x => !x.Value.IsCompleted); clock.PendingCount().Should().BeGreaterOrEqualTo(2).And.BeLessOrEqualTo(concurCount - 2); await Task.WhenAll(clock.Advance(step)).ConfigureAwait(false); clock.PendingCount().Should().Be(0); var done = await Task.WhenAll(concurrent.Values).ConfigureAwait(false); done.Should().BeEquivalentTo(concurrent.Keys); }
public void sub_reporter_creates_sub_report_and_reports_to_it() { var subReporter = _statisticsReporter.CreateSubReporter("foo"); _clock.Advance(2.5); subReporter.ReportEvent("bar"); _statisticsReporter.DumpToReport(_report); var subReport = _report.GetSubReportAndRemove("foo"); _report.ValidateNoMoreReports(); subReport.ValidateReportedAndRemove("bar_COUNT", 1); }
public async Task StreamShedulingIsAppliedAccordingToClockAndSchedulers() { var clock = new ManualClock(new DateTime(2000, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc)); var stream = new MemoryStream(); var scheduler = new ShapeStreamAverage(BandwidthStreamCost.Default, 1000, 2000, 10, averageSpan: TimeSpan.FromSeconds(5), clock: clock); var bw = stream.Shape(scheduler); // Writing (2000b/s over 5s = 10kb/5s) await WriteAsync(bw, 1, 9000).ConfigureAwait(false); stream.Length.Should().Be(9000); await WriteAsync(bw, 2, 1000).ConfigureAwait(false); stream.Length.Should().Be(10000); await Task.WhenAll(clock.Advance(TimeSpan.FromSeconds(4), CancellationToken)).ConfigureAwait(false); // Exceed the shaping: will timeout and not get invoked await Waiter.TimesOut(ct => WriteAsync(bw, 3, 2000, ct), cancellationToken : CancellationToken).ConfigureAwait(false); stream.Length.Should().Be(10000); // Try writing again var wtask = WriteAsync(bw, 4, 2000); // That won't run immediately await Waiter.TimesOut(wtask.WaitAsync, cancellationToken : CancellationToken).ConfigureAwait(false); // And one second is not enough, as we are accounted the 2000 from time timed out write await Task.WhenAll(clock.Advance(TimeSpan.FromSeconds(1), CancellationToken)).ConfigureAwait(false); await Waiter.TimesOut(wtask.WaitAsync, cancellationToken : CancellationToken).ConfigureAwait(false); // So we need another second await Task.WhenAll(clock.Advance(TimeSpan.FromSeconds(1), CancellationToken)); // Now, It will be completed (pretty quickly) await Waiter.WaitAsync(wtask.WaitAsync, cancellationToken : CancellationToken); // And now have the required state stream.Length.Should().Be(12000); stream.Position = 0; var streamBytes = stream.ToArray(); IList <IList <byte> > readParts = new List <IList <byte> >(); // ReSharper disable once ImplicitlyCapturedClosure - bw, this captured void AssertReadCorrect() => readParts.Aggregate(0, (offset, part) => { part.Should().BeEquivalentTo(streamBytes.Skip(offset).Take(part.Count)); return(offset + part.Count); }); // Reading (1000b/s over 5s = 50kb/5s) readParts.Add(await ReadAsync(bw, 4999, CancellationToken).ConfigureAwait(false)); AssertReadCorrect(); readParts.Add(await ReadAsync(bw, 1).ConfigureAwait(false)); AssertReadCorrect(); // Exceed the shaping: will timeout and not get invoked // ReSharper disable once ImplicitlyCapturedClosure -- streamBytes captured await Waiter.TimesOut(async ct => readParts.Add(await ReadAsync(bw, 1, ct).ConfigureAwait(false)), cancellationToken : CancellationToken).ConfigureAwait(false); AssertReadCorrect(); // Try reading again var rtask = ReadAsync(bw, 6000); // That won't run immediately await Waiter.TimesOut(rtask.WaitAsync, cancellationToken : CancellationToken).ConfigureAwait(false); // And 6 seconds is not enough, as we are exceeding average in the read itself and pay for the extra 1 failing byte await Task.WhenAll(clock.Advance(TimeSpan.FromSeconds(6), CancellationToken)).ConfigureAwait(false); await Waiter.TimesOut(rtask.WaitAsync, cancellationToken : CancellationToken).ConfigureAwait(false); // But a little more is await Task.WhenAll(clock.Advance(TimeSpan.FromSeconds(.1), CancellationToken)).ConfigureAwait(false); // Now, It will be completed (pretty quickly) readParts.Add(await Waiter.WaitAsync(rtask.WaitAsync, cancellationToken: CancellationToken)); AssertReadCorrect(); readParts.Sum(x => x.Count).Should().Be(4999 + 1 + 6000); }