public async Task BufferMore(int maxPageSize, int[] pageSizes)
        {
            (ItemProducer itemProducer, ReadOnlyCollection <ToDoItem> allItems)itemFactory = MockItemProducerFactory.Create(
                responseMessagesPageSize: pageSizes,
                maxPageSize: maxPageSize,
                cancellationToken: this.cancellationToken);

            ItemProducer itemProducer     = itemFactory.itemProducer;
            int          currentItemCount = pageSizes[0];

            // Single thread
            for (int iterations = 0; iterations < 10; iterations++)
            {
                // Only first buffer more should go through
                await itemProducer.BufferMoreIfEmptyAsync(this.cancellationToken);

                Assert.AreEqual(currentItemCount, itemProducer.BufferedItemCount);
            }

            for (int roundTrip = 1; roundTrip < pageSizes.Length; roundTrip++)
            {
                await itemProducer.BufferMoreDocumentsAsync(this.cancellationToken);

                currentItemCount += pageSizes[roundTrip];
                Assert.AreEqual(currentItemCount, itemProducer.BufferedItemCount);
            }

            Assert.AreEqual(itemFactory.allItems.Count, itemProducer.BufferedItemCount);

            // Verify that Buffer More does nothing after all pages are already loaded
            for (int roundTrip = 0; roundTrip < pageSizes.Length; roundTrip++)
            {
                await itemProducer.BufferMoreDocumentsAsync(this.cancellationToken);

                Assert.AreEqual(itemFactory.allItems.Count, itemProducer.BufferedItemCount);
            }

            // Parallel
            itemFactory = MockItemProducerFactory.Create(
                responseMessagesPageSize: pageSizes,
                maxPageSize: 10,
                cancellationToken: this.cancellationToken);
            itemProducer = itemFactory.itemProducer;

            List <Task> tasks = new List <Task>();

            for (int i = 0; i < pageSizes.Length; i++)
            {
                tasks.Add(Task.Run(async() => { await itemProducer.BufferMoreDocumentsAsync(this.cancellationToken); }));
            }

            await Task.WhenAll(tasks);

            Assert.AreEqual(currentItemCount, itemProducer.BufferedItemCount);
        }
        public async Task TestMoveNextAsync(int pageSize, int maxPageSize)
        {
            List <int[]> combinations = new List <int[]>()
            {
                // Create all combination with empty pages
                new int[] { pageSize },
                new int[] { pageSize, 0 },
                new int[] { 0, pageSize },
                new int[] { pageSize, pageSize },
                new int[] { pageSize, 0, 0 },
                new int[] { 0, pageSize, 0 },
                new int[] { 0, 0, pageSize },
                new int[] { pageSize, 0, pageSize },
                new int[] { pageSize, pageSize, pageSize },
            };

            foreach (int[] combination in combinations)
            {
                (ItemProducer itemProducer, ReadOnlyCollection <ToDoItem> allItems)itemFactory = MockItemProducerFactory.Create(
                    responseMessagesPageSize: combination,
                    maxPageSize: maxPageSize,
                    cancellationToken: this.cancellationToken);

                ItemProducer itemProducer = itemFactory.itemProducer;

                List <ToDoItem> itemsRead = new List <ToDoItem>();

                Assert.IsTrue(itemProducer.HasMoreResults);

                while ((await itemProducer.MoveNextAsync(this.cancellationToken)).successfullyMovedNext)
                {
                    Assert.IsTrue(itemProducer.HasMoreResults);
                    string   jsonValue = itemProducer.Current.ToString();
                    ToDoItem item      = JsonConvert.DeserializeObject <ToDoItem>(jsonValue);
                    itemsRead.Add(item);
                }

                Assert.IsFalse(itemProducer.HasMoreResults);

                Assert.AreEqual(itemFactory.allItems.Count, itemsRead.Count);

                CollectionAssert.AreEqual(itemsRead, itemFactory.allItems, new ToDoItemComparer());
            }
        }
Пример #3
0
        private static IList <ToDoItem> GenerateAndMockResponseHelper(
            Mock <CosmosQueryClient> mockQueryClient,
            Mock <IRoutingMapProvider> mockRoutingMap,
            IList <ToDoItem> allItemsOrdered,
            bool isOrderByQuery,
            SqlQuerySpec sqlQuerySpec,
            string containerRid,
            string initContinuationToken,
            int maxPageSize,
            MockPartitionResponse[] mockResponseForSinglePartition,
            CancellationToken cancellationTokenForMocks)
        {
            if (mockResponseForSinglePartition == null)
            {
                throw new ArgumentNullException(nameof(mockResponseForSinglePartition));
            }

            // Loop through all the partitions
            foreach (MockPartitionResponse partitionAndMessages in mockResponseForSinglePartition)
            {
                PartitionKeyRange partitionKeyRange = partitionAndMessages.PartitionKeyRange;

                string previousContinuationToken = initContinuationToken;

                // Loop through each message inside the partition
                List <int[]> messages         = partitionAndMessages.MessagesWithItemIndex;
                int          messagesCount    = messages == null ? 0 : messages.Count;
                int          lastMessageIndex = messagesCount - 1;
                for (int i = 0; i < messagesCount; i++)
                {
                    int[] message = partitionAndMessages.MessagesWithItemIndex[i];

                    string newContinuationToken = null;

                    List <ToDoItem> currentPageItems = new List <ToDoItem>();
                    // Null represents an empty page
                    if (message != null)
                    {
                        foreach (int itemPosition in message)
                        {
                            currentPageItems.Add(allItemsOrdered[itemPosition]);
                        }
                    }

                    // Last message should have null continuation token
                    // Split means it's not the last message for this PK range
                    if (i != lastMessageIndex || partitionAndMessages.HasSplit)
                    {
                        newContinuationToken = Guid.NewGuid().ToString();
                    }

                    QueryResponse queryResponse = QueryResponseMessageFactory.CreateQueryResponse(
                        currentPageItems,
                        isOrderByQuery,
                        newContinuationToken,
                        containerRid);

                    mockQueryClient.Setup(x =>
                                          x.ExecuteItemQueryAsync(
                                              It.IsAny <Uri>(),
                                              ResourceType.Document,
                                              OperationType.Query,
                                              containerRid,
                                              It.IsAny <QueryRequestOptions>(),
                                              It.Is <SqlQuerySpec>(specInput => MockItemProducerFactory.IsSqlQuerySpecEqual(sqlQuerySpec, specInput)),
                                              previousContinuationToken,
                                              It.Is <PartitionKeyRangeIdentity>(rangeId => string.Equals(rangeId.PartitionKeyRangeId, partitionKeyRange.Id) && string.Equals(rangeId.CollectionRid, containerRid)),
                                              It.IsAny <bool>(),
                                              maxPageSize,
                                              cancellationTokenForMocks))
                    .Returns(Task.FromResult(queryResponse));

                    previousContinuationToken = newContinuationToken;
                }

                if (partitionAndMessages.HasSplit)
                {
                    QueryResponse querySplitResponse = QueryResponseMessageFactory.CreateSplitResponse(containerRid);

                    mockRoutingMap.Setup(x =>
                                         x.TryGetOverlappingRangesAsync(
                                             containerRid,
                                             It.Is <Documents.Routing.Range <string> >(inputRange => inputRange.Equals(partitionKeyRange.ToRange())),
                                             true)).Returns(Task.FromResult(partitionAndMessages.GetPartitionKeyRangeOfSplit()));

                    mockQueryClient.Setup(x =>
                                          x.ExecuteItemQueryAsync(
                                              It.IsAny <Uri>(),
                                              ResourceType.Document,
                                              OperationType.Query,
                                              containerRid,
                                              It.IsAny <QueryRequestOptions>(),
                                              It.Is <SqlQuerySpec>(specInput => MockItemProducerFactory.IsSqlQuerySpecEqual(sqlQuerySpec, specInput)),
                                              previousContinuationToken,
                                              It.Is <PartitionKeyRangeIdentity>(rangeId => string.Equals(rangeId.PartitionKeyRangeId, partitionKeyRange.Id) && string.Equals(rangeId.CollectionRid, containerRid)),
                                              It.IsAny <bool>(),
                                              maxPageSize,
                                              cancellationTokenForMocks))
                    .Returns(Task.FromResult(querySplitResponse));

                    GenerateAndMockResponseHelper(
                        mockQueryClient: mockQueryClient,
                        mockRoutingMap: mockRoutingMap,
                        allItemsOrdered: allItemsOrdered,
                        isOrderByQuery: isOrderByQuery,
                        sqlQuerySpec: sqlQuerySpec,
                        containerRid: containerRid,
                        initContinuationToken: previousContinuationToken,
                        maxPageSize: maxPageSize,
                        mockResponseForSinglePartition: partitionAndMessages.Split,
                        cancellationTokenForMocks: cancellationTokenForMocks);
                }
            }

            return(allItemsOrdered);
        }
        public async Task ConcurrentMoveNextAndBufferMore()
        {
            int[] pageSizes = new int[] { 2, 3, 1, 4 };
            (ItemProducer itemProducer, ReadOnlyCollection <ToDoItem> allItems)itemFactory = MockItemProducerFactory.Create(
                responseMessagesPageSize: pageSizes,
                maxPageSize: 10,
                responseDelay: TimeSpan.FromSeconds(1),
                cancellationToken: this.cancellationToken);

            ItemProducer itemProducer = itemFactory.itemProducer;

            // BufferMore
            // Fire and Forget this task.
#pragma warning disable 4014
            Task.Run(() => itemProducer.BufferMoreDocumentsAsync(this.cancellationToken));
#pragma warning restore 4014

            List <ToDoItem> itemsRead = new List <ToDoItem>();
            Assert.AreEqual(0, itemProducer.BufferedItemCount, "Mocked response should be delayed until after move next is called.");

            int itemsToRead = pageSizes[0] + pageSizes[1];
            // Call move next while buffer more is waiting for response.
            // Move next should wait for buffer more to complete then use the results of the buffer more.
            List <ToDoItem> currentPage = await this.ReadItemProducer(itemProducer, itemsToRead);

            itemsRead.AddRange(currentPage);

            Assert.AreEqual(itemsToRead, itemsRead.Count, "All of the first and 2nd page should be read.");
            Assert.AreEqual(1, itemProducer.BufferedItemCount, "The last element should still be buffered. Moving next will cause another buffer.");

            itemsToRead = pageSizes[2] + pageSizes[3];
#pragma warning disable 4014
            Task.Run(() => itemProducer.MoveNextAsync(this.cancellationToken));
#pragma warning restore 4014

            await itemProducer.BufferMoreDocumentsAsync(this.cancellationToken);

            Assert.AreEqual(itemsToRead, itemProducer.BufferedItemCount, "2nd Page should be loaded.");
        }
        public async Task ConcurrentMoveNextAndBufferMore()
        {
            bool   blockExecute  = true;
            int    callBackCount = 0;
            Action callbackBlock = () =>
            {
                int callBackWaitCount = 0;
                callBackCount++;
                while (blockExecute)
                {
                    if (callBackWaitCount++ > 200)
                    {
                        Assert.Fail("The task never started to buffer the items. The callback was never called");
                    }

                    Thread.Sleep(TimeSpan.FromSeconds(.1));
                }

                // Reset the block for the  next call
                blockExecute = true;
            };

            int[] pageSizes = new int[] { 2, 3, 1, 4 };
            (ItemProducer itemProducer, ReadOnlyCollection <ToDoItem> allItems)itemFactory = MockItemProducerFactory.Create(
                responseMessagesPageSize: pageSizes,
                maxPageSize: 10,
                executeCallback: callbackBlock,
                cancellationToken: this.cancellationToken);

            ItemProducer itemProducer = itemFactory.itemProducer;

            // BufferMore
            // Fire and Forget this task.
#pragma warning disable 4014
            Task bufferTask = Task.Run(() => itemProducer.BufferMoreDocumentsAsync(this.cancellationToken));

            // Verify the task started
            int waitCount = 0;
            while (callBackCount == 0)
            {
                if (waitCount++ > 100)
                {
                    Assert.Fail("The task never started to buffer the items. The callback was never called");
                }

                Thread.Sleep(TimeSpan.FromSeconds(.1));
            }

            List <ToDoItem> itemsRead = new List <ToDoItem>();
            Assert.AreEqual(0, itemProducer.BufferedItemCount, "Mocked response should be delayed until after move next is called.");

            int itemsToRead = pageSizes[0] + pageSizes[1];
            // Call move next while buffer more is waiting for response.
            // Move next should wait for buffer more to complete then use the results of the buffer more.
#pragma warning disable 4014
            bool readTaskRunning = false;
            Task readTask        = Task.Run(async() =>
            {
                readTaskRunning             = true;
                List <ToDoItem> currentPage = await this.ReadItemProducer(itemProducer, itemsToRead);
                itemsRead.AddRange(currentPage);
            });
#pragma warning restore 4014

            // Verify the task started
            while (readTaskRunning == false)
            {
                Thread.Sleep(TimeSpan.FromSeconds(.1));
            }

            Assert.AreEqual(0, itemProducer.BufferedItemCount, "The call back block will prevent any item from being buffered");
            Assert.AreEqual(1, callBackCount, "Buffer more should have a lock which prevents multiple executes.");

            // Unblock the buffer task
            blockExecute = false;
            await bufferTask;
            Assert.AreEqual(2, itemProducer.BufferedItemCount, "Buffer should be completed and have 2 items from first page");

            // Unblock the read task
            blockExecute = false;
            await readTask;
            Assert.AreEqual(itemsToRead, itemsRead.Count, "All of the first and 2nd page should be read.");
            Assert.AreEqual(1, itemProducer.BufferedItemCount, "The last element should still be buffered. Moving next will cause another buffer.");

            itemsToRead = pageSizes[2] + pageSizes[3];
#pragma warning disable 4014
            Task moveNext = Task.Run(() => itemProducer.MoveNextAsync(this.cancellationToken));
#pragma warning restore 4014
            while (callBackCount == 2)
            {
                if (waitCount++ > 100)
                {
                    Assert.Fail("The task never started to buffer the items. The callback was never called");
                }

                Thread.Sleep(TimeSpan.FromSeconds(.1));
            }

            bufferTask = Task.Run(() => itemProducer.BufferMoreDocumentsAsync(this.cancellationToken));

            Assert.AreEqual(3, callBackCount, "Buffer more should have a lock which prevents multiple executes.");

            blockExecute = false;
            await moveNext;

            blockExecute = false;
            await bufferTask;
            Assert.AreEqual(itemsToRead, itemProducer.BufferedItemCount, "2nd Page should be loaded.");
        }