Sorted tests by issue # (#1646)

Sorted tests by issue # (#1646)

--- src/Stratis.Bitcoin.Tests/Consensus/ChainedHeaderTreeTest.cs
+++ src/Stratis.Bitcoin.Tests/Consensus/ChainedHeaderTreeTest.cs
@@ -1484,6 +1484,77 @@
         }

         /// <summary>
+        /// Issue 30 @ Chain is 10 headers long. All headers are "Data Available". Call PartialValidationSucceeded on
+        /// the first header. First header is marked Partially Validated (PV). Make sure the next header is also marked
+        /// for partial validation.
+        /// </summary>
+        [Fact]
+        public void ChainWith10DataAvailableHeaders_PartialValidationSucceededOnTheFirstHeaderCalled_RemainingHeadersPartiallyValidated()
+        {
+            // Chain header tree setup. Initial chain has 2 headers.
+            // Example: h1=h2.
+            const int initialChainSize = 2;
+            TestContext ctx = new TestContextBuilder()
+                .WithInitialChain(initialChainSize)
+                .Build();
+            ChainedHeaderTree cht = ctx.ChainedHeaderTree;
+            ChainedHeader chainTip = ctx.InitialChainTip;
+
+            // Extend chain by 10 headers and connect it to CHT.
+            // Example: h1=h2=h3=h4=h5=h6=h7=h8=h9=h10=h11=h12.
+            const int extensionSize = 10;
+            chainTip = ctx.ExtendAChain(extensionSize, chainTip);
+            List<BlockHeader> listOfExtendedHeaders = ctx.ChainedHeaderToList(chainTip, extensionSize);
+            ConnectNewHeadersResult connectionResult = cht.ConnectNewHeaders(1, listOfExtendedHeaders);
+            ChainedHeader consumed = connectionResult.Consumed;
+
+            // Download all header blocks and call PartialValidationSucceeded on h3.
+            ChainedHeader[] originalHeaderArray = chainTip.ToArray(extensionSize);
+            ChainedHeader[] headerArray = consumed.ToArray(extensionSize);
+            ChainedHeader firstHeader = headerArray[0];
+            ChainedHeader secondHeader = headerArray[1];
+            for (int i = 0; i < headerArray.Length; i++)
+            {
+                cht.BlockDataDownloaded(headerArray[i], originalHeaderArray[i].Block);
+            }
+
+            List<ChainedHeaderBlock> listOfHeaders = cht.PartialValidationSucceeded(firstHeader, out bool fullValidationRequired);
+
+            // First header validation state should be "PartiallyValidated" and next header returned.
+            firstHeader.BlockValidationState.Should().Be(ValidationState.PartiallyValidated);
+            listOfHeaders.Should().HaveCount(1);
+            listOfHeaders.First().ChainedHeader.HashBlock.Should().Be(secondHeader.HashBlock);
+        }
+
+        /// <summary>
+        /// Issue 31 @ Chain is 2 blocks long, CT is header 1, call PartialValidationSucceeded on header 2.
+        /// Make sure that full validation is required.
+        /// </summary>
+        [Fact]
+        public void ChainOfHeaders_CallPartialValidationSucceededOnBlockBeyondConsensusTip_FullValidationIsRequired()
+        {
+            // Chain header tree setup.
+            const int initialChainSize = 1;
+            TestContext testContext = new TestContextBuilder().WithInitialChain(initialChainSize).Build();
+            ChainedHeaderTree chainedHeaderTree = testContext.ChainedHeaderTree;
+            ChainedHeader initialChainTip = testContext.InitialChainTip;
+
+            const int chainExtension = 1;
+            ChainedHeader chainTip = testContext.ExtendAChain(chainExtension, initialChainTip);
+            List<BlockHeader> listOfChainHeaders = testContext.ChainedHeaderToList(chainTip, 1);
+
+            // Chain is 2 blocks long: h1=h2.
+            chainedHeaderTree.ConnectNewHeaders(1, listOfChainHeaders);
+
+            // Call PartialValidationSucceeded on h2.
+            chainedHeaderTree.PartialValidationSucceeded(chainTip, out bool fullValidationRequired);
+
+            chainTip.BlockValidationState.Should().Be(ValidationState.PartiallyValidated);
+
+            fullValidationRequired.Should().BeTrue();
+        }
+
+        /// <summary>
         /// Issue 32 @ Call FullValidationSucceeded on some header.
         /// Make sure header.ValidationState == FV
         /// </summary>
@@ -1573,6 +1644,68 @@
         }

         /// <summary>
+        /// Issue 34 @ We receive headers message
+        /// (first header in the message is HEADERS_START and last is HEADERS_END).
+        /// AV = assume valid header, CP1,CP2 - checkpointed headers. '---' some headers.
+        /// CP1---HEADERS_START---AV---HEADERS_END---CP2---
+        /// Check that headers till AV(including AV) are marked for download.
+        /// Headers after AV are not marked for download.
+        /// </summary>
+        [Fact]
+        public void
+        ConnectHeaders_AssumeValidBetweenTwoCheckPoints_BothCheckpointsExcluded_DownloadUpToIncludingAssumeValid()
+        {
+            const int initialChainSize = 14;
+            const int chainExtension = 16;
+            const int assumeValidHeaderHeight = 20;
+            const int headersStartHeight = 15;
+            const int headersEndHeight = 25;
+
+            TestContext testContext = new TestContextBuilder().WithInitialChain(initialChainSize).UseCheckpoints(true).Build();
+            ChainedHeaderTree chainedHeaderTree = testContext.ChainedHeaderTree;
+            ChainedHeader initialChainTip = testContext.InitialChainTip;
+            ChainedHeader chainTip = testContext.ExtendAChain(chainExtension, initialChainTip);
+
+            // Assume valid header at h20.
+            ChainedHeader assumeValidChainHeader = chainTip.GetAncestor(assumeValidHeaderHeight);
+            testContext.ConsensusSettings.BlockAssumedValid = assumeValidChainHeader.HashBlock;
+
+            List<BlockHeader> listOfChainHeaders = testContext.ChainedHeaderToList(chainTip, initialChainSize + chainExtension);
+
+            // Two checkpoints at h10 and h30.
+            const int firstCheckpointHeight = 10;
+            const int secondCheckpointHeight = 30;
+            var checkpoint1 = new CheckpointFixture(firstCheckpointHeight, listOfChainHeaders[firstCheckpointHeight - 1]);
+            var checkpoint2 = new CheckpointFixture(secondCheckpointHeight, listOfChainHeaders[secondCheckpointHeight - 1]);
+            testContext.SetupCheckpoints(checkpoint1, checkpoint2);
+
+            // Present chain h15->h25 covering assume valid at h20 and excluding both checkpoints.
+            int headersToPresentCount = (headersEndHeight - headersStartHeight + 1 /* inclusive */);
+            listOfChainHeaders = listOfChainHeaders.Skip(headersStartHeight - 1).Take(headersToPresentCount).ToList();
+
+            // Present chain:
+            // ----CP1----HEADERS_START----AV----HEADERS_END----CP2
+            // h1--h10--------h15----------h20------h25---------h30
+            ConnectNewHeadersResult connectedHeadersResultNew = chainedHeaderTree.ConnectNewHeaders(1, listOfChainHeaders);
+
+            // From initialised chain up to and including assume valid (h15->h20) inclusive are marked for download.
+            // Headers after assume valid (h21->h30) are not marked for download.
+            Assert.Equal(listOfChainHeaders.First(), connectedHeadersResultNew.DownloadFrom.Header);
+            Assert.Equal(assumeValidChainHeader.Header, connectedHeadersResultNew.DownloadTo.Header);
+
+            // Check block data availability of headers marked for download.
+            Assert.True(connectedHeadersResultNew.HaveBlockDataAvailabilityStateOf(BlockDataAvailabilityState.BlockRequired));
+
+            // Check block data availability of headers not marked for download after assumed valid block.
+            ChainedHeader chainedHeader = chainTip;
+            while (chainedHeader.Height > assumeValidChainHeader.Height)
+            {
+                chainedHeader.BlockDataAvailability.Should().Be(BlockDataAvailabilityState.HeaderOnly);
+                chainedHeader = chainedHeader.Previous;
+            }
+        }
+
+        /// <summary>
         /// Issue 35 @ We receive headers message
         /// (first header in the message is HEADERS_START and last is HEADERS_END).
         /// AV = assume valid header, CP1,CP2 - checkpointed headers. '---' some headers.
@@ -1621,68 +1754,6 @@

             // Check block data availability of headers marked for download.
             Assert.True(connectedHeadersResultNew.HaveBlockDataAvailabilityStateOf(BlockDataAvailabilityState.BlockRequired));
-        }
-
-        /// <summary>
-        /// Issue 34 @ We receive headers message
-        /// (first header in the message is HEADERS_START and last is HEADERS_END).
-        /// AV = assume valid header, CP1,CP2 - checkpointed headers. '---' some headers.
-        /// CP1---HEADERS_START---AV---HEADERS_END---CP2---
-        /// Check that headers till AV(including AV) are marked for download.
-        /// Headers after AV are not marked for download.
-        /// </summary>
-        [Fact]
-        public void
-        ConnectHeaders_AssumeValidBetweenTwoCheckPoints_BothCheckpointsExcluded_DownloadUpToIncludingAssumeValid()
-        {
-            const int initialChainSize = 14;
-            const int chainExtension = 16;
-            const int assumeValidHeaderHeight = 20;
-            const int headersStartHeight = 15;
-            const int headersEndHeight = 25;
-
-            TestContext testContext = new TestContextBuilder().WithInitialChain(initialChainSize).UseCheckpoints(true).Build();
-            ChainedHeaderTree chainedHeaderTree = testContext.ChainedHeaderTree;
-            ChainedHeader initialChainTip = testContext.InitialChainTip;
-            ChainedHeader chainTip = testContext.ExtendAChain(chainExtension, initialChainTip);
-
-            // Assume valid header at h20.
-            ChainedHeader assumeValidChainHeader = chainTip.GetAncestor(assumeValidHeaderHeight);
-            testContext.ConsensusSettings.BlockAssumedValid = assumeValidChainHeader.HashBlock;
-
-            List<BlockHeader> listOfChainHeaders = testContext.ChainedHeaderToList(chainTip, initialChainSize + chainExtension);
-
-            // Two checkpoints at h10 and h30.
-            const int firstCheckpointHeight = 10;
-            const int secondCheckpointHeight = 30;
-            var checkpoint1 = new CheckpointFixture(firstCheckpointHeight, listOfChainHeaders[firstCheckpointHeight - 1]);
-            var checkpoint2 = new CheckpointFixture(secondCheckpointHeight, listOfChainHeaders[secondCheckpointHeight - 1]);
-            testContext.SetupCheckpoints(checkpoint1, checkpoint2);
-
-            // Present chain h15->h25 covering assume valid at h20 and excluding both checkpoints.
-            int headersToPresentCount = (headersEndHeight - headersStartHeight + 1 /* inclusive */);
-            listOfChainHeaders = listOfChainHeaders.Skip(headersStartHeight - 1).Take(headersToPresentCount).ToList();
-
-            // Present chain:
-            // ----CP1----HEADERS_START----AV----HEADERS_END----CP2
-            // h1--h10--------h15----------h20------h25---------h30
-            ConnectNewHeadersResult connectedHeadersResultNew = chainedHeaderTree.ConnectNewHeaders(1, listOfChainHeaders);
-
-            // From initialised chain up to and including assume valid (h15->h20) inclusive are marked for download.
-            // Headers after assume valid (h21->h30) are not marked for download.
-            Assert.Equal(listOfChainHeaders.First(), connectedHeadersResultNew.DownloadFrom.Header);
-            Assert.Equal(assumeValidChainHeader.Header, connectedHeadersResultNew.DownloadTo.Header);
-
-            // Check block data availability of headers marked for download.
-            Assert.True(connectedHeadersResultNew.HaveBlockDataAvailabilityStateOf(BlockDataAvailabilityState.BlockRequired));
-
-            // Check block data availability of headers not marked for download after assumed valid block.
-            ChainedHeader chainedHeader = chainTip;
-            while (chainedHeader.Height > assumeValidChainHeader.Height)
-            {
-                chainedHeader.BlockDataAvailability.Should().Be(BlockDataAvailabilityState.HeaderOnly);
-                chainedHeader = chainedHeader.Previous;
-            }
         }

         /// <summary>
@@ -1727,6 +1798,135 @@
         }

         /// <summary>
+        /// Issue 37 @ CT advances at 1000 blocks. Make sure that block data for headers 1-900 are removed.
+        /// </summary>
+        [Fact]
+        public void ChainTipAdvancesAt1000Blocks_DataForHeadersForFirst900HeadersAreRemoved()
+        {
+            // Chain header tree setup. Initial chain has 1 header.
+            // Example: h1.
+            const int initialChainSize = 1;
+            TestContext ctx = new TestContextBuilder().WithInitialChain(initialChainSize).Build();
+            ChainedHeaderTree cht = ctx.ChainedHeaderTree;
+            ChainedHeader chainTip = ctx.InitialChainTip;
+
+            // Extend a chain by 1000 headers.
+            // Example: h1=h2=...=h1001.
+            const int extensionSize = 1000;
+            chainTip = ctx.ExtendAChain(extensionSize, chainTip);
+            List<BlockHeader> listOfCurrentChainHeaders = ctx.ChainedHeaderToList(chainTip, extensionSize);
+
+            // Peer 1 presents a chain.
+            const int peerId = 1;
+            ConnectNewHeadersResult connectionResult = cht.ConnectNewHeaders(peerId, listOfCurrentChainHeaders);
+            ChainedHeader[] consumedHeaders = connectionResult.Consumed.ToArray(extensionSize);
+            ChainedHeader[] originalHeaders = chainTip.ToArray(extensionSize);
+
+            // Sync all blocks.
+            for (int i = 0; i < extensionSize; i++)
+            {
+                ChainedHeader currentChainTip = consumedHeaders[i];
+
+                cht.BlockDataDownloaded(currentChainTip, originalHeaders[i].Block);
+                cht.PartialValidationSucceeded(currentChainTip, out bool fullValidationRequired);
+                cht.ConsensusTipChanged(currentChainTip);
+            }
+
+            // Headers 2-901 should have block data null.
+            // Headers 901 - 1001 should have block data.
+            Dictionary<uint256, ChainedHeader> connectedHeaders = cht.GetChainedHeadersByHash();
+            foreach (ChainedHeader consumedHeader in consumedHeaders)
+            {
+                if (consumedHeader.Height <= (extensionSize - ChainedHeaderTree.KeepBlockDataForLastBlocks))
+                {
+                    connectedHeaders[consumedHeader.HashBlock].Block.Should().BeNull();
+                }
+                else
+                {
+                    connectedHeaders[consumedHeader.HashBlock].Block.Should().NotBeNull();
+                }
+            }
+        }
+
+        /// <summary>
+        /// Issue 38 @ CT advances at 150. Alternative chain with fork at 120 and total length 160 is presented.
+        /// CT switches to 160. Make sure block data for 0-60 is removed.
+        /// </summary>
+        [Fact]
+        public void ChainTipAdvancesAt150Blocks_AlternativeChainPresented_RelevantBlockDataIsRemoved()
+        {
+            // Chain header tree setup. Initial chain has 1 header.
+            // Example: h1.
+            const int initialChainSize = 1;
+            TestContext ctx = new TestContextBuilder().WithInitialChain(initialChainSize).Build();
+            ChainedHeaderTree cht = ctx.ChainedHeaderTree;
+            ChainedHeader chainTip = ctx.InitialChainTip;
+
+            // Create a chain A with 150 headers.
+            // Example: h1=h2=...h120=h121=a122=a123=a124=...=a151.
+            const int extensionBeforeFork = 120;
+            const int chainAExtensionSize = 150;
+            chainTip = ctx.ExtendAChain(extensionBeforeFork, chainTip);
+            ChainedHeader chainATip = ctx.ExtendAChain(chainAExtensionSize - extensionBeforeFork, chainTip);
+            List<BlockHeader> listOfCurrentChainAHeaders = ctx.ChainedHeaderToList(chainATip, chainAExtensionSize);
+
+            // Peer 1 presents a chain A.
+            const int peer1Id = 1;
+            ConnectNewHeadersResult connectionResult = cht.ConnectNewHeaders(peer1Id, listOfCurrentChainAHeaders);
+            ChainedHeader[] consumedChainAHeaders = connectionResult.Consumed.ToArray(chainAExtensionSize);
+            ChainedHeader[] originalChainAHeaders = chainATip.ToArray(chainAExtensionSize);
+
+            // Sync all blocks for chain A.
+            for (int i = 0; i < chainAExtensionSize; i++)
+            {
+                ChainedHeader currentChainTip = consumedChainAHeaders[i];
+
+                cht.BlockDataDownloaded(currentChainTip, originalChainAHeaders[i].Block);
+                cht.PartialValidationSucceeded(currentChainTip, out bool fullValidationRequired);
+                cht.ConsensusTipChanged(currentChainTip);
+            }
+
+            // Create a chain B with 160 headers and a fork at 121.
+            // Example: h1=h2=...h120=h121=b122=b123=b124=...=b161.
+            const int chainBExtensionSize = 160;
+            ChainedHeader chainBTip = ctx.ExtendAChain(chainBExtensionSize - extensionBeforeFork, chainTip);
+            List<BlockHeader> listOfCurrentChainBHeaders = ctx.ChainedHeaderToList(chainBTip, chainBExtensionSize - extensionBeforeFork);
+
+            // Peer 2 presents a chain B.
+            const int peer2Id = 2;
+            connectionResult = cht.ConnectNewHeaders(peer2Id, listOfCurrentChainBHeaders);
+            ChainedHeader[] consumedChainBHeaders = connectionResult.Consumed.ToArray(chainBExtensionSize - extensionBeforeFork);
+            ChainedHeader[] originalChainBHeaders = chainBTip.ToArray(chainBExtensionSize - extensionBeforeFork);
+
+            // Sync all new blocks for chain B.
+            for (int i = 0; i < chainBExtensionSize - extensionBeforeFork; i++)
+            {
+                ChainedHeader currentChainTip = consumedChainBHeaders[i];
+
+                cht.BlockDataDownloaded(currentChainTip, originalChainBHeaders[i].Block);
+                cht.PartialValidationSucceeded(currentChainTip, out bool fullValidationRequired);
+            }
+
+            cht.ConsensusTipChanged(connectionResult.Consumed);
+
+            // Headers 2-60 should have block data null.
+            // Headers 61 - 161 should have block data.
+            Dictionary<uint256, ChainedHeader> connectedHeaders = cht.GetChainedHeadersByHash();
+            ChainedHeader[] allChainBHeaders = connectionResult.Consumed.ToArray(chainBExtensionSize);
+            foreach (ChainedHeader consumedHeader in allChainBHeaders)
+            {
+                if (consumedHeader.Height <= (chainBExtensionSize - ChainedHeaderTree.KeepBlockDataForLastBlocks))
+                {
+                    connectedHeaders[consumedHeader.HashBlock].Block.Should().BeNull();
+                }
+                else
+                {
+                    connectedHeaders[consumedHeader.HashBlock].Block.Should().NotBeNull();
+                }
+            }
+        }
+
+        /// <summary>
         /// Issue 39 @ Initial chain is 20 headers long. Single checkpoint is at 1000. Max reorg is 10. Finalized height is 10.
         /// We receive a chain of 100 headers from peer1. Nothing is marked for download. Peer2 presents a chain which forks
         /// at 50 and goes to 150. Nothing is marked for download but the chain is accepted.
@@ -1796,178 +1996,6 @@
         }

         /// <summary>
-        /// Issue 37 @ CT advances at 1000 blocks. Make sure that block data for headers 1-900 are removed.
-        /// </summary>
-        [Fact]
-        public void ChainTipAdvancesAt1000Blocks_DataForHeadersForFirst900HeadersAreRemoved()
-        {
-            // Chain header tree setup. Initial chain has 1 header.
-            // Example: h1.
-            const int initialChainSize = 1;
-            TestContext ctx = new TestContextBuilder().WithInitialChain(initialChainSize).Build();
-            ChainedHeaderTree cht = ctx.ChainedHeaderTree;
-            ChainedHeader chainTip = ctx.InitialChainTip;
-
-            // Extend a chain by 1000 headers.
-            // Example: h1=h2=...=h1001.
-            const int extensionSize = 1000;
-            chainTip = ctx.ExtendAChain(extensionSize, chainTip);
-            List<BlockHeader> listOfCurrentChainHeaders = ctx.ChainedHeaderToList(chainTip, extensionSize);
-
-            // Peer 1 presents a chain.
-            const int peerId = 1;
-            ConnectNewHeadersResult connectionResult = cht.ConnectNewHeaders(peerId, listOfCurrentChainHeaders);
-            ChainedHeader[] consumedHeaders = connectionResult.Consumed.ToArray(extensionSize);
-            ChainedHeader[] originalHeaders = chainTip.ToArray(extensionSize);
-
-            // Sync all blocks.
-            for (int i = 0; i < extensionSize; i++)
-            {
-                ChainedHeader currentChainTip = consumedHeaders[i];
-
-                cht.BlockDataDownloaded(currentChainTip, originalHeaders[i].Block);
-                cht.PartialValidationSucceeded(currentChainTip, out bool fullValidationRequired);
-                cht.ConsensusTipChanged(currentChainTip);
-            }
-
-            // Headers 2-901 should have block data null.
-            // Headers 901 - 1001 should have block data.
-            Dictionary<uint256, ChainedHeader> connectedHeaders = cht.GetChainedHeadersByHash();
-            foreach (ChainedHeader consumedHeader in consumedHeaders)
-            {
-                if (consumedHeader.Height <= (extensionSize - ChainedHeaderTree.KeepBlockDataForLastBlocks))
-                {
-                    connectedHeaders[consumedHeader.HashBlock].Block.Should().BeNull();
-                }
-                else
-                {
-                    connectedHeaders[consumedHeader.HashBlock].Block.Should().NotBeNull();
-                }
-            }
-        }
-
-        /// <summary>
-        /// Issue 38 @ CT advances at 150. Alternative chain with fork at 120 and total length 160 is presented.
-        /// CT switches to 160. Make sure block data for 0-60 is removed.
-        /// </summary>
-        [Fact]
-        public void ChainTipAdvancesAt150Blocks_AlternativeChainPresented_RelevantBlockDataIsRemoved()
-        {
-            // Chain header tree setup. Initial chain has 1 header.
-            // Example: h1.
-            const int initialChainSize = 1;
-            TestContext ctx = new TestContextBuilder().WithInitialChain(initialChainSize).Build();
-            ChainedHeaderTree cht = ctx.ChainedHeaderTree;
-            ChainedHeader chainTip = ctx.InitialChainTip;
-
-            // Create a chain A with 150 headers.
-            // Example: h1=h2=...h120=h121=a122=a123=a124=...=a151.
-            const int extensionBeforeFork = 120;
-            const int chainAExtensionSize = 150;
-            chainTip = ctx.ExtendAChain(extensionBeforeFork, chainTip);
-            ChainedHeader chainATip = ctx.ExtendAChain(chainAExtensionSize - extensionBeforeFork, chainTip);
-            List<BlockHeader> listOfCurrentChainAHeaders = ctx.ChainedHeaderToList(chainATip, chainAExtensionSize);
-
-            // Peer 1 presents a chain A.
-            const int peer1Id = 1;
-            ConnectNewHeadersResult connectionResult = cht.ConnectNewHeaders(peer1Id, listOfCurrentChainAHeaders);
-            ChainedHeader[] consumedChainAHeaders = connectionResult.Consumed.ToArray(chainAExtensionSize);
-            ChainedHeader[] originalChainAHeaders = chainATip.ToArray(chainAExtensionSize);
-
-            // Sync all blocks for chain A.
-            for (int i = 0; i < chainAExtensionSize; i++)
-            {
-                ChainedHeader currentChainTip = consumedChainAHeaders[i];
-
-                cht.BlockDataDownloaded(currentChainTip, originalChainAHeaders[i].Block);
-                cht.PartialValidationSucceeded(currentChainTip, out bool fullValidationRequired);
-                cht.ConsensusTipChanged(currentChainTip);
-            }
-
-            // Create a chain B with 160 headers and a fork at 121.
-            // Example: h1=h2=...h120=h121=b122=b123=b124=...=b161.
-            const int chainBExtensionSize = 160;
-            ChainedHeader chainBTip = ctx.ExtendAChain(chainBExtensionSize - extensionBeforeFork, chainTip);
-            List<BlockHeader> listOfCurrentChainBHeaders = ctx.ChainedHeaderToList(chainBTip, chainBExtensionSize - extensionBeforeFork);
-
-            // Peer 2 presents a chain B.
-            const int peer2Id = 2;
-            connectionResult = cht.ConnectNewHeaders(peer2Id, listOfCurrentChainBHeaders);
-            ChainedHeader[] consumedChainBHeaders = connectionResult.Consumed.ToArray(chainBExtensionSize - extensionBeforeFork);
-            ChainedHeader[] originalChainBHeaders = chainBTip.ToArray(chainBExtensionSize - extensionBeforeFork);
-
-            // Sync all new blocks for chain B.
-            for (int i = 0; i < chainBExtensionSize - extensionBeforeFork; i++)
-            {
-                ChainedHeader currentChainTip = consumedChainBHeaders[i];
-
-                cht.BlockDataDownloaded(currentChainTip, originalChainBHeaders[i].Block);
-                cht.PartialValidationSucceeded(currentChainTip, out bool fullValidationRequired);
-            }
-
-            cht.ConsensusTipChanged(connectionResult.Consumed);
-
-            // Headers 2-60 should have block data null.
-            // Headers 61 - 161 should have block data.
-            Dictionary<uint256, ChainedHeader> connectedHeaders = cht.GetChainedHeadersByHash();
-            ChainedHeader[] allChainBHeaders = connectionResult.Consumed.ToArray(chainBExtensionSize);
-            foreach (ChainedHeader consumedHeader in allChainBHeaders)
-            {
-                if (consumedHeader.Height <= (chainBExtensionSize - ChainedHeaderTree.KeepBlockDataForLastBlocks))
-                {
-                    connectedHeaders[consumedHeader.HashBlock].Block.Should().BeNull();
-                }
-                else
-                {
-                    connectedHeaders[consumedHeader.HashBlock].Block.Should().NotBeNull();
-                }
-            }
-        }
-
-        /// <summary>
-        /// Issue 30 @ Chain is 10 headers long. All headers are "Data Available". Call PartialValidationSucceeded on
-        /// the first header. First header is marked Partially Validated (PV). Make sure the next header is also marked
-        /// for partial validation.
-        /// </summary>
-        [Fact]
-        public void ChainWith10DataAvailableHeaders_PartialValidationSucceededOnTheFirstHeaderCalled_RemainingHeadersPartiallyValidated()
-        {
-            // Chain header tree setup. Initial chain has 2 headers.
-            // Example: h1=h2.
-            const int initialChainSize = 2;
-            TestContext ctx = new TestContextBuilder()
-                .WithInitialChain(initialChainSize)
-                .Build();
-            ChainedHeaderTree cht = ctx.ChainedHeaderTree;
-            ChainedHeader chainTip = ctx.InitialChainTip;
-
-            // Extend chain by 10 headers and connect it to CHT.
-            // Example: h1=h2=h3=h4=h5=h6=h7=h8=h9=h10=h11=h12.
-            const int extensionSize = 10;
-            chainTip = ctx.ExtendAChain(extensionSize, chainTip);
-            List<BlockHeader> listOfExtendedHeaders = ctx.ChainedHeaderToList(chainTip, extensionSize);
-            ConnectNewHeadersResult connectionResult = cht.ConnectNewHeaders(1, listOfExtendedHeaders);
-            ChainedHeader consumed = connectionResult.Consumed;
-
-            // Download all header blocks and call PartialValidationSucceeded on h3.
-            ChainedHeader[] originalHeaderArray = chainTip.ToArray(extensionSize);
-            ChainedHeader[] headerArray = consumed.ToArray(extensionSize);
-            ChainedHeader firstHeader = headerArray[0];
-            ChainedHeader secondHeader = headerArray[1];
-            for (int i = 0; i < headerArray.Length; i++)
-            {
-                cht.BlockDataDownloaded(headerArray[i], originalHeaderArray[i].Block);
-            }
-
-            List<ChainedHeaderBlock> listOfHeaders = cht.PartialValidationSucceeded(firstHeader, out bool fullValidationRequired);
-
-            // First header validation state should be "PartiallyValidated" and next header returned.
-            firstHeader.BlockValidationState.Should().Be(ValidationState.PartiallyValidated);
-            listOfHeaders.Should().HaveCount(1);
-            listOfHeaders.First().ChainedHeader.HashBlock.Should().Be(secondHeader.HashBlock);
-        }
-
-        /// <summary>
         /// Issue 40 @ CT advances after last checkpoint to height LC + MaxReorg + 10. New chain is presented with
         /// fork point at LC + 5, chain is not accepted.
         /// </summary>
@@ -2027,31 +2055,183 @@
         }

         /// <summary>
-        /// Issue 31 @ Chain is 2 blocks long, CT is header 1, call PartialValidationSucceeded on header 2.
-        /// Make sure that full validation is required.
-        /// </summary>
-        [Fact]
-        public void ChainOfHeaders_CallPartialValidationSucceededOnBlockBeyondConsensusTip_FullValidationIsRequired()
-        {
+        /// Issue 41 @ BlockDataDownloaded called on 10 known blocks.
+        /// Make sure that UnconsumedBlocksDataBytes is equal to the sum of serialized sizes of those blocks.
+        /// </summary>
+        [Fact]
+        public void BlockDataDownloadedIsCalled_UnconsumedBlocksDataBytes_Equals_SumOfSerializedBlockSize()
+        {
+            const int initialChainSize = 5;
+            const int chainExtension = 10;
+
             // Chain header tree setup.
-            const int initialChainSize = 1;
-            TestContext testContext = new TestContextBuilder().WithInitialChain(initialChainSize).Build();
+            TestContext testContext = new TestContextBuilder().WithInitialChain(initialChainSize).UseCheckpoints(false).Build();
             ChainedHeaderTree chainedHeaderTree = testContext.ChainedHeaderTree;
             ChainedHeader initialChainTip = testContext.InitialChainTip;

-            const int chainExtension = 1;
-            ChainedHeader chainTip = testContext.ExtendAChain(chainExtension, initialChainTip);
-            List<BlockHeader> listOfChainHeaders = testContext.ChainedHeaderToList(chainTip, 1);
-
-            // Chain is 2 blocks long: h1=h2.
-            chainedHeaderTree.ConnectNewHeaders(1, listOfChainHeaders);
-
-            // Call PartialValidationSucceeded on h2.
-            chainedHeaderTree.PartialValidationSucceeded(chainTip, out bool fullValidationRequired);
-
-            chainTip.BlockValidationState.Should().Be(ValidationState.PartiallyValidated);
-
-            fullValidationRequired.Should().BeTrue();
+            ChainedHeader extendedChainTip = testContext.ExtendAChain(chainExtension, initialChainTip);
+            List<BlockHeader> listOfChainBlockHeaders = testContext.ChainedHeaderToList(extendedChainTip, initialChainSize + chainExtension);
+
+            // Present all chain headers h1->h15.
+            ConnectNewHeadersResult connectNewHeadersResult = chainedHeaderTree.ConnectNewHeaders(1, listOfChainBlockHeaders);
+            Assert.True(connectNewHeadersResult.HaveBlockDataAvailabilityStateOf(BlockDataAvailabilityState.BlockRequired));
+
+            int serializedSizeOfBlocks = 0;
+            foreach (ChainedHeader chainedHeader in connectNewHeadersResult.ToArray())
+            {
+                chainedHeaderTree.BlockDataDownloaded(chainedHeader, extendedChainTip.FindAncestorOrSelf(chainedHeader).Block);
+                serializedSizeOfBlocks += chainedHeader.Block.GetSerializedSize();
+            }
+
+            // UnconsumedBlocksDataBytes is equal to the sum of serialized sizes of the blocks.
+            Assert.Equal(chainedHeaderTree.UnconsumedBlocksDataBytes, serializedSizeOfBlocks);
+        }
+
+        /// <summary>
+        /// Issue 42 @ CT is at 0. 10 headers are presented.
+        /// 10 blocks are downloaded. CT advances to 5.
+        /// Make sure that UnconsumedBlocksDataBytes is equal to
+        /// the sum of serialized sizes of last five blocks.
+        /// CT advances to 10.  Make sure UnconsumedBlocksDataBytes is 0.
+        /// </summary>
+        [Fact]
+        public void PresentHeaders_ChainHeaderTreeAdvances_UnconsumedBlocksDataBytes_Equals_SumOfSerializedBlockSize()
+        {
+            const int initialChainSize = 0;
+            const int chainExtensionSize = 10;
+
+            // Chain header tree setup.
+            TestContext ctx = new TestContextBuilder().WithInitialChain(initialChainSize).UseCheckpoints(false).Build();
+            ChainedHeaderTree chainedHeaderTree = ctx.ChainedHeaderTree;
+            ChainedHeader initialChainTip = ctx.InitialChainTip;
+
+            // 10 headers are presented.
+            ChainedHeader commonChainTip = ctx.ExtendAChain(chainExtensionSize, initialChainTip);
+            List<BlockHeader> listOfChainBlockHeaders = ctx.ChainedHeaderToList(commonChainTip, chainExtensionSize);
+
+            // 10 blocks are downloaded.
+            ConnectNewHeadersResult connectNewHeadersResult = chainedHeaderTree.ConnectNewHeaders(1, listOfChainBlockHeaders);
+            ChainedHeader chainedHeaderTo = connectNewHeadersResult.DownloadTo;
+            chainedHeaderTo.HashBlock.Should().Be(commonChainTip.HashBlock);
+
+            Assert.True(connectNewHeadersResult.HaveBlockDataAvailabilityStateOf(BlockDataAvailabilityState.BlockRequired));
+
+            foreach (ChainedHeader chainedHeader in connectNewHeadersResult.ToArray())
+            {
+                chainedHeaderTree.BlockDataDownloaded(chainedHeader, commonChainTip.FindAncestorOrSelf(chainedHeader).Block);
+                chainedHeaderTree.PartialValidationSucceeded(chainedHeader, out bool fullValidationRequired);
+                fullValidationRequired.Should().BeTrue();
+            }
+
+            // CT advances to 5.
+            chainedHeaderTree.ConsensusTipChanged(chainedHeaderTo.GetAncestor(5));
+
+            int serializedSizeOfBlocks = 0;
+            IEnumerable<ChainedHeader> lastFive = connectNewHeadersResult.ToArray().TakeLast(5);
+            foreach (ChainedHeader chainedHeader in lastFive)
+            {
+                serializedSizeOfBlocks += chainedHeader.Block.GetSerializedSize();
+            }
+
+            // UnconsumedBlocksDataBytes is non-zero and equal to the sum of serialized sizes of last five blocks.
+            Assert.NotEqual(0, chainedHeaderTree.UnconsumedBlocksDataBytes);
+            Assert.Equal(serializedSizeOfBlocks, chainedHeaderTree.UnconsumedBlocksDataBytes);
+
+            // CT advances to 10.
+            chainedHeaderTree.ConsensusTipChanged(chainedHeaderTo);
+
+            // UnconsumedBlocksDataBytes is 0.
+            Assert.Equal(0, chainedHeaderTree.UnconsumedBlocksDataBytes);
+        }
+
+        /// <summary>
+        /// Issue 43 @ CT is at 0. 10 headers are presented. 10 blocks are downloaded.
+        /// CT advances to 5. Alternative chain with fork at 3 and tip at 12 is presented.
+        /// Block data for alternative chain is downloaded. CT changes to block 8 of the 2nd chain.
+        /// Make sure that UnconsumedBlocksDataBytes is equal to the sum of
+        /// serialized sizes of 9b-12b + 6a-10a (a- first chain, b- second chain).
+        /// </summary>
+        [Fact]
+        public void PresentHeaders_BlocksDownloaded_ForkPresented_BlockDataForAlternativeChainDownloaded_ChainTipChanges()
+        {
+            const int initialChainSize = 0;
+            const int chainExtensionSize = 10;
+            const int peerOneId = 1;
+            const int peerTwoId = 2;
+
+            // Chain header tree setup.
+            TestContext testContext = new TestContextBuilder().WithInitialChain(initialChainSize).UseCheckpoints(false).Build();
+            ChainedHeaderTree chainedHeaderTree = testContext.ChainedHeaderTree;
+            ChainedHeader initialChainTip = testContext.InitialChainTip;
+
+            // Chain tip is at 0.
+            Assert.Equal(0, initialChainTip.Height);
+
+            // Headers are presented for h1 -> h10.
+            ChainedHeader extendedChainTip = testContext.ExtendAChain(chainExtensionSize, initialChainTip);
+            List<BlockHeader> listOfExtendedChainBlockHeaders = testContext.ChainedHeaderToList(extendedChainTip, chainExtensionSize);
+            ConnectNewHeadersResult connectNewHeadersResult = chainedHeaderTree.ConnectNewHeaders(peerOneId, listOfExtendedChainBlockHeaders);
+            Assert.Equal(connectNewHeadersResult.DownloadFrom.Header, extendedChainTip.GetAncestor(initialChainSize + 1).Header); // h1
+            Assert.Equal(connectNewHeadersResult.DownloadTo.Header, extendedChainTip.Header); // h10
+            Assert.True(connectNewHeadersResult.HaveBlockDataAvailabilityStateOf(BlockDataAvailabilityState.BlockRequired));
+
+            // 10 blocks are downloaded.
+            foreach (ChainedHeader chainedHeader in connectNewHeadersResult.ToArray())
+            {
+                chainedHeaderTree.BlockDataDownloaded(chainedHeader, extendedChainTip.FindAncestorOrSelf(chainedHeader).Block);
+                chainedHeaderTree.PartialValidationSucceeded(chainedHeader, out bool fullValidationRequired);
+
+                if (chainedHeader.Height <= 5) // CT advances to 5.
+                {
+                    chainedHeaderTree.ConsensusTipChanged(chainedHeader);
+                }
+            }
+
+            // Alternative chain with fork at h3 and tip at h12 is presented.
+            const int heightOfFork = 3;
+            const int chainBExtension = 9;
+            ChainedHeader forkedChainHeader = connectNewHeadersResult.DownloadTo.GetAncestor(heightOfFork);
+            ChainedHeader tipOfFork = testContext.ExtendAChain(chainBExtension, forkedChainHeader);
+            Assert.Equal(12, tipOfFork.Height);
+
+            // Headers are presented for h3 -> h12.
+            listOfExtendedChainBlockHeaders = testContext.ChainedHeaderToList(tipOfFork, tipOfFork.Height - heightOfFork);
+            connectNewHeadersResult = chainedHeaderTree.ConnectNewHeaders(peerTwoId, listOfExtendedChainBlockHeaders);
+            Assert.True(connectNewHeadersResult.HaveBlockDataAvailabilityStateOf(BlockDataAvailabilityState.BlockRequired));
+            Assert.Equal(connectNewHeadersResult.DownloadFrom.Header, tipOfFork.GetAncestor(heightOfFork + 1).Header);
+            Assert.Equal(connectNewHeadersResult.DownloadTo.Header, tipOfFork.Header);
+
+            foreach (ChainedHeader chainedHeader in connectNewHeadersResult.ToArray())
+            {
+                chainedHeaderTree.BlockDataDownloaded(chainedHeader, tipOfFork.FindAncestorOrSelf(chainedHeader).Block);
+                chainedHeaderTree.PartialValidationSucceeded(chainedHeader, out bool fullValidationRequired);
+
+                if (chainedHeader.Height <= 8)  // CT advances to 8.
+                {
+                    chainedHeaderTree.ConsensusTipChanged(chainedHeader);
+                }
+            }
+
+            // UnconsumedBlocksDataBytes is equal to the sum of serialized sizes of 9b-12b + 6a-10a.
+            int serializedSizeOfChainA = 0;
+            int serializedSizeOfChainB = 0;
+
+            ChainedHeader chainedHeaderChainA = extendedChainTip;
+            while (chainedHeaderChainA.Height >= 6)
+            {
+                serializedSizeOfChainA += chainedHeaderChainA.Block.GetSerializedSize();
+                chainedHeaderChainA = chainedHeaderChainA.Previous;
+            }
+
+            ChainedHeader chainedHeaderChainB = tipOfFork;
+            while (chainedHeaderChainB.Height >= 9)
+            {
+                serializedSizeOfChainB += chainedHeaderChainB.Block.GetSerializedSize();
+                chainedHeaderChainB = chainedHeaderChainB.Previous;
+            }
+
+            int serializedSizeOfChainsAandB = serializedSizeOfChainA + serializedSizeOfChainB;
+            Assert.Equal(chainedHeaderTree.UnconsumedBlocksDataBytes, serializedSizeOfChainsAandB);
         }

         /// <summary>
@@ -2132,96 +2312,6 @@
         }

         /// <summary>
-        /// Issue 41 @ BlockDataDownloaded called on 10 known blocks.
-        /// Make sure that UnconsumedBlocksDataBytes is equal to the sum of serialized sizes of those blocks.
-        /// </summary>
-        [Fact]
-        public void BlockDataDownloadedIsCalled_UnconsumedBlocksDataBytes_Equals_SumOfSerializedBlockSize()
-        {
-            const int initialChainSize = 5;
-            const int chainExtension = 10;
-
-            // Chain header tree setup.
-            TestContext testContext = new TestContextBuilder().WithInitialChain(initialChainSize).UseCheckpoints(false).Build();
-            ChainedHeaderTree chainedHeaderTree = testContext.ChainedHeaderTree;
-            ChainedHeader initialChainTip = testContext.InitialChainTip;
-
-            ChainedHeader extendedChainTip = testContext.ExtendAChain(chainExtension, initialChainTip);
-            List<BlockHeader> listOfChainBlockHeaders = testContext.ChainedHeaderToList(extendedChainTip, initialChainSize + chainExtension);
-
-            // Present all chain headers h1->h15.
-            ConnectNewHeadersResult connectNewHeadersResult = chainedHeaderTree.ConnectNewHeaders(1, listOfChainBlockHeaders);
-            Assert.True(connectNewHeadersResult.HaveBlockDataAvailabilityStateOf(BlockDataAvailabilityState.BlockRequired));
-
-            int serializedSizeOfBlocks = 0;
-            foreach (ChainedHeader chainedHeader in connectNewHeadersResult.ToArray())
-            {
-                chainedHeaderTree.BlockDataDownloaded(chainedHeader, extendedChainTip.FindAncestorOrSelf(chainedHeader).Block);
-                serializedSizeOfBlocks += chainedHeader.Block.GetSerializedSize();
-            }
-
-            // UnconsumedBlocksDataBytes is equal to the sum of serialized sizes of the blocks.
-            Assert.Equal(chainedHeaderTree.UnconsumedBlocksDataBytes, serializedSizeOfBlocks);
-        }
-
-        /// <summary>
-        /// Issue 42 @ CT is at 0. 10 headers are presented.
-        /// 10 blocks are downloaded. CT advances to 5.
-        /// Make sure that UnconsumedBlocksDataBytes is equal to
-        /// the sum of serialized sizes of last five blocks.
-        /// CT advances to 10.  Make sure UnconsumedBlocksDataBytes is 0.
-        /// </summary>
-        [Fact]
-        public void PresentHeaders_ChainHeaderTreeAdvances_UnconsumedBlocksDataBytes_Equals_SumOfSerializedBlockSize()
-        {
-            const int initialChainSize = 0;
-            const int chainExtensionSize = 10;
-
-            // Chain header tree setup.
-            TestContext ctx = new TestContextBuilder().WithInitialChain(initialChainSize).UseCheckpoints(false).Build();
-            ChainedHeaderTree chainedHeaderTree = ctx.ChainedHeaderTree;
-            ChainedHeader initialChainTip = ctx.InitialChainTip;
-
-            // 10 headers are presented.
-            ChainedHeader commonChainTip = ctx.ExtendAChain(chainExtensionSize, initialChainTip);
-            List<BlockHeader> listOfChainBlockHeaders = ctx.ChainedHeaderToList(commonChainTip, chainExtensionSize);
-
-            // 10 blocks are downloaded.
-            ConnectNewHeadersResult connectNewHeadersResult = chainedHeaderTree.ConnectNewHeaders(1, listOfChainBlockHeaders);
-            ChainedHeader chainedHeaderTo = connectNewHeadersResult.DownloadTo;
-            chainedHeaderTo.HashBlock.Should().Be(commonChainTip.HashBlock);
-
-            Assert.True(connectNewHeadersResult.HaveBlockDataAvailabilityStateOf(BlockDataAvailabilityState.BlockRequired));
-
-            foreach (ChainedHeader chainedHeader in connectNewHeadersResult.ToArray())
-            {
-                chainedHeaderTree.BlockDataDownloaded(chainedHeader, commonChainTip.FindAncestorOrSelf(chainedHeader).Block);
-                chainedHeaderTree.PartialValidationSucceeded(chainedHeader, out bool fullValidationRequired);
-                fullValidationRequired.Should().BeTrue();
-            }
-
-            // CT advances to 5.
-            chainedHeaderTree.ConsensusTipChanged(chainedHeaderTo.GetAncestor(5));
-
-            int serializedSizeOfBlocks = 0;
-            IEnumerable<ChainedHeader> lastFive = connectNewHeadersResult.ToArray().TakeLast(5);
-            foreach (ChainedHeader chainedHeader in lastFive)
-            {
-                serializedSizeOfBlocks += chainedHeader.Block.GetSerializedSize();
-            }
-
-            // UnconsumedBlocksDataBytes is non-zero and equal to the sum of serialized sizes of last five blocks.
-            Assert.NotEqual(0, chainedHeaderTree.UnconsumedBlocksDataBytes);
-            Assert.Equal(serializedSizeOfBlocks, chainedHeaderTree.UnconsumedBlocksDataBytes);
-
-            // CT advances to 10.
-            chainedHeaderTree.ConsensusTipChanged(chainedHeaderTo);
-
-            // UnconsumedBlocksDataBytes is 0.
-            Assert.Equal(0, chainedHeaderTree.UnconsumedBlocksDataBytes);
-        }
-
-        /// <summary>
         /// Issue 45 @ Node has a reorg of 5 blocks.
         /// Make sure that headers for the blocks that were reorged away
         /// have no block pointers and block data availability == headers only.
@@ -2276,6 +2366,53 @@
         }

         /// <summary>
+        /// Issue 46 @ CT is at 5. Checkpoint is at 10.
+        /// ConnectNewHeaders called with 9 new headers (from peer1).
+        /// After that ConnectNewHeaders called with headers 5 to 15 (from peer2).
+        /// Make sure 6 to 15 are marked for download.
+        /// </summary>
+        [Fact]
+        public void PresentHeaders_PresentHeadersFromAlternatePeer_MarkedForDownload()
+        {
+            const int initialChainSizeOfFive = 5;
+            const int chainExtensionSizeOfFive = 5;
+            const int peerOneId = 1;
+            const int peerTwoId = 2;
+
+            // Chain header tree setup.
+            TestContext testContext = new TestContextBuilder().WithInitialChain(initialChainSizeOfFive).UseCheckpoints(true).Build();
+            ChainedHeaderTree chainedHeaderTree = testContext.ChainedHeaderTree;
+            ChainedHeader initialChainTip = testContext.InitialChainTip;
+
+            // Chain tip is at h5.
+            Assert.Equal(initialChainSizeOfFive, initialChainTip.Height);
+
+            // 5 more headers are presented.
+            ChainedHeader extendedChainTip = testContext.ExtendAChain(chainExtensionSizeOfFive, initialChainTip);
+            List<BlockHeader> listOfChainBlockHeaders = testContext.ChainedHeaderToList(extendedChainTip, initialChainSizeOfFive + chainExtensionSizeOfFive);
+
+            // Checkpoint is at h10.
+            const int checkpointHeight = 10;
+            var checkpoint = new CheckpointFixture(checkpointHeight, listOfChainBlockHeaders[checkpointHeight - 1]);
+            testContext.SetupCheckpoints(checkpoint);
+
+            // Peer 1 presents nine headers up to checkpoint: h1 -> h9.
+            ConnectNewHeadersResult connectNewHeadersResult = chainedHeaderTree.ConnectNewHeaders(peerOneId, listOfChainBlockHeaders.Take(9).ToList());
+            connectNewHeadersResult.DownloadFrom.Should().Be(null);
+            connectNewHeadersResult.DownloadTo.Should().Be(null);
+
+            // Peer 2 presents ten headers including checkpoint: h5 -> h15.
+            extendedChainTip = testContext.ExtendAChain(chainExtensionSizeOfFive, extendedChainTip); // tip h15
+            listOfChainBlockHeaders = testContext.ChainedHeaderToList(extendedChainTip, initialChainSizeOfFive + chainExtensionSizeOfFive * 2);
+            connectNewHeadersResult = chainedHeaderTree.ConnectNewHeaders(peerTwoId, listOfChainBlockHeaders.GetRange(initialChainSizeOfFive - 1, 11).ToList());
+
+            // Headers h6 -> 15 should be marked for download.
+            connectNewHeadersResult.DownloadFrom.HashBlock.Should().Be(extendedChainTip.GetAncestor(initialChainSizeOfFive + 1).HashBlock); // h6
+            connectNewHeadersResult.DownloadTo.HashBlock.Should().Be(extendedChainTip.HashBlock); // h15
+            connectNewHeadersResult.HaveBlockDataAvailabilityStateOf(BlockDataAvailabilityState.BlockRequired).Should().BeTrue();
+        }
+
+        /// <summary>
         /// Issue 47 @ CT is at 5. Checkpoints are at 10 and 20.
         /// ConnectNewHeaders called with 9 new headers (from peer1).
         /// After that ConnectNewHeaders called with headers 5 to 15 (from peer2).
@@ -2329,53 +2466,6 @@
                 Assert.Equal(BlockDataAvailabilityState.HeaderOnly, chainedHeader.BlockDataAvailability);
                 chainedHeader = chainedHeader.Previous;
             }
-        }
-
-        /// <summary>
-        /// Issue 46 @ CT is at 5. Checkpoint is at 10.
-        /// ConnectNewHeaders called with 9 new headers (from peer1).
-        /// After that ConnectNewHeaders called with headers 5 to 15 (from peer2).
-        /// Make sure 6 to 15 are marked for download.
-        /// </summary>
-        [Fact]
-        public void PresentHeaders_PresentHeadersFromAlternatePeer_MarkedForDownload()
-        {
-            const int initialChainSizeOfFive = 5;
-            const int chainExtensionSizeOfFive = 5;
-            const int peerOneId = 1;
-            const int peerTwoId = 2;
-
-            // Chain header tree setup.
-            TestContext testContext = new TestContextBuilder().WithInitialChain(initialChainSizeOfFive).UseCheckpoints(true).Build();
-            ChainedHeaderTree chainedHeaderTree = testContext.ChainedHeaderTree;
-            ChainedHeader initialChainTip = testContext.InitialChainTip;
-
-            // Chain tip is at h5.
-            Assert.Equal(initialChainSizeOfFive, initialChainTip.Height);
-
-            // 5 more headers are presented.
-            ChainedHeader extendedChainTip = testContext.ExtendAChain(chainExtensionSizeOfFive, initialChainTip);
-            List<BlockHeader> listOfChainBlockHeaders = testContext.ChainedHeaderToList(extendedChainTip, initialChainSizeOfFive + chainExtensionSizeOfFive);
-
-            // Checkpoint is at h10.
-            const int checkpointHeight = 10;
-            var checkpoint = new CheckpointFixture(checkpointHeight, listOfChainBlockHeaders[checkpointHeight - 1]);
-            testContext.SetupCheckpoints(checkpoint);
-
-            // Peer 1 presents nine headers up to checkpoint: h1 -> h9.
-            ConnectNewHeadersResult connectNewHeadersResult = chainedHeaderTree.ConnectNewHeaders(peerOneId, listOfChainBlockHeaders.Take(9).ToList());
-            connectNewHeadersResult.DownloadFrom.Should().Be(null);
-            connectNewHeadersResult.DownloadTo.Should().Be(null);
-
-            // Peer 2 presents ten headers including checkpoint: h5 -> h15.
-            extendedChainTip = testContext.ExtendAChain(chainExtensionSizeOfFive, extendedChainTip); // tip h15
-            listOfChainBlockHeaders = testContext.ChainedHeaderToList(extendedChainTip, initialChainSizeOfFive + chainExtensionSizeOfFive * 2);
-            connectNewHeadersResult = chainedHeaderTree.ConnectNewHeaders(peerTwoId, listOfChainBlockHeaders.GetRange(initialChainSizeOfFive - 1, 11).ToList());
-
-            // Headers h6 -> 15 should be marked for download.
-            connectNewHeadersResult.DownloadFrom.HashBlock.Should().Be(extendedChainTip.GetAncestor(initialChainSizeOfFive + 1).HashBlock); // h6
-            connectNewHeadersResult.DownloadTo.HashBlock.Should().Be(extendedChainTip.HashBlock); // h15
-            connectNewHeadersResult.HaveBlockDataAvailabilityStateOf(BlockDataAvailabilityState.BlockRequired).Should().BeTrue();
         }

         /// <summary>
@@ -2647,151 +2737,6 @@
         }

         /// <summary>
-        /// Issue 43 @ CT is at 0. 10 headers are presented. 10 blocks are downloaded.
-        /// CT advances to 5. Alternative chain with fork at 3 and tip at 12 is presented.
-        /// Block data for alternative chain is downloaded. CT changes to block 8 of the 2nd chain.
-        /// Make sure that UnconsumedBlocksDataBytes is equal to the sum of
-        /// serialized sizes of 9b-12b + 6a-10a (a- first chain, b- second chain).
-        /// </summary>
-        [Fact]
-        public void PresentHeaders_BlocksDownloaded_ForkPresented_BlockDataForAlternativeChainDownloaded_ChainTipChanges()
-        {
-            const int initialChainSize = 0;
-            const int chainExtensionSize = 10;
-            const int peerOneId = 1;
-            const int peerTwoId = 2;
-
-            // Chain header tree setup.
-            TestContext testContext = new TestContextBuilder().WithInitialChain(initialChainSize).UseCheckpoints(false).Build();
-            ChainedHeaderTree chainedHeaderTree = testContext.ChainedHeaderTree;
-            ChainedHeader initialChainTip = testContext.InitialChainTip;
-
-            // Chain tip is at 0.
-            Assert.Equal(0, initialChainTip.Height);
-
-            // Headers are presented for h1 -> h10.
-            ChainedHeader extendedChainTip = testContext.ExtendAChain(chainExtensionSize, initialChainTip);
-            List<BlockHeader> listOfExtendedChainBlockHeaders = testContext.ChainedHeaderToList(extendedChainTip, chainExtensionSize);
-            ConnectNewHeadersResult connectNewHeadersResult = chainedHeaderTree.ConnectNewHeaders(peerOneId, listOfExtendedChainBlockHeaders);
-            Assert.Equal(connectNewHeadersResult.DownloadFrom.Header, extendedChainTip.GetAncestor(initialChainSize + 1).Header); // h1
-            Assert.Equal(connectNewHeadersResult.DownloadTo.Header, extendedChainTip.Header); // h10
-            Assert.True(connectNewHeadersResult.HaveBlockDataAvailabilityStateOf(BlockDataAvailabilityState.BlockRequired));
-
-            // 10 blocks are downloaded.
-            foreach (ChainedHeader chainedHeader in connectNewHeadersResult.ToArray())
-            {
-                chainedHeaderTree.BlockDataDownloaded(chainedHeader, extendedChainTip.FindAncestorOrSelf(chainedHeader).Block);
-                chainedHeaderTree.PartialValidationSucceeded(chainedHeader, out bool fullValidationRequired);
-
-                if (chainedHeader.Height <= 5) // CT advances to 5.
-                {
-                    chainedHeaderTree.ConsensusTipChanged(chainedHeader);
-                }
-            }
-
-            // Alternative chain with fork at h3 and tip at h12 is presented.
-            const int heightOfFork = 3;
-            const int chainBExtension = 9;
-            ChainedHeader forkedChainHeader = connectNewHeadersResult.DownloadTo.GetAncestor(heightOfFork);
-            ChainedHeader tipOfFork = testContext.ExtendAChain(chainBExtension, forkedChainHeader);
-            Assert.Equal(12, tipOfFork.Height);
-
-            // Headers are presented for h3 -> h12.
-            listOfExtendedChainBlockHeaders = testContext.ChainedHeaderToList(tipOfFork, tipOfFork.Height - heightOfFork);
-            connectNewHeadersResult = chainedHeaderTree.ConnectNewHeaders(peerTwoId, listOfExtendedChainBlockHeaders);
-            Assert.True(connectNewHeadersResult.HaveBlockDataAvailabilityStateOf(BlockDataAvailabilityState.BlockRequired));
-            Assert.Equal(connectNewHeadersResult.DownloadFrom.Header, tipOfFork.GetAncestor(heightOfFork + 1).Header);
-            Assert.Equal(connectNewHeadersResult.DownloadTo.Header, tipOfFork.Header);
-
-            foreach (ChainedHeader chainedHeader in connectNewHeadersResult.ToArray())
-            {
-                chainedHeaderTree.BlockDataDownloaded(chainedHeader, tipOfFork.FindAncestorOrSelf(chainedHeader).Block);
-                chainedHeaderTree.PartialValidationSucceeded(chainedHeader, out bool fullValidationRequired);
-
-                if (chainedHeader.Height <= 8)  // CT advances to 8.
-                {
-                    chainedHeaderTree.ConsensusTipChanged(chainedHeader);
-                }
-            }
-
-            // UnconsumedBlocksDataBytes is equal to the sum of serialized sizes of 9b-12b + 6a-10a.
-            int serializedSizeOfChainA = 0;
-            int serializedSizeOfChainB = 0;
-
-            ChainedHeader chainedHeaderChainA = extendedChainTip;
-            while (chainedHeaderChainA.Height >= 6)
-            {
-                serializedSizeOfChainA += chainedHeaderChainA.Block.GetSerializedSize();
-                chainedHeaderChainA = chainedHeaderChainA.Previous;
-            }
-
-            ChainedHeader chainedHeaderChainB = tipOfFork;
-            while (chainedHeaderChainB.Height >= 9)
-            {
-                serializedSizeOfChainB += chainedHeaderChainB.Block.GetSerializedSize();
-                chainedHeaderChainB = chainedHeaderChainB.Previous;
-            }
-
-            int serializedSizeOfChainsAandB = serializedSizeOfChainA + serializedSizeOfChainB;
-            Assert.Equal(chainedHeaderTree.UnconsumedBlocksDataBytes, serializedSizeOfChainsAandB);
-        }
-
-        /// <summary>
-        /// Issue 55 @ CT is at 20a. AssumeValid is on 12b.
-        /// Peer 1 presents a chain with tip at 15b and fork point at 10a.
-        /// 11b,12b should be marked as AV, 13b,14b,15b should be header only.
-        /// No headers from chain b are requested for download.
-        /// </summary>
-        [Fact]
-        public void PresentHeaders_AssumeValidBelowAVBlock_HeadersOnlyAboveAVBlock_NoHeadersRequestedForDownloaded()
-        {
-            const int initialChainSizeOfTwenty = 20;
-            const int chainExtensionSizeOfFive = 5;
-            const int assumeValidBlockHeightOfTwelve = 12;
-
-            const int forkHeight = 10;
-            const int peerOneId = 1;
-
-            // Chain header tree setup.
-            TestContext testContext = new TestContextBuilder().WithInitialChain(initialChainSizeOfTwenty).UseCheckpoints(false).Build();
-            ChainedHeaderTree chainedHeaderTree = testContext.ChainedHeaderTree;
-            ChainedHeader initialChainTip = testContext.InitialChainTip;
-
-            // Chain tip is at 20.
-            Assert.Equal(initialChainSizeOfTwenty, initialChainTip.Height);
-
-            ChainedHeader tipOfChainBFork = testContext.ExtendAChain(chainExtensionSizeOfFive, initialChainTip.GetAncestor(forkHeight));
-
-            // AssumeValid is on 12b.
-            testContext.ConsensusSettings.BlockAssumedValid = tipOfChainBFork.GetAncestor(assumeValidBlockHeightOfTwelve).HashBlock;
-
-            // Peer 1 presents a chain with tip at 15b and fork point at 10a.
-            List<BlockHeader> listOfExtendedChainBlockHeaders = testContext.ChainedHeaderToList(tipOfChainBFork, tipOfChainBFork.Height - forkHeight + 1 /* fork inclusive */);
-            ConnectNewHeadersResult connectNewHeadersResult = chainedHeaderTree.ConnectNewHeaders(peerOneId, listOfExtendedChainBlockHeaders);
-
-            // 11b, 12b should be marked as AV.
-            connectNewHeadersResult.Consumed.GetAncestor(11).BlockValidationState.Should().Be(ValidationState.AssumedValid);
-            connectNewHeadersResult.Consumed.GetAncestor(12).BlockValidationState.Should().Be(ValidationState.AssumedValid);
-
-            // 13b, 14b, 15b should be header only.
-            connectNewHeadersResult.Consumed.GetAncestor(13).BlockValidationState.Should().Be(ValidationState.HeaderValidated);
-            connectNewHeadersResult.Consumed.GetAncestor(14).BlockValidationState.Should().Be(ValidationState.HeaderValidated);
-            connectNewHeadersResult.Consumed.GetAncestor(15).BlockValidationState.Should().Be(ValidationState.HeaderValidated);
-
-            // No headers from chain b are requested for download.
-            connectNewHeadersResult.DownloadFrom.Should().Be(null);
-            connectNewHeadersResult.DownloadTo.Should().Be(null);
-
-            // Block availability state for 11b – 15b is header only.
-            ChainedHeader chainedHeader = connectNewHeadersResult.Consumed;
-            while (chainedHeader.Height > connectNewHeadersResult.Consumed.GetAncestor(11).Height)
-            {
-                chainedHeader.BlockDataAvailability.Should().Be(BlockDataAvailabilityState.HeaderOnly);
-                chainedHeader = chainedHeader.Previous;
-            }
-        }
-
-        /// <summary>
         /// Issue 54 @ CT is at 50a.
         /// Finalized height is 40, max reorg is 10.
         /// Some headers are presented (from 20a to 60b, with fork point 40a) by peer 1.
@@ -2842,5 +2787,60 @@
             List<BlockHeader> listOfChainCBlockHeaders = testContext.ChainedHeaderToList(tipOfForkOfChainC, 31); // 25 -> 55
             Assert.Throws<MaxReorgViolationException>(() => chainedHeaderTree.ConnectNewHeaders(peerTwoId, listOfChainCBlockHeaders));
         }
+
+        /// <summary>
+        /// Issue 55 @ CT is at 20a. AssumeValid is on 12b.
+        /// Peer 1 presents a chain with tip at 15b and fork point at 10a.
+        /// 11b,12b should be marked as AV, 13b,14b,15b should be header only.
+        /// No headers from chain b are requested for download.
+        /// </summary>
+        [Fact]
+        public void PresentHeaders_AssumeValidBelowAVBlock_HeadersOnlyAboveAVBlock_NoHeadersRequestedForDownloaded()
+        {
+            const int initialChainSizeOfTwenty = 20;
+            const int chainExtensionSizeOfFive = 5;
+            const int assumeValidBlockHeightOfTwelve = 12;
+
+            const int forkHeight = 10;
+            const int peerOneId = 1;
+
+            // Chain header tree setup.
+            TestContext testContext = new TestContextBuilder().WithInitialChain(initialChainSizeOfTwenty).UseCheckpoints(false).Build();
+            ChainedHeaderTree chainedHeaderTree = testContext.ChainedHeaderTree;
+            ChainedHeader initialChainTip = testContext.InitialChainTip;
+
+            // Chain tip is at 20.
+            Assert.Equal(initialChainSizeOfTwenty, initialChainTip.Height);
+
+            ChainedHeader tipOfChainBFork = testContext.ExtendAChain(chainExtensionSizeOfFive, initialChainTip.GetAncestor(forkHeight));
+
+            // AssumeValid is on 12b.
+            testContext.ConsensusSettings.BlockAssumedValid = tipOfChainBFork.GetAncestor(assumeValidBlockHeightOfTwelve).HashBlock;
+
+            // Peer 1 presents a chain with tip at 15b and fork point at 10a.
+            List<BlockHeader> listOfExtendedChainBlockHeaders = testContext.ChainedHeaderToList(tipOfChainBFork, tipOfChainBFork.Height - forkHeight + 1 /* fork inclusive */);
+            ConnectNewHeadersResult connectNewHeadersResult = chainedHeaderTree.ConnectNewHeaders(peerOneId, listOfExtendedChainBlockHeaders);
+
+            // 11b, 12b should be marked as AV.
+            connectNewHeadersResult.Consumed.GetAncestor(11).BlockValidationState.Should().Be(ValidationState.AssumedValid);
+            connectNewHeadersResult.Consumed.GetAncestor(12).BlockValidationState.Should().Be(ValidationState.AssumedValid);
+
+            // 13b, 14b, 15b should be header only.
+            connectNewHeadersResult.Consumed.GetAncestor(13).BlockValidationState.Should().Be(ValidationState.HeaderValidated);
+            connectNewHeadersResult.Consumed.GetAncestor(14).BlockValidationState.Should().Be(ValidationState.HeaderValidated);
+            connectNewHeadersResult.Consumed.GetAncestor(15).BlockValidationState.Should().Be(ValidationState.HeaderValidated);
+
+            // No headers from chain b are requested for download.
+            connectNewHeadersResult.DownloadFrom.Should().Be(null);
+            connectNewHeadersResult.DownloadTo.Should().Be(null);
+
+            // Block availability state for 11b – 15b is header only.
+            ChainedHeader chainedHeader = connectNewHeadersResult.Consumed;
+            while (chainedHeader.Height > connectNewHeadersResult.Consumed.GetAncestor(11).Height)
+            {
+                chainedHeader.BlockDataAvailability.Should().Be(BlockDataAvailabilityState.HeaderOnly);
+                chainedHeader = chainedHeader.Previous;
+            }
+        }
     }
 }


Handling an exception when there is no High Score.

--- Core/Core.cs
+++ Core/Core.cs
@@ -102,23 +102,33 @@
                 case 1:
                     Console.Clear();
                     Console.WriteLine("You selected High Scores!");
-
-                    string fileName = @"..\..\..\test.txt";
-                    StreamReader reader = new StreamReader(fileName);
-                    //using (StreamReader sr = new StreamReader(fileName))
-                    using(reader)
-                    {
-                        string line;
-                        // Read and display lines from the file until the end of
-                        // the file is reached.
-                        while ((line = reader.ReadLine()) != null)
+
+
+                    try
+                    {
+                        string fileName = @"..\..\..\test.txt";
+                        StreamReader reader = new StreamReader(fileName);
+                        //using (StreamReader sr = new StreamReader(fileName))
+                        using (reader)
                         {
-                            Console.WriteLine(line);
+                            string line;
+                            // Read and display lines from the file until the end of
+                            // the file is reached.
+                            while ((line = reader.ReadLine()) != null)
+                            {
+                                Console.WriteLine(line);
+                            }
+                            reader.Close();
                         }
-                        reader.Close();
-                    }
-
-                    System.Threading.Thread.Sleep(99999);
+
+                        System.Threading.Thread.Sleep(99999);
+                        break;
+                    }
+                    catch(FileNotFoundException)
+                    {
+                        Console.WriteLine("There are no High Scores recorded!");
+                        System.Threading.Thread.Sleep(99999);
+                    }
                     break;

                 case 2:
@@ -346,7 +356,7 @@
             Console.Write("Enter your name: ");
             string name = Console.ReadLine();

-            StreamWriter writer = new StreamWriter(@"..\..\..\test.txt", true);
+            StreamWriter writer = new StreamWriter(@"..\..\..\test.txt",true);
             //using (writer)
             //{
                 writer.WriteLine("{0}:{1}", score, name);


Latch on null for disposed CancellationSeries

--- src/Workspaces/Core/Portable/Utilities/CancellationSeries.cs
+++ src/Workspaces/Core/Portable/Utilities/CancellationSeries.cs
@@ -80,7 +80,23 @@
             // This way we would return a cancelled token, which is reasonable.
             var nextToken = nextSource.Token;

-            var priorSource = Interlocked.Exchange(ref _cts, nextSource);
+            // The following block is identical to Interlocked.Exchange, except no replacement is made if the current
+            // field value is null (latch on null). This ensures state is not corrupted if CreateNext is called after
+            // the object is disposed.
+            var priorSource = Volatile.Read(ref _cts);
+            while (priorSource is not null)
+            {
+                var candidate = Interlocked.CompareExchange(ref _cts, nextSource, priorSource);
+
+                if (candidate == priorSource)
+                {
+                    break;
+                }
+                else
+                {
+                    priorSource = candidate;
+                }
+            }

             if (priorSource == null)
             {


more levels

more levels

--- Cube/Shapes/Page.cs
+++ Cube/Shapes/Page.cs
@@ -37,7 +37,7 @@
         [XmlIgnore]
         public NormalShape Shape;

-        public int[] LevelCounts = new int[13];
+        public int[] LevelCounts = new int[14];

         #endregion


Fixed signature formatting. Fixes #344.

Fixed signature formatting. Fixes #344.

git-svn-id: eda3d06fcef731589ace1b284159cead3416df9b@484 af82e41b-90c4-0310-8c96-b1721e28e2e2

--- templates/ticket_notify_email.cs
+++ templates/ticket_notify_email.cs
@@ -16,6 +16,6 @@
 <?cs var:$ticket.change.comment.newvalue ?>
 <?cs /if ?><?cs
 /if ?>
---
+--
 Ticket URL: <<?cs var:$ticket.link ?>>
 <?cs var:$project.name ?> <<?cs var:$trac.href.homepage ?>>


Update cake script to use VSWhere

Update cake script to use VSWhere

--- build.cake
+++ build.cake
@@ -11,6 +11,7 @@
 #tool GitVersion.CommandLine
 #tool GitLink
 #tool xunit.runner.console&version=2.3.1
+#tool vswhere

 //////////////////////////////////////////////////////////////////////
 // ARGUMENTS
@@ -59,19 +60,41 @@
 // TASKS
 //////////////////////////////////////////////////////////////////////

+FilePath msBuildPath;
+Task("ResolveBuildTools")
+    .WithCriteria(() => isRunningOnWindows)
+    .Does(() =>
+{
+    var vsWhereSettings = new VSWhereLatestSettings
+    {
+        IncludePrerelease = true
+    };
+
+    var vsLatest = VSWhereLatest();
+    msBuildPath = (vsLatest == null)
+        ? null
+        : vsLatest.CombineWithFilePath("./MSBuild/15.0/Bin/MSBuild.exe");
+});
+
 Task("Build")
+    .IsDependentOn("ResolveBuildtools")
     .IsDependentOn("RestorePackages")
     .IsDependentOn("UpdateAssemblyInfo")
     .Does (() =>
 {
     Information("Building {0}", solutionFile);

-    MSBuild(solutionFile, new MSBuildSettings()
+    var settings = new MSBuildSettings()
         .SetConfiguration(configuration)
         .WithProperty("NoWarn", "1591") // ignore missing XML doc warnings
         .WithProperty("TreatWarningsAsErrors", treatWarningsAsErrors.ToString())
         .SetVerbosity(Verbosity.Minimal)
-        .SetNodeReuse(false));
+        .SetNodeReuse(false);
+
+    if (msBuildPath != null)
+        settings.ToolPath = msBuildPath;
+
+    MSBuild(solutionFile, settings);
 });

 Task("UpdateAppVeyorBuildNumber")
@@ -118,14 +141,19 @@
 {
     // switched to msbuild-based nuget creation
     // see here for parameters: https://docs.microsoft.com/en-us/nuget/schema/msbuild-targets
-    MSBuild ("./src/Cake.AndroidAppManifest/Cake.AndroidAppManifest.csproj", c => {
-        c.Configuration = configuration;
-        c.Targets.Add ("pack");
-        c.Properties.Add("IncludeSymbols", new List<string> { "true" });
-        c.Properties.Add("PackageReleaseNotes", new List<string>(releaseNotes.Notes));
-        c.Properties.Add("PackageVersion", new List<string> { version });
-        c.Properties.Add("PackageOutputPath", new List<string> { artifactDirectory });
-    });
+    var settings = new MSBuildSettings()
+        .SetConfiguration(configuration)
+        .WithTarget("pack")
+        .WithProperty("IncludeSymbols", "True")
+        .WithProperty("PackageVersion", version)
+        .WithProperty("PackageOutputPath", artifactDirectory);
+
+        settings.Properties.Add("PackageReleaseNotes", new List<string>(releaseNotes.Notes));
+
+    if (msBuildPath != null)
+        settings.ToolPath = msBuildPath;
+
+    MSBuild ("./src/Cake.AndroidAppManifest/Cake.AndroidAppManifest.csproj", settings);
 });

 //////////////////////////////////////////////////////////////////////


Added docs tasks

Added docs tasks

--- build.cake
+++ build.cake
@@ -4,12 +4,14 @@

 #addin "nuget:?package=Polly&version=4.2.0"
 #addin "nuget:?package=NuGet.Core&version=2.12.0"
+#addin "nuget:?package=Cake.DocFx&version=0.1.6"

 ///////////////////////////////////////////////////////////////////////////////
 // TOOLS
 ///////////////////////////////////////////////////////////////////////////////

 #tool "nuget:?package=xunit.runner.console&version=2.1.0"
+#tool "nuget:?package=docfx.msbuild&version=2.4.0"

 ///////////////////////////////////////////////////////////////////////////////
 // USINGS
@@ -41,6 +43,7 @@
 var ReleaseConfiguration = "Release";
 var MSBuildSolution = "./Core2D.sln";
 var XBuildSolution = "./Core2D.mono.sln";
+var DocFxProject = "./docs/docfx.json";

 ///////////////////////////////////////////////////////////////////////////////
 // PARAMETERS
@@ -92,10 +95,14 @@
 var nugetRoot = artifactsDir.Combine("nuget");
 var chocolateyRoot = artifactsDir.Combine("chocolatey");
 var zipRoot = artifactsDir.Combine("zip");
+var docsRoot = artifactsDir.Combine("docs");
+var docsSiteRoot = docsRoot.Combine("_site");
 var binRoot = artifactsDir.Combine("bin");

 var dirSuffix = platform + "/" + configuration;
 var dirSuffixSkia = (isPlatformAnyCPU ? "x86" : platform) + "/" + configuration;
+
+var zipDocsSiteArtifacts = zipRoot.CombineWithFilePath("Core2D-Docs-" + version + ".zip");

 Func<IFileSystemInfo, bool> ExcludeSkia = i => {
     return !(i.Path.FullPath.IndexOf("Skia", StringComparison.OrdinalIgnoreCase) >= 0);
@@ -904,10 +911,12 @@
     CleanDirectories(buildDirs);
     CleanDirectory(artifactsDir);
     CleanDirectory(testResultsDir);
-    CleanDirectory(zipRoot);
-    CleanDirectory(binRoot);
     CleanDirectory(nugetRoot);
     CleanDirectory(chocolateyRoot);
+    CleanDirectory(zipRoot);
+    CleanDirectory(docsRoot);
+    CleanDirectory(docsSiteRoot);
+    CleanDirectory(binRoot);
 });

 Task("Restore-NuGet-Packages")
@@ -995,6 +1004,16 @@
     }
 });

+Task("Create-Docs")
+    .IsDependentOn("Run-Unit-Tests")
+    .Does(() =>
+{
+    DocFxMetadata(DocFxProject);
+    DocFxBuild(DocFxProject, new DocFxBuildSettings() {
+        OutputPath = docsRoot
+    });
+});
+
 Task("Copy-Files")
     .IsDependentOn("Run-Unit-Tests")
     .Does(() =>
@@ -1003,9 +1022,12 @@
 });

 Task("Zip-Files")
+    .IsDependentOn("Create-Docs")
     .IsDependentOn("Copy-Files")
     .Does(() =>
 {
+    Zip(docsSiteRoot, zipDocsSiteArtifacts);
+
     Zip(binRoot, zipCoreArtifacts);

     Zip(zipSourceCairoDirs,
@@ -1052,6 +1074,17 @@
         nuspec.Key.Files = GetChocolateyNuSpecContent(nuspec.Value);
         ChocolateyPack(nuspec.Key);
     }
+});
+
+Task("Publish-Docs")
+    .IsDependentOn("Create-Docs")
+    .WithCriteria(() => !isLocalBuild)
+    .WithCriteria(() => !isPullRequest)
+    .WithCriteria(() => isMainRepo)
+    .WithCriteria(() => isMasterBranch)
+    .WithCriteria(() => isNuGetRelease)
+    .Does(() =>
+{
 });

 Task("Publish-MyGet")
@@ -1170,6 +1203,7 @@

 Task("AppVeyor")
   .IsDependentOn("Zip-Files")
+  .IsDependentOn("Publish-Docs")
   .IsDependentOn("Publish-MyGet")
   .IsDependentOn("Publish-NuGet")
   .IsDependentOn("Publish-Chocolatey");


Run tests in parallel and don't upload to AppVeyor

--- build.cake
+++ build.cake
@@ -61,23 +61,13 @@
                 project.GetDirectory().FullPath,
                 new XUnit2Settings()
                 {
-                    OutputDirectory = artifactsDirectory
+                    OutputDirectory = artifactsDirectory,
+                    Parallelism = ParallelismOption.All
                 });
         }
     });

-Task("Upload-AppVeyor-Artifacts")
-    .IsDependentOn("Test")
-    .WithCriteria(() => AppVeyor.IsRunningOnAppVeyor)
-    .Does(() =>
-    {
-        foreach(var file in GetFiles(artifactsDirectory.Path + "/*"))
-        {
-            AppVeyor.UploadArtifact(file);
-        }
-    });
-
 Task("Default")
-    .IsDependentOn("Upload-AppVeyor-Artifacts");
+    .IsDependentOn("Test");

 RunTarget(target);


Build script fix

Build script fix

--- build.cake
+++ build.cake
@@ -82,7 +82,7 @@
 	.IsDependentOn ("nuget")
 	.Does (() =>
 {
-	compVersion = version;
+	var compVersion = version;
 	if (compVersion.Contains ("-"))
 		compVersion = compVersion.Substring (0, compVersion.IndexOf ("-"));


Updated SonarQube-Scanner to 4.5.0.2216

Updated SonarQube-Scanner to 4.5.0.2216

--- build.cake
+++ build.cake
@@ -72,7 +72,7 @@
     .IsDependentOn("Clean-Output")
     .Does(() =>
 {
-    var version = "4.4.0.2170";
+    var version = "4.5.0.2216";

     var packageName = "sonarqube-scanner.portable";
     var hash = GetOnlineFileHash($"https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-{version}-windows.zip");


Continuing to test Azure logger

Continuing to test Azure logger

--- build.cake
+++ build.cake
@@ -8,7 +8,7 @@
 #addin nuget:?package=Cake.Wyam&version=1.5.1
 #addin "Octokit"
 #addin "NetlifySharp"
-#tool "AzurePipelines.TestLogger&version=0.4.0"
+#tool "AzurePipelines.TestLogger&version=0.4.1"

 using Octokit;
 using NetlifySharp;


(GH-8) Removed TFX Addin

(GH-8) Removed TFX Addin

- This is not required just now


Reintroduce acceptance criteria since secrets are unavailable in pull requests anyway

--- build.cake
+++ build.cake
@@ -192,7 +192,6 @@
     .IsDependentOn("Package")
     .WithCriteria(() =>
     {
-        return true;
         var branchName = gitVersion.BranchName.Trim();
         return branchName == "master" || branchName == "develop";
     })


Only replace if we should

Only replace if we should

--- build.cake
+++ build.cake
@@ -314,7 +314,13 @@
     FileInfo[] files = dir.GetFiles();
     foreach (FileInfo file in files) {
         string temppath = dest.CombineWithFilePath(file.Name).FullPath;
-        file.CopyTo(temppath, replace);
+        if (FileExists(temppath)) {
+            if (replace) {
+                DeleteFile(temppath);
+            }
+        } else {
+            file.CopyTo(temppath);
+        }
     }

     DirectoryInfo[] dirs = dir.GetDirectories();


Set the source

Set the source

--- build.cake
+++ build.cake
@@ -200,7 +200,8 @@
         var nugetFiles = GetFiles(artifactsDir.ToString() + "/*.nupkg");
         NuGetPush(nugetFiles, new NuGetPushSettings
         {
-            ApiKey = EnvironmentVariable("NuGetOrgApiKey")
+            ApiKey = EnvironmentVariable("NuGetOrgApiKey"),
+            Source = "https://api.nuget.org/v3/index.json"
         });
     });


absent comma added to cake

absent comma added to cake

--- build.cake
+++ build.cake
@@ -37,10 +37,10 @@
                           "Stove.RavenDB.Tests",
                           "Stove.Redis.Tests",
                           "Stove.Tests",
-                          "Stove.Tests.SampleApplication"
+                          "Stove.Tests.SampleApplication",
                           "Stove.Dapper.Tests",
                           "Stove.Hangfire.Tests",
-                          "Stove.NHibernate.Tests",
+                          "Stove.NHibernate.Tests"
                         };

 var nupkgPath = "nupkg";


Typo in build file

Typo in build file

--- build.cake
+++ build.cake
@@ -221,7 +221,7 @@
 	.WithCriteria(() => parameters.GitHub.HasCredentials)
 	.Does(() =>
 	{
-		GitReleaseManagerPulblish(
+		GitReleaseManagerPublish(
 			parameters.GitHub.Username,
 			parameters.GitHub.Password,
 			"AdmiringWorm",


ensure SQLite dependencies are included in the mono archive

--- build.cake
+++ build.cake
@@ -723,8 +723,6 @@
     CopyMonoBuild(env, buildFolder, outputFolder);

     CopyExtraDependencies(env, outputFolder);
-
-    Package(project, "mono", outputFolder, env.Folders.ArtifactsPackage, env.Folders.DeploymentPackage);

      // Copy dependencies of Mono build
      FileHelper.Copy(
@@ -744,6 +742,8 @@
          destination: CombinePaths(outputFolder, "SQLitePCLRaw.batteries_green.dll"),
          overwrite: true);

+    Package(project, "mono", outputFolder, env.Folders.ArtifactsPackage, env.Folders.DeploymentPackage);
+
     return outputFolder;
 }


Improve VerifyCodeGen error message.

Improve VerifyCodeGen error message.

--- build.cake
+++ build.cake
@@ -33,6 +33,148 @@
 string version = null;
 string headSha = null;

+Task("Clean")
+	.Does(() =>
+	{
+		CleanDirectories($"src/**/bin");
+		CleanDirectories($"src/**/obj");
+		CleanDirectories($"tests/**/bin");
+		CleanDirectories($"tests/**/obj");
+		CleanDirectories("release");
+	});
+
+Task("Build")
+	.IsDependentOn("Clean")
+	.Does(() =>
+	{
+		NuGetRestore(solutionFileName);
+		MSBuild(solutionFileName, settings => settings.SetConfiguration(configuration));
+	});
+
+Task("CodeGen")
+	.IsDependentOn("Build")
+	.Does(() => CodeGen(verify: false));
+
+Task("VerifyCodeGen")
+	.IsDependentOn("Build")
+	.Does(() => CodeGen(verify: true));
+
+Task("Test")
+	.IsDependentOn("VerifyCodeGen")
+	.Does(() => NUnit3($"tests/**/bin/**/*.UnitTests.dll", new NUnit3Settings { NoResults = true }));
+
+Task("SourceIndex")
+	.IsDependentOn("Test")
+	.WithCriteria(() => configuration == "Release" && gitRepository != null)
+	.Does(() =>
+	{
+		if (sourceIndex)
+		{
+			var dirtyEntry = gitRepository.RetrieveStatus().FirstOrDefault(x => x.State != FileStatus.Unaltered && x.State != FileStatus.Ignored);
+			if (dirtyEntry != null)
+				throw new InvalidOperationException($"The git working directory must be clean, but '{dirtyEntry.FilePath}' is dirty.");
+
+			headSha = gitRepository.Head.Tip.Sha;
+			try
+			{
+				githubClient.Repository.Commit.GetSha1(githubOwner, githubRepo, headSha).GetAwaiter().GetResult();
+			}
+			catch (Octokit.NotFoundException exception)
+			{
+				throw new InvalidOperationException($"The current commit '{headSha}' must be pushed to GitHub.", exception);
+			}
+
+			GitLink(MakeAbsolute(Directory(".")).FullPath, new GitLinkSettings
+			{
+				RepositoryUrl = $"{githubRawUri}/{githubOwner}/{githubRepo}",
+				ArgumentCustomization = args => args.Append($"-ignore Bom,BomTest"),
+			});
+		}
+		else
+		{
+			Warning("Skipping source index.");
+		}
+
+		version = GetSemVerFromFile(GetFiles($"src/**/bin/**/{coverageAssemblies[0]}.dll").First().ToString());
+	});
+
+Task("NuGetPackage")
+	.IsDependentOn("SourceIndex")
+	.Does(() =>
+	{
+		CreateDirectory("release");
+
+		foreach (var nuspecPath in GetFiles($"src/**/*.nuspec"))
+		{
+			NuGetPack(nuspecPath, new NuGetPackSettings
+			{
+				Version = version,
+				OutputDirectory = "release",
+			});
+		}
+	});
+
+Task("NuGetPublish")
+	.IsDependentOn("NuGetPackage")
+	.WithCriteria(() => !string.IsNullOrEmpty(nugetApiKey) && !string.IsNullOrEmpty(githubApiKey))
+	.Does(() =>
+	{
+		foreach (var nupkgPath in GetFiles($"release/*.nupkg"))
+		{
+			NuGetPush(nupkgPath, new NuGetPushSettings
+			{
+				ApiKey = nugetApiKey,
+				Source = nugetSource,
+			});
+		}
+
+		if (headSha != null)
+		{
+			var tagName = $"nuget-{version}";
+			Information($"Creating git tag '{tagName}'...");
+			githubClient.Git.Reference.Create(githubOwner, githubRepo,
+				new Octokit.NewReference($"refs/tags/{tagName}", headSha)).GetAwaiter().GetResult();
+		}
+		else
+		{
+			Warning("Skipping git tag for prerelease.");
+		}
+	});
+
+Task("Coverage")
+	.IsDependentOn("VerifyCodeGen")
+	.Does(() =>
+	{
+		CreateDirectory("release");
+		if (FileExists("release/coverage.xml"))
+			DeleteFile("release/coverage.xml");
+
+		string filter = string.Concat(coverageAssemblies.Select(x => $@" ""-filter:+[{x}]*"""));
+
+		foreach (var testDllPath in GetFiles($"tests/**/bin/**/*.UnitTests.dll"))
+		{
+			ExecuteProcess(@"cake\OpenCover\tools\OpenCover.Console.exe",
+				$@"-register:user -mergeoutput ""-target:cake\NUnit.ConsoleRunner\tools\nunit3-console.exe"" ""-targetargs:{testDllPath} --noresult"" ""-output:release\coverage.xml"" -skipautoprops -returntargetcode" + filter);
+		}
+	});
+
+Task("CoverageReport")
+	.IsDependentOn("Coverage")
+	.Does(() =>
+	{
+		ExecuteProcess(@"cake\ReportGenerator\tools\ReportGenerator.exe", $@"""-reports:release\coverage.xml"" ""-targetdir:release\coverage""");
+	});
+
+Task("CoveragePublish")
+	.IsDependentOn("Coverage")
+	.Does(() =>
+	{
+		ExecuteProcess(@"cake\coveralls.io\tools\coveralls.net.exe", $@"--opencover ""release\coverage.xml"" --full-sources --repo-token {coverallsApiKey}");
+	});
+
+Task("Default")
+	.IsDependentOn("Test");
+
 string GetSemVerFromFile(string path)
 {
 	var versionInfo = System.Diagnostics.FileVersionInfo.GetVersionInfo(path);
@@ -44,153 +186,18 @@

 void CodeGen(bool verify)
 {
-	ExecuteProcess($@"src\fsdgencsharp\bin\{configuration}\fsdgencsharp.exe",
-		@"fsd\FacilityCore.fsd src\Facility.Core --clean --csproj" + (verify ? " --verify" : ""));
-	ExecuteProcess($@"src\fsdgencsharp\bin\{configuration}\fsdgencsharp.exe",
-		@"example\ExampleApi.fsd src\Facility.ExampleApi --clean --csproj" + (verify ? " --verify" : ""));
-}
-
-Task("Clean")
-	.Does(() =>
-	{
-		CleanDirectories($"src/**/bin");
-		CleanDirectories($"src/**/obj");
-		CleanDirectories($"tests/**/bin");
-		CleanDirectories($"tests/**/obj");
-		CleanDirectories("release");
-	});
-
-Task("Build")
-	.IsDependentOn("Clean")
-	.Does(() =>
-	{
-		NuGetRestore(solutionFileName);
-		MSBuild(solutionFileName, settings => settings.SetConfiguration(configuration));
-	});
-
-Task("CodeGen")
-	.IsDependentOn("Build")
-	.Does(() => CodeGen(verify: false));
-
-Task("VerifyCodeGen")
-	.IsDependentOn("Build")
-	.Does(() => CodeGen(verify: true));
-
-Task("Test")
-	.IsDependentOn("VerifyCodeGen")
-	.Does(() => NUnit3($"tests/**/bin/**/*.UnitTests.dll", new NUnit3Settings { NoResults = true }));
-
-Task("SourceIndex")
-	.IsDependentOn("Test")
-	.WithCriteria(() => configuration == "Release" && gitRepository != null)
-	.Does(() =>
-	{
-		if (sourceIndex)
-		{
-			var dirtyEntry = gitRepository.RetrieveStatus().FirstOrDefault(x => x.State != FileStatus.Unaltered && x.State != FileStatus.Ignored);
-			if (dirtyEntry != null)
-				throw new InvalidOperationException($"The git working directory must be clean, but '{dirtyEntry.FilePath}' is dirty.");
-
-			headSha = gitRepository.Head.Tip.Sha;
-			try
-			{
-				githubClient.Repository.Commit.GetSha1(githubOwner, githubRepo, headSha).GetAwaiter().GetResult();
-			}
-			catch (Octokit.NotFoundException exception)
-			{
-				throw new InvalidOperationException($"The current commit '{headSha}' must be pushed to GitHub.", exception);
-			}
-
-			GitLink(MakeAbsolute(Directory(".")).FullPath, new GitLinkSettings
-			{
-				RepositoryUrl = $"{githubRawUri}/{githubOwner}/{githubRepo}",
-				ArgumentCustomization = args => args.Append($"-ignore Bom,BomTest"),
-			});
-		}
-		else
-		{
-			Warning("Skipping source index.");
-		}
-
-		version = GetSemVerFromFile(GetFiles($"src/**/bin/**/{coverageAssemblies[0]}.dll").First().ToString());
-	});
-
-Task("NuGetPackage")
-	.IsDependentOn("SourceIndex")
-	.Does(() =>
-	{
-		CreateDirectory("release");
-
-		foreach (var nuspecPath in GetFiles($"src/**/*.nuspec"))
-		{
-			NuGetPack(nuspecPath, new NuGetPackSettings
-			{
-				Version = version,
-				OutputDirectory = "release",
-			});
-		}
-	});
-
-Task("NuGetPublish")
-	.IsDependentOn("NuGetPackage")
-	.WithCriteria(() => !string.IsNullOrEmpty(nugetApiKey) && !string.IsNullOrEmpty(githubApiKey))
-	.Does(() =>
-	{
-		foreach (var nupkgPath in GetFiles($"release/*.nupkg"))
-		{
-			NuGetPush(nupkgPath, new NuGetPushSettings
-			{
-				ApiKey = nugetApiKey,
-				Source = nugetSource,
-			});
-		}
-
-		if (headSha != null)
-		{
-			var tagName = $"nuget-{version}";
-			Information($"Creating git tag '{tagName}'...");
-			githubClient.Git.Reference.Create(githubOwner, githubRepo,
-				new Octokit.NewReference($"refs/tags/{tagName}", headSha)).GetAwaiter().GetResult();
-		}
-		else
-		{
-			Warning("Skipping git tag for prerelease.");
-		}
-	});
-
-Task("Coverage")
-	.IsDependentOn("VerifyCodeGen")
-	.Does(() =>
-	{
-		CreateDirectory("release");
-		if (FileExists("release/coverage.xml"))
-			DeleteFile("release/coverage.xml");
-
-		string filter = string.Concat(coverageAssemblies.Select(x => $@" ""-filter:+[{x}]*"""));
-
-		foreach (var testDllPath in GetFiles($"tests/**/bin/**/*.UnitTests.dll"))
-		{
-			ExecuteProcess(@"cake\OpenCover\tools\OpenCover.Console.exe",
-				$@"-register:user -mergeoutput ""-target:cake\NUnit.ConsoleRunner\tools\nunit3-console.exe"" ""-targetargs:{testDllPath} --noresult"" ""-output:release\coverage.xml"" -skipautoprops -returntargetcode" + filter);
-		}
-	});
-
-Task("CoverageReport")
-	.IsDependentOn("Coverage")
-	.Does(() =>
-	{
-		ExecuteProcess(@"cake\ReportGenerator\tools\ReportGenerator.exe", $@"""-reports:release\coverage.xml"" ""-targetdir:release\coverage""");
-	});
-
-Task("CoveragePublish")
-	.IsDependentOn("Coverage")
-	.Does(() =>
-	{
-		ExecuteProcess(@"cake\coveralls.io\tools\coveralls.net.exe", $@"--opencover ""release\coverage.xml"" --full-sources --repo-token {coverallsApiKey}");
-	});
-
-Task("Default")
-	.IsDependentOn("Test");
+	ExecuteCodeGen(@"fsd\FacilityCore.fsd src\Facility.Core --clean --csproj", verify);
+	ExecuteCodeGen(@"example\ExampleApi.fsd src\Facility.ExampleApi --clean --csproj", verify);
+}
+
+void ExecuteCodeGen(string args, bool verify)
+{
+	int exitCode = StartProcess($@"src\fsdgencsharp\bin\{configuration}\fsdgencsharp.exe", args + (verify ? " --verify" : ""));
+	if (exitCode == 1 && verify)
+		throw new InvalidOperationException("Generated code doesn't match; use -target=CodeGen to regenerate.");
+	else if (exitCode != 0)
+		throw new InvalidOperationException($"Code generation failed with exit code {exitCode}.");
+}

 void ExecuteProcess(string exePath, string arguments)
 {


(GH-3440) Update GitVersion.Tool to version 5.7.0 * fixes #3440

--- build.cake
+++ build.cake
@@ -10,7 +10,7 @@
 #tool "nuget:https://api.nuget.org/v3/index.json?package=NuGet.CommandLine&version=5.9.1"

 // Install .NET Core Global tools.
-#tool "dotnet:https://api.nuget.org/v3/index.json?package=GitVersion.Tool&version=5.1.2"
+#tool "dotnet:https://api.nuget.org/v3/index.json?package=GitVersion.Tool&version=5.7.0"
 #tool "dotnet:https://api.nuget.org/v3/index.json?package=SignClient&version=1.2.109"
 #tool "dotnet:https://api.nuget.org/v3/index.json?package=GitReleaseManager.Tool&version=0.11.0"


Fixed package version in cake pack script

--- build.cake
+++ build.cake
@@ -49,7 +49,6 @@
             DotNetCorePack(proj.ToString(), new DotNetCorePackSettings {
                 OutputDirectory = "out",
                 Configuration = "Release",
-                NoBuild = true,
             });
         }
     });


Another try at test results

Another try at test results

--- build.cake
+++ build.cake
@@ -1,14 +1,16 @@
 #addin "Cake.Incubator"
-#tool "xunit.runner.console"
+#tool "nuget:?package=xunit.runner.console"
+#tool "nuget:?package=ReportUnit"

 var target = Argument("target", "Default");
 var configuration = Argument("configuration", "Release");

-var binDir = "";       // Destination Binary File Directory name i.e. bin
-var projJson = "";     // Path to the project.json
+     // Path to the project.json
 var projDir = "";      //  Project Directory
 var solutionFile = "LockOnCode.VirtualMachine.sln"; // Solution file if needed
 var outputDir = Directory("Build") + Directory(configuration);  // The output directory the build artifacts saved too
+var report = outputDir;//+Directory("reports");
+var xunitReport = report;// + Directory("xunit");

 var buildSettings = new DotNetCoreBuildSettings
      {
@@ -53,20 +55,35 @@
             Logger = "trx"

 		};
-
-        var directoryToScanForTests = "./"+outputDir+"/*Tests.csproj";
+
+        var xunitSettings = new XUnit2Settings
+        {
+           Parallelism = ParallelismOption.All,
+
+            NoAppDomain = true,
+            OutputDirectory = xunitReport,
+            //XmlReport = true,
+            NUnitReport = true
+        };
+		Information(Environment.CurrentDirectory);
+        var directoryToScanForTests = "./**/*Tests.csproj";
         Information("Scanning directory for tests: " + directoryToScanForTests);

 		var testProjects = GetFiles(directoryToScanForTests);
 		foreach(var testProject in testProjects)
 		{
 			Information("Found Test Project: " + testProject);
+            //XUnit2(testProject.ToString(), xunitSettings);
+
 			DotNetCoreTest(testProject.ToString(), testSettings);
 		}
 		//XUnit2(testAssemblies);*/

 		//DotNetCoreTest(testProject);
-		});
+		}).Finally(() =>
+        {
+            //ReportUnit(xunitReport);
+        });

 Task("Package")
     .IsDependentOn("Build")
@@ -77,7 +94,7 @@
             NoBuild = true
         };

-         DotNetCorePack(projJson, packSettings);
+         //DotNetCorePack(projJson, packSettings);

     });


Update the build script to work on the release branch

--- build.cake
+++ build.cake
@@ -1,4 +1,4 @@
-#tool nuget:?package=NUnit.ConsoleRunner&version=3.4.1
+#tool nuget:?package=NUnit.ConsoleRunner&version=3.5.0

 //////////////////////////////////////////////////////////////////////
 // PROJECT-SPECIFIC
@@ -49,6 +49,8 @@

 			if (isPullRequest)
 				suffix += "-pr-" + AppVeyor.Environment.PullRequest.Number;
+			else if (AppVeyor.Environment.Repository.Branch.StartsWith("release", StringComparison.OrdinalIgnoreCase))
+				suffix += "-pre-" + buildNumber;
 			else
 				suffix += "-" + branch;


Enable coveralls in non-Release build

Enable coveralls in non-Release build

--- build.cake
+++ build.cake
@@ -426,7 +426,7 @@
 });

 Task("Update-Coverage-Report")
-    .WithCriteria(() => "Release".Equals(configuration) && !"NOTSET".Equals(coverallsApiKey))
+    .WithCriteria(() => !"NOTSET".Equals(coverallsApiKey))
     .IsDependentOn("Build-NuGet-Package")
     .Does(() =>
 {


Install Android API levels in cake build script

--- build.cake
+++ build.cake
@@ -1,5 +1,6 @@
 #tool nuget:?package=XamarinComponent

+#addin nuget:?package=Cake.Android.SdkManager
 #addin nuget:?package=Cake.XCode
 #addin nuget:?package=Cake.Xamarin
 #addin nuget:?package=Cake.Xamarin.Build
@@ -8,16 +9,12 @@
 #addin nuget:?package=Cake.MonoApiTools

 var PREVIEW = "";
-var VERSION = EnvironmentVariable ("APPVEYOR_BUILD_VERSION") ?? Argument("version", "2.1.9999");
+var VERSION = EnvironmentVariable ("APPVEYOR_BUILD_VERSION") ?? Argument("version", "0.0.0");
 var NUGET_VERSION = VERSION;

+var ANDROID_HOME = EnvironmentVariable ("ANDROID_HOME") ?? Argument ("android_home", "");
+
 var TARGET = Argument ("t", Argument ("target", "Default"));
-
-// Build a semver string out of the preview if it's specified
-if (!string.IsNullOrEmpty (PREVIEW)) {
-	var sv = ParseSemVer (VERSION);
-	NUGET_VERSION = CreateSemVer (sv.Major, sv.Minor, sv.Patch, PREVIEW).ToString ();
-}

 var buildSpec = new BuildSpec {

@@ -78,6 +75,27 @@
 	};
 }

+Task ("externals")
+	.IsDependentOn ("externals-base")
+	.Does (() =>
+{
+	Information ("ANDROID_HOME: {0}", ANDROID_HOME);
+
+	var androidSdkSettings = new AndroidSdkManagerToolSettings {
+		SdkRoot = ANDROID_HOME,
+		SkipVersionCheck = true
+	};
+
+	try { AcceptLicenses (androidSdkSettings); } catch { }
+
+	AndroidSdkManagerInstall (new [] {
+			"platforms;android-15",
+			"platforms;android-23",
+			"platforms;android-25",
+			"platforms;android-26"
+		}, androidSdkSettings);
+});
+

 Task ("component-setup")
 	.IsDependentOn ("samples")


Removing the gyp build changes as we are going to require VS2015

--- build.cake
+++ build.cake
@@ -222,7 +222,7 @@
         !FileExists ("native-builds/lib/windows/x64/libskia_windows.dll"))
     .Does (() =>
 {
-    var propsFixup = new Action (() => {
+    var fixup = new Action (() => {
         var props = SKIA_PATH.CombineWithFilePath ("out/gyp/libjpeg-turbo.props").FullPath;
         var xdoc = XDocument.Load (props);
         var ns = (XNamespace) "http://schemas.microsoft.com/developer/msbuild/2003";
@@ -238,46 +238,18 @@
         }
     });

-    var gypVsVersionFixup = new Action<string> ((solutionPlatform) => {
-        var ns = (XNamespace) "http://schemas.microsoft.com/developer/msbuild/2003";
-        var correctVsVersion = 2015;
-        var vsVersionVar = EnvironmentVariable ("GYP_MSVS_VERSION") ?? correctVsVersion.ToString ();
-        var vsVersion = correctVsVersion;
-        if (!int.TryParse (vsVersionVar, out vsVersion)) {
-            vsVersion = correctVsVersion;
-        }
-        if (vsVersion < correctVsVersion) {
-            Information ("Fixing VS project versions. Was {0}, now {1}", vsVersionVar, correctVsVersion);
-            var sln = ROOT_PATH.CombineWithFilePath ("native-builds/libskia_windows/libskia_windows_" + solutionPlatform + ".sln");
-            ProcessSolutionProjects (sln, (projectName, projectPath) => {
-                if (projectName != "libskia_windows") {
-                    var xproj = XDocument.Load (projectPath.FullPath);
-                    var locals = xproj.Root
-                        .Elements (ns + "PropertyGroup")
-                        .Where (e => e.Attribute ("Label") != null && e.Attribute ("Label").Value == "Locals")
-                        .Elements (ns + "PlatformToolset");
-                    foreach (var local in locals) {
-                        local.Value = "v140";
-                    }
-                    xproj.Save (projectPath.FullPath);
-                }
-            });
-        }
-    });
-
     var buildArch = new Action<string, string, string> ((platform, skiaArch, dir) => {
         SetEnvironmentVariable ("GYP_DEFINES", "skia_arch_type='" + skiaArch + "'");
         RunGyp ();
-        gypVsVersionFixup (dir);
-        propsFixup ();
-        DotNetBuild ("native-builds/libskia_windows/libskia_windows_" + dir + ".sln", c => {
-            c.Configuration = "Release";
-            c.Properties ["Platform"] = new [] { platform };
-        });
-        if (!DirectoryExists ("native-builds/lib/windows/" + dir)) CreateDirectory ("native-builds/lib/windows/" + dir);
-        CopyFileToDirectory ("native-builds/libskia_windows/Release/libskia_windows.lib", "native-builds/lib/windows/" + dir);
-        CopyFileToDirectory ("native-builds/libskia_windows/Release/libskia_windows.dll", "native-builds/lib/windows/" + dir);
-        CopyFileToDirectory ("native-builds/libskia_windows/Release/libskia_windows.pdb", "native-builds/lib/windows/" + dir);
+        fixup ();
+        // DotNetBuild ("native-builds/libskia_windows/libskia_windows_" + dir + ".sln", c => {
+        //     c.Configuration = "Release";
+        //     c.Properties ["Platform"] = new [] { platform };
+        // });
+        // if (!DirectoryExists ("native-builds/lib/windows/" + dir)) CreateDirectory ("native-builds/lib/windows/" + dir);
+        // CopyFileToDirectory ("native-builds/libskia_windows/Release/libskia_windows.lib", "native-builds/lib/windows/" + dir);
+        // CopyFileToDirectory ("native-builds/libskia_windows/Release/libskia_windows.dll", "native-builds/lib/windows/" + dir);
+        // CopyFileToDirectory ("native-builds/libskia_windows/Release/libskia_windows.pdb", "native-builds/lib/windows/" + dir);
     });

     // set up the gyp environment variables


remove diagnostic build flag

remove diagnostic build flag

--- build.cake
+++ build.cake
@@ -140,8 +140,7 @@
 					var msBuildSettings = new MSBuildSettings {
 						MaxCpuCount = 1,
 						Configuration = configuration,
-						PlatformTarget = PlatformTarget.MSIL,
-						Verbosity = Verbosity.Diagnostic
+						PlatformTarget = PlatformTarget.MSIL
 					}.WithProperty("TreatWarningsAsErrors",buildSettings.Build.TreatWarningsAsErrors.ToString())
 					 .WithTarget("Build");
 				 	MSBuild(solution, msBuildSettings);
@@ -149,8 +148,7 @@
 				else
 				{
 					var settings = new XBuildSettings {
-						Configuration = configuration,
-						Verbosity = Verbosity.Diagnostic
+						Configuration = configuration
 					}.WithProperty("TreatWarningsAsErrors",buildSettings.Build.TreatWarningsAsErrors.ToString())
 					 .WithTarget("Build");
 					XBuild(solution, settings);