示例#1
0
        public async Task ComputedPerformanceTest()
        {
            if (TestRunnerInfo.IsBuildAgent())
            {
                return; // Shouldn't run this test on build agents
            }
            var users                    = Services.GetRequiredService <IUserService>();
            var plainUsers               = Services.GetRequiredService <UserService>();
            var useImdb                  = Options.UseInMemoryDatabase;
            var opCountPerCore           = 2_000_000;
            var readersPerCore           = 4;
            var readerCount              = HardwareInfo.GetProcessorCountFactor(readersPerCore);
            var cachingIterationCount    = opCountPerCore / readersPerCore;
            var nonCachingIterationCount = cachingIterationCount / (useImdb ? 1000 : 10_000);

            var withoutSerialization = (Action <User>)(u => { });
            var withSerialization    = (Action <User>)(u => JsonConvert.SerializeObject(u));

            Out.WriteLine($"Database: {(useImdb ? "In-memory" : "Sqlite")}");
            Out.WriteLine("With Stl.Fusion:");
            await Test("Standard test", users, withoutSerialization,
                       readerCount, cachingIterationCount);
            await Test("Standard test + serialization", users, withSerialization,
                       readerCount, cachingIterationCount / 3);

            Out.WriteLine("Without Stl.Fusion:");
            await Test("Standard test", plainUsers, withoutSerialization,
                       readerCount, nonCachingIterationCount);
            await Test("Standard test + serialization", plainUsers, withSerialization,
                       readerCount, nonCachingIterationCount);
        }
示例#2
0
    public async Task TestNoKeepAlive()
    {
        if (TestRunnerInfo.IsBuildAgent())
        {
            // TODO: Fix intermittent failures on GitHub
            return;
        }

        var services = CreateProviderFor <Service>();
        var service  = services.GetRequiredService <Service>();

        service.CallCount = 0;
        await service.Multiply(1, 1);

        service.CallCount.Should().Be(1);
        await service.Multiply(1, 1);

        service.CallCount.Should().Be(1);

        await GCCollect();

        await service.Multiply(1, 1);

        service.CallCount.Should().Be(2);
    }
示例#3
0
    public async Task BasicTest()
    {
        if (TestRunnerInfo.IsBuildAgent())
        {
            return; // No Redis on build agent for now
        }
        var sw = new Stopwatch();

        sw.Start();
        var pub1 = GetRedisDb().GetPub <string>("pub-1");
        var pub2 = GetRedisDb().GetPub <string>("pub-2");
        var sub  = GetRedisDb().GetTaskSub <string>("pub-*");

        await using var _ = sub.ConfigureAwait(false);

        await sub.WhenSubscribed.ConfigureAwait(false);

        Out.WriteLine($"{sw.ElapsedMilliseconds}: <- 1");
        var messageTask = sub.NextMessage();
        await pub1.Publish("1");

        Out.WriteLine($"{sw.ElapsedMilliseconds}: -> {await messageTask}");

        Out.WriteLine($"{sw.ElapsedMilliseconds}: <- 2");
        messageTask = sub.NextMessage();
        await pub2.Publish("2");

        Out.WriteLine($"{sw.ElapsedMilliseconds}: -> {await messageTask}");
    }
示例#4
0
        public async void TestKeepAlive()
        {
            if (TestRunnerInfo.IsBuildAgent())
            {
                // TODO: Fix intermittent failures on GitHub
                return;
            }

            var services = CreateProviderFor <Service>();
            var service  = services.GetRequiredService <Service>();

            service.CallCount = 0;
            await service.SumAsync(1, 1);

            service.CallCount.Should().Be(1);
            await service.SumAsync(1, 1);

            service.CallCount.Should().Be(1);

            await Task.Delay(250);

            await GCCollectAsync();

            await service.SumAsync(1, 1);

            service.CallCount.Should().Be(1);

            await Task.Delay(1000);

            await GCCollectAsync();

            await service.SumAsync(1, 1);

            service.CallCount.Should().Be(2);
        }
示例#5
0
    public async Task BasicTest()
    {
        if (TestRunnerInfo.IsBuildAgent())
        {
            return; // No Redis on build agent for now
        }
        var hash = GetRedisDb().GetHash("hash");
        await hash.Clear();

        (await hash.GetAll()).Length.Should().Be(0);

        (await hash.Increment("a")).Should().Be(1);
        (await hash.Increment("a")).Should().Be(2);
        (await hash.Set("a", "10")).Should().Be(false);
        (await hash.Increment("a")).Should().Be(11);
        (await hash.GetAll()).Length.Should().Be(1);

        (await hash.Increment("b")).Should().Be(1);
        (await hash.Increment("a")).Should().Be(12);
        (await hash.GetAll()).Length.Should().Be(2);

        (await hash.Remove("a")).Should().BeTrue();
        (await hash.GetAll()).Length.Should().Be(1);

        await hash.Clear();

        (await hash.GetAll()).Length.Should().Be(0);
    }
示例#6
0
    public async Task ComputedPerformanceTest()
    {
        if (TestRunnerInfo.IsBuildAgent())
        {
            return; // Shouldn't run this test on build agents
        }
        var users                    = Services.GetRequiredService <IUserService>();
        var plainUsers               = Services.GetRequiredService <UserService>();
        var opCountPerCore           = 4_000_000;
        var readersPerCore           = 5;
        var readerCount              = HardwareInfo.GetProcessorCountFactor(readersPerCore);
        var cachingIterationCount    = opCountPerCore / readersPerCore;
        var nonCachingIterationCount = cachingIterationCount / 2000;

        var withoutSerialization = (Action <User>)(u => { });
        var withSerialization    = (Action <User>)(u => JsonSerializer.Serialize(u)); // STJ serializer

        Out.WriteLine($".NET: {RuntimeInfo.DotNetCore.VersionString}");
        Out.WriteLine($"Database: {Options.DbType}");
        Out.WriteLine("With Stl.Fusion:");
        await Test("Standard test", users, withoutSerialization,
                   readerCount, cachingIterationCount);
        await Test("Standard test + serialization", users, withSerialization,
                   readerCount, cachingIterationCount / 3);

        Out.WriteLine("Without Stl.Fusion:");
        await Test("Standard test", plainUsers, withoutSerialization,
                   readerCount, nonCachingIterationCount);
        await Test("Standard test + serialization", plainUsers, withSerialization,
                   readerCount, nonCachingIterationCount);
    }
示例#7
0
    public async Task BasicTest()
    {
        var epsilon   = TimeSpan.FromSeconds(TestRunnerInfo.IsBuildAgent() ? 5 : 1);
        var epsilon10 = epsilon.Multiply(10);

        using var clock = new TestClock().SpeedupBy(10).OffsetBy(1000);
        var realStart  = SystemClock.Now;
        var clockStart = clock.Now;

        ShouldEqual(realStart, DateTime.Now.ToMoment(), epsilon);
        ShouldEqual(realStart, clock.ToRealTime(clockStart), epsilon);
        ShouldEqual(clockStart, clock.ToLocalTime(realStart), epsilon10);
        ShouldEqual(clockStart, realStart + TimeSpan.FromSeconds(1), epsilon);

        await clock.Delay(TimeSpan.FromSeconds(5));

        ShouldEqual(realStart + TimeSpan.FromSeconds(0.5), SystemClock.Now, epsilon);
        ShouldEqual(clockStart + TimeSpan.FromSeconds(5), clock.Now, epsilon10);
        Out.WriteLine(clock.Now.ToString());

        clock.OffsetBy(1000);
        ShouldEqual(clockStart + TimeSpan.FromSeconds(6), clock.Now, epsilon10);

        clock.SpeedupBy(0.1);
        await clock.Delay(TimeSpan.FromSeconds(0.5));

        ShouldEqual(realStart + TimeSpan.FromSeconds(1), SystemClock.Now, epsilon);
        ShouldEqual(clockStart + TimeSpan.FromSeconds(6.5), clock.Now, epsilon10);
    }
示例#8
0
        public async void BasicTest()
        {
            if (TestRunnerInfo.IsBuildAgent())
            {
                // TODO: Fix intermittent failures on GitHub
                return;
            }

            var services    = CreateServiceProviderFor <Service>();
            var swapService = services.GetRequiredService <SwapService>();
            var service     = services.GetRequiredService <Service>();
            var clock       = Timeouts.Clock;

            service.CallCount = 0;
            var a = "a";
            var v = await service.SameValueAsync(a);

            service.CallCount.Should().Be(1);
            swapService.LoadCallCount.Should().Be(0);
            swapService.StoreCallCount.Should().Be(0);
            swapService.RenewCallCount.Should().Be(0);
            v.Should().BeSameAs(a);

            await DelayAsync(1.4);

            swapService.LoadCallCount.Should().Be(0);
            swapService.RenewCallCount.Should().Be(1);
            swapService.StoreCallCount.Should().Be(1);
            v = await service.SameValueAsync(a);

            service.CallCount.Should().Be(1);
            swapService.LoadCallCount.Should().Be(1);
            swapService.RenewCallCount.Should().Be(1);
            swapService.StoreCallCount.Should().Be(1);
            v.Should().Be(a);
            v.Should().NotBeSameAs(a);

            // We accessed the value, so we need to wait for
            // SwapTime + KeepAliveTime to make sure it's
            // available for GC
            await DelayAsync(1.9);

            swapService.LoadCallCount.Should().Be(1);
            swapService.RenewCallCount.Should().Be(2);
            swapService.StoreCallCount.Should().Be(1);

            while (service.CallCount == 1)
            {
                GCCollect();
                v = await service.SameValueAsync(a);
            }
            service.CallCount.Should().Be(2);
            swapService.LoadCallCount.Should().Be(1);
            swapService.RenewCallCount.Should().Be(2);
            swapService.StoreCallCount.Should().Be(1);
            v.Should().Be(a);
            v.Should().BeSameAs(a);
        }
示例#9
0
    public async Task BasicTest()
    {
        if (TestRunnerInfo.IsBuildAgent())
        {
            return; // No Redis on build agent for now
        }
        var db       = GetRedisDb();
        var started  = TaskSource.New <Unit>(true);
        var streamer = db.GetStreamer <int>("s");
        await streamer.Remove();

        var streamerCopy = db.GetStreamer <int>("s");

        var writeTask = streamer.Write(
            Delays(new[] { 0.1, 0.2, 0.3, 0.1 }),
            _ => started.SetResult(default));
        public async Task Test2()
        {
            var epsilon = TimeSpan.FromSeconds(0.5);

            if (TestRunnerInfo.IsBuildAgent())
            {
                epsilon *= 2;
            }

            await using var serving = await WebSocketHost.ServeAsync();

            var service = Services.GetRequiredService <IClientTimeService>();

            for (int i = 0; i < 20; i++)
            {
                var time = await service.GetTimeAsync();

                (DateTime.Now - time).Should().BeLessThan(epsilon);
                await Task.Delay(TimeSpan.FromSeconds(0.1));
            }
        }
示例#11
0
    public async Task BasicTest()
    {
        if (TestRunnerInfo.IsBuildAgent())
        {
            return; // No Redis on build agent for now
        }
        await using var queue = await CreateQueue <string>("q", true).ConfigureAwait(false);

        var sw = new Stopwatch();

        sw.Start();

        Out.WriteLine($"{sw.ElapsedMilliseconds}: <- 1");
        await queue.Enqueue("1");

        Out.WriteLine($"{sw.ElapsedMilliseconds}: -> {await queue.Dequeue()}");

        Out.WriteLine($"{sw.ElapsedMilliseconds}: <- 2");
        await queue.Enqueue("2");

        Out.WriteLine($"{sw.ElapsedMilliseconds}: -> {await queue.Dequeue()}");
    }
示例#12
0
    public void BasicTest()
    {
        var holder  = new RefHolder();
        var count   = TestRunnerInfo.IsBuildAgent() ? 100 : 1000;
        var objects = Enumerable.Range(0, count).Select(i => i.ToString()).ToArray();

        var holds = new HashSet <IDisposable>();

        foreach (var o in objects)
        {
            holds.Add(holder.Hold(o));
        }

        holder.IsEmpty.Should().BeFalse();

        // HashSet randomizes the order of disposal
        foreach (var hold in holds)
        {
            hold.Dispose();
        }
        holder.IsEmpty.Should().BeTrue();
    }
示例#13
0
    public async Task BasicTest()
    {
        if (TestRunnerInfo.IsBuildAgent())
        {
            return; // No Redis on build agent for now
        }
        var set = GetRedisDb().GetSequenceSet("seq", 250);
        await set.Clear();

        (await set.Next("a")).Should().Be(1);
        (await set.Next("a")).Should().Be(2);
        (await set.Next("a", 500)).Should().Be(501);
        (await set.Next("a", 300)).Should().Be(502);
        (await set.Next("a", 200)).Should().Be(201);
        (await set.Next("a")).Should().Be(202);

        (await set.Next("a", 1000_000_000).WithTimeout(TimeSpan.FromMilliseconds(100)))
        .Should().Be(Option.Some(1000_000_001L));     // Auto-reset test

        await set.Reset("a", 10);

        (await set.Next("a", 5)).Should().Be(11);
    }
示例#14
0
        public async Task ComputedPerformanceTest()
        {
            if (TestRunnerInfo.IsBuildAgent())
            {
                return; // Shouldn't run this test on build agents
            }
            var users                    = Services.GetRequiredService <IUserService>();
            var useImdb                  = Options.UseInMemoryDatabase;
            var opCountPerCore           = 2_000_000;
            var readersPerCore           = 4;
            var readerCount              = HardwareInfo.ProcessorCount * readersPerCore;
            var cachingIterationCount    = opCountPerCore / readersPerCore;
            var nonCachingIterationCount = cachingIterationCount / (useImdb ? 1000 : 10_000);

            var cachingProviderPool    = new ConcurrentPool <IUserService>(() => users);
            var nonCachingProviderPool = new ConcurrentPool <IUserService>(() => {
                var scope = Services.CreateScope();
                return(scope.ServiceProvider.GetRequiredService <UserService>());
                // No scope disposal, but it's fine for the test, I guess
            });

            var withoutSerialization = (Action <User>)(u => { });
            var withSerialization    = (Action <User>)(u => JsonConvert.SerializeObject(u));

            Out.WriteLine($"Database: {(useImdb ? "In-memory" : "Sqlite")}");
            Out.WriteLine("With Stl.Fusion:");
            await Test("Standard test", cachingProviderPool, withoutSerialization,
                       readerCount, cachingIterationCount);
            await Test("Standard test + serialization", cachingProviderPool, withSerialization,
                       readerCount, cachingIterationCount / 3);

            Out.WriteLine("Without Stl.Fusion:");
            await Test("Standard test", nonCachingProviderPool, withoutSerialization,
                       readerCount, nonCachingIterationCount);
            await Test("Standard test + serialization", nonCachingProviderPool, withSerialization,
                       readerCount, nonCachingIterationCount);
        }
示例#15
0
        public async Task DropReconnectTest()
        {
            if (TestRunnerInfo.IsBuildAgent())
            {
                // TODO: Fix intermittent failures on GitHub
                return;
            }

            var serving = await WebSocketHost.ServeAsync();

            var tp = Services.GetRequiredService <ITimeService>();

            Debug.WriteLine("0");
            var pub = await Publisher.PublishAsync(_ => tp.GetTimeAsync());

            var rep = Replicator.GetOrAdd <DateTime>(pub.Ref);

            Debug.WriteLine("1");
            await rep.RequestUpdateAsync().AsAsyncFunc()
            .Should().CompleteWithinAsync(TimeSpan.FromMinutes(1));

            Debug.WriteLine("2");
            var state = Replicator.GetPublisherConnectionState(pub.Publisher.Id);

            state.Computed.IsConsistent().Should().BeTrue();
            Debug.WriteLine("3");
            await state.Computed.UpdateAsync(false);

            Debug.WriteLine("4");
            state.Should().Be(Replicator.GetPublisherConnectionState(pub.Publisher.Id));
            state.Value.Should().BeTrue();

            Debug.WriteLine("WebServer: stopping.");
            await serving.DisposeAsync();

            Debug.WriteLine("WebServer: stopped.");

            // First try -- should fail w/ WebSocketException or ChannelClosedException
            Debug.WriteLine("5");
            await rep.RequestUpdateAsync().AsAsyncFunc()
            .Should().ThrowAsync <Exception>();

            Debug.WriteLine("6");
            state.Should().Be(Replicator.GetPublisherConnectionState(pub.Publisher.Id));
            await state.Computed.UpdateAsync(false);

            Debug.WriteLine("7");
            state.Should().Be(Replicator.GetPublisherConnectionState(pub.Publisher.Id));
            state.Error.Should().BeAssignableTo <Exception>();

            // Second try -- should fail w/ WebSocketException
            Debug.WriteLine("8");
            await rep.Computed.UpdateAsync(false).AsAsyncFunc()
            .Should().ThrowAsync <WebSocketException>();

            Debug.WriteLine("9");
            rep.UpdateError.Should().BeOfType <WebSocketException>();
            await state.Computed.UpdateAsync(false);

            Debug.WriteLine("10");
            state.Error.Should().BeOfType <WebSocketException>();

            Debug.WriteLine("WebServer: starting.");
            serving = await WebSocketHost.ServeAsync();

            await Task.Delay(1000);

            Debug.WriteLine("WebServer: started.");

            Debug.WriteLine("11");
            await rep.RequestUpdateAsync().AsAsyncFunc()
            .Should().CompleteWithinAsync(TimeSpan.FromMinutes(1));

            Debug.WriteLine("12");
            await state.Computed.UpdateAsync(false);

            Debug.WriteLine("13");
            state.Value.Should().BeTrue();

            Debug.WriteLine("100");
            await serving.DisposeAsync();

            Debug.WriteLine("101");
        }
示例#16
0
    public async Task DropReconnectTest()
    {
        if (TestRunnerInfo.IsBuildAgent())
        {
            // TODO: Fix intermittent failures on GitHub
            return;
        }

        var serving = await WebHost.Serve(false);

        var replicator = ClientServices.GetRequiredService <IReplicator>();
        var kvsClient  = ClientServices.GetRequiredService <IKeyValueServiceClient <string> >();

        Debug.WriteLine("0");
        var kvs = WebServices.GetRequiredService <IKeyValueService <string> >();
        await kvs.Set("a", "b");

        var c = (ReplicaMethodComputed <string>)
                await Computed.Capture(_ => kvsClient.Get("a"));

        c.Value.Should().Be("b");
        c.IsConsistent().Should().BeTrue();

        Debug.WriteLine("1");
        await c.Replica !.RequestUpdate().AsAsyncFunc()
        .Should().CompleteWithinAsync(TimeSpan.FromSeconds(5));

        c.IsConsistent().Should().BeTrue();

        Debug.WriteLine("2");
        var cs = replicator.GetPublisherConnectionState(c.Replica.PublicationRef.PublisherId);

        cs.Value.Should().BeTrue();
        cs.Computed.IsConsistent().Should().BeTrue();
        await cs.Recompute();

        Debug.WriteLine("3");
        cs.Value.Should().BeTrue();
        cs.Computed.IsConsistent().Should().BeTrue();
        var cs1 = replicator.GetPublisherConnectionState(c.Replica.PublicationRef.PublisherId);

        cs1.Should().BeSameAs(cs);

        Debug.WriteLine("WebServer: stopping.");
        await serving.DisposeAsync();

        Debug.WriteLine("WebServer: stopped.");

        // First try -- should fail w/ WebSocketException or ChannelClosedException
        c.IsConsistent().Should().BeTrue();
        c.Value.Should().Be("b");
        Debug.WriteLine("4");

        await cs.Update();

        cs.Error.Should().BeAssignableTo <Exception>();
        cs.Computed.IsConsistent().Should().BeTrue();
        var updateTask = c.Replica.RequestUpdate();

        updateTask.IsCompleted.Should().BeFalse();
        Debug.WriteLine("5");

        await kvs.Set("a", "c");

        await Delay(0.1);

        c.IsConsistent().Should().BeTrue();
        c.Value.Should().Be("b");
        Debug.WriteLine("6");

        Debug.WriteLine("WebServer: starting.");
        serving = await WebHost.Serve();
        await Delay(1);

        Debug.WriteLine("WebServer: started.");

        await TestExt.WhenMet(
            () => cs.Error.Should().BeNull(),
            TimeSpan.FromSeconds(30));

        Debug.WriteLine("7");

        await Delay(1);

        updateTask.IsCompleted.Should().BeTrue();
        c = (ReplicaMethodComputed <string>) await c.Update();

        c.IsConsistent().Should().BeTrue();
        c.Value.Should().Be("c");

        await serving.DisposeAsync();
    }
        public async Task DropReconnectTest()
        {
            if (TestRunnerInfo.IsBuildAgent())
            {
                // TODO: Fix intermittent failures on GitHub
                return;
            }

            var serving = await WebHost.Serve();

            var publisher  = WebServices.GetRequiredService <IPublisher>();
            var replicator = ClientServices.GetRequiredService <IReplicator>();
            var tp         = Services.GetRequiredService <ITimeService>();

            Debug.WriteLine("0");
            var pub = await publisher.Publish(_ => tp.GetTime());

            var rep = replicator.GetOrAdd <DateTime>(pub.Ref);

            Debug.WriteLine("1");
            await rep.RequestUpdate().AsAsyncFunc()
            .Should().CompleteWithinAsync(TimeSpan.FromMinutes(1));

            Debug.WriteLine("2");
            var state = replicator.GetPublisherConnectionState(pub.Publisher.Id);

            state.Computed.IsConsistent().Should().BeTrue();
            Debug.WriteLine("3");
            await state.Computed.Update(false);

            Debug.WriteLine("4");
            state.Should().Be(replicator.GetPublisherConnectionState(pub.Publisher.Id));
            state.Value.Should().BeTrue();

            Debug.WriteLine("WebServer: stopping.");
            await serving.DisposeAsync();

            Debug.WriteLine("WebServer: stopped.");

            // First try -- should fail w/ WebSocketException or ChannelClosedException
            Debug.WriteLine("5");
            await rep.RequestUpdate().AsAsyncFunc()
            .Should().ThrowAsync <Exception>();

            Debug.WriteLine("6");
            state.Should().Be(replicator.GetPublisherConnectionState(pub.Publisher.Id));
            await state.Computed.Update(false);

            Debug.WriteLine("7");
            state.Should().Be(replicator.GetPublisherConnectionState(pub.Publisher.Id));
            state.Error.Should().BeAssignableTo <Exception>();

            // Second try -- should fail w/ WebSocketException
            Debug.WriteLine("8");
            await rep.Computed.Update(false).AsAsyncFunc()
            .Should().ThrowAsync <WebSocketException>();

            Debug.WriteLine("9");
            rep.UpdateError.Should().BeOfType <WebSocketException>();
            await state.Computed.Update(false);

            Debug.WriteLine("10");
            state.Error.Should().BeOfType <WebSocketException>();

            // The remaining part of this test shouldn't work:
            // since the underlying web host is actually re-created on
            // every ServeAsync call, its endpoints change,
            // and moreover, IPublisher, etc. dies there,
            // so reconnect won't happen in this case.
            //
            // TODO: Add similar test relying on Replica Services.

            /*
             * Debug.WriteLine("WebServer: starting.");
             * serving = await WebHost.ServeAsync();
             * await Task.Delay(1000);
             * Debug.WriteLine("WebServer: started.");
             *
             * Debug.WriteLine("11");
             * await rep.RequestUpdateAsync().AsAsyncFunc()
             *  .Should().CompleteWithinAsync(TimeSpan.FromMinutes(1));
             * Debug.WriteLine("12");
             * await state.Computed.UpdateAsync(false);
             * Debug.WriteLine("13");
             * state.Value.Should().BeTrue();
             *
             * Debug.WriteLine("100");
             * await serving.DisposeAsync();
             * Debug.WriteLine("101");
             */
        }
示例#18
0
    public async Task DistributionTest()
    {
        if (TestRunnerInfo.IsBuildAgent())
        {
            return; // No Redis on build agent for now
        }
        var queueName = "iq";

        await using var queue = await CreateQueue <int>(queueName, true).ConfigureAwait(false);

        var itemCount   = 10_000;
        var writerCount = 4;
        var readerCount = 8;

        async Task Writer(int writerIndex)
        {
            await using var q = await CreateQueue <int>(queueName).ConfigureAwait(false);

            var rnd = new Random();

            for (var i = writerIndex; i < itemCount; i += writerCount)
            {
                await q.Enqueue(i).ConfigureAwait(false);

                var delay = rnd.Next(10);
                if (delay < 3)
                {
                    await Task.Delay(delay);
                }
            }
        }

        var writeTasks = Enumerable.Range(0, writerCount)
                         .Select(i => Task.Run(() => Writer(i)))
                         .ToArray();
        var writeTask = Task.WhenAll(writeTasks);

        async Task <List <int> > Reader()
        {
            await using var q = await CreateQueue <int>(queueName).ConfigureAwait(false);

            var rnd  = new Random();
            var list = new List <int>();

            while (true)
            {
                var valueOpt = await q.Dequeue()
                               .WithTimeout(TimeSpan.FromSeconds(1))
                               .ConfigureAwait(false);

                if (!valueOpt.IsSome(out var value))
                {
                    writeTask.IsCompleted.Should().BeTrue();
                    break;
                }
                list.Add(value);
                var delay = rnd.Next(10);
                if (delay < 2)
                {
                    await Task.Delay(delay);
                }
            }
            return(list);
        }

        var sw = new Stopwatch();

        sw.Start();

        var readTasks = Enumerable.Range(0, readerCount)
                        .Select(_ => Task.Run(Reader))
                        .ToArray();
        var lists = await Task.WhenAll(readTasks).ConfigureAwait(false);

        Out.WriteLine($"{sw.Elapsed}: completed");

        var items = lists.SelectMany(i => i).ToImmutableHashSet();

        items.Count.Should().Be(itemCount);
    }