public void RenderBlock(bool exception) { bool called = false; LogEvent firstLog = null; IRenderer <DumbAction> renderer = new AnonymousRenderer <DumbAction> { BlockRenderer = (oldTip, newTip) => { LogEvent[] logs = LogEvents.ToArray(); Assert.Single(logs); firstLog = logs[0]; Assert.Equal(_genesis, oldTip); Assert.Equal(_blockA, newTip); called = true; if (exception) { throw new ThrowException.SomeException(string.Empty); } }, }; renderer = new LoggedRenderer <DumbAction>(renderer, _logger); Assert.False(called); Assert.Empty(LogEvents); renderer.RenderReorg(_blockA, _blockB, _genesis); Assert.False(called); Assert.Equal(2, LogEvents.Count()); ResetContext(); if (exception) { Assert.Throws <ThrowException.SomeException>( () => renderer.RenderBlock(_genesis, _blockA) ); } else { renderer.RenderBlock(_genesis, _blockA); } Assert.True(called); LogEvent[] logEvents = LogEvents.ToArray(); Assert.Equal(2, logEvents.Length); Assert.Equal(firstLog, logEvents[0]); Assert.Equal(LogEventLevel.Debug, firstLog.Level); Assert.Equal( "Invoking {MethodName}() for #{NewIndex} {NewHash} (was #{OldIndex} {OldHash})...", firstLog.MessageTemplate.Text ); Assert.Equal("\"RenderBlock\"", firstLog.Properties["MethodName"].ToString()); Assert.Equal( _blockA.Index.ToString(CultureInfo.InvariantCulture), firstLog.Properties["NewIndex"].ToString() ); Assert.Equal($"\"{_blockA.Hash}\"", firstLog.Properties["NewHash"].ToString()); Assert.Equal( _genesis.Index.ToString(CultureInfo.InvariantCulture), firstLog.Properties["OldIndex"].ToString() ); Assert.Equal($"\"{_genesis.Hash}\"", firstLog.Properties["OldHash"].ToString()); Assert.Equal( $"\"{typeof(AnonymousRenderer<DumbAction>).FullName}\"", firstLog.Properties[Constants.SourceContextPropertyName].ToString() ); Assert.Null(firstLog.Exception); LogEvent secondLog = logEvents[1]; Assert.Equal( exception ? LogEventLevel.Error : LogEventLevel.Debug, secondLog.Level ); Assert.Equal( exception ? "An exception was thrown during {MethodName}() for #{NewIndex} {NewHash} " + "(was #{OldIndex} {OldHash}): {Exception}" : "Invoked {MethodName}() for #{NewIndex} {NewHash} " + "(was #{OldIndex} {OldHash}).", secondLog.MessageTemplate.Text ); Assert.Equal(firstLog.Properties["MethodName"], secondLog.Properties["MethodName"]); Assert.Equal(firstLog.Properties["NewIndex"], secondLog.Properties["NewIndex"]); Assert.Equal(firstLog.Properties["NewHash"], secondLog.Properties["NewHash"]); Assert.Equal(firstLog.Properties["OldIndex"], secondLog.Properties["OldIndex"]); Assert.Equal(firstLog.Properties["OldHash"], secondLog.Properties["OldHash"]); Assert.Equal( firstLog.Properties[Constants.SourceContextPropertyName], secondLog.Properties[Constants.SourceContextPropertyName] ); if (exception) { Assert.StartsWith( $"\"{typeof(ThrowException.SomeException).FullName}", secondLog.Properties["Exception"].ToString() ); Assert.IsType <ThrowException.SomeException>(secondLog.Exception); } else { Assert.False(secondLog.Properties.ContainsKey("Exception")); Assert.Null(secondLog.Exception); } }
public virtual void BlocksBeingAppendedInParallel() { var blockLogs = new List <(Block <DumbAction> OldTip, Block <DumbAction> NewTip)>(); var reorgLogs = new List <( Block <DumbAction> OldTip, Block <DumbAction> NewTip, Block <DumbAction> Branchpoint )>(); var innerRenderer = new AnonymousRenderer <DumbAction> { BlockRenderer = (oldTip, newTip) => blockLogs.Add((oldTip, newTip)), ReorgRenderer = (oldTip, newTip, bp) => reorgLogs.Add((oldTip, newTip, bp)), }; var delayedRenderer = new DelayedRenderer <DumbAction>( innerRenderer, _canonicalChainComparer, _store, confirmations: 3 ); var renderer = new LoggedRenderer <DumbAction>( delayedRenderer, _logger, LogEventLevel.Verbose ); Assert.Null(delayedRenderer.Tip); Assert.Empty(blockLogs); Assert.Empty(reorgLogs); // Some explanation on the fixture: there are two chains that shares a certain block // as a branchpoint: _chainA and _chainB. The topmost mutual block, i.e., branchpoint, // between _chainA and _chainB is _chainA[4] (== _chainB[4]). // In this test, we tries to simulate blocks from two parallel chains being "appended" // (discovered) to a peer. The order of discovery can be drawn as below (the prime // [apostrophe] after the block index number like #N' shows it's from _chainB; the bare // block index without that like #N means it's from _chainA): // // #4 (1st) // | // / \ // #5 (2nd) #5' (3rd) // | | // #6 (3rd) #6' (4th) // | | // #7 (5th) #7' (6th) // | | // #8 (7th) #8' (8th) // | // #9' (9th) // #4 -> 1 confirm // #5 -> no confirms // #5' -> no confirms renderer.RenderBlock(_chainA[4], _chainA[5]); var expectedBlockLogs = new List <(Block <DumbAction> OldTip, Block <DumbAction> NewTip)> { (_chainA[1], _chainA[2]), }; Assert.Equal(_chainA[2], delayedRenderer.Tip); Assert.Empty(reorgLogs); Assert.Equal(expectedBlockLogs, blockLogs); // #4 -> 1 confirms // #5 -> no confirms // #5' -> no confirms renderer.RenderReorg(_chainA[5], _chainB[5], _branchpoint); renderer.RenderBlock(_chainA[5], _chainB[5]); renderer.RenderReorgEnd(_chainA[5], _chainB[5], _branchpoint); Assert.Equal(_chainA[2], delayedRenderer.Tip); Assert.Empty(reorgLogs); Assert.Equal(expectedBlockLogs, blockLogs); // #4 -> 2 confirms; tip changed -> #3 // #5 -> 1 confirm; #6 -> no confirm // #5' -> no confirms renderer.RenderReorg(_chainB[5], _chainA[6], _branchpoint); renderer.RenderBlock(_chainB[5], _chainA[6]); renderer.RenderReorgEnd(_chainB[5], _chainA[6], _branchpoint); expectedBlockLogs.Add((_chainA[2], _chainA[3])); Assert.Equal(_chainA[3], delayedRenderer.Tip); Assert.Empty(reorgLogs); Assert.Equal(expectedBlockLogs, blockLogs); // #4 -> 2 confirms // #5 -> 1 confirm; #6 -> no confirm // #5' -> 1 confirm; #6' -> no confirm renderer.RenderReorg(_chainA[6], _chainB[6], _branchpoint); renderer.RenderBlock(_chainA[6], _chainB[6]); renderer.RenderReorgEnd(_chainA[6], _chainB[6], _branchpoint); Assert.Equal(_chainA[3], delayedRenderer.Tip); Assert.Empty(reorgLogs); Assert.Equal(expectedBlockLogs, blockLogs); // #4 -> 3 confirms; tip changed -> #4 // #5 -> 2 confirms; #6 -> 1 confirm; #7 -> no confirm // #5' -> 1 confirm; #6' -> no confirm renderer.RenderReorg(_chainB[6], _chainA[7], _branchpoint); renderer.RenderBlock(_chainB[6], _chainA[7]); renderer.RenderReorgEnd(_chainB[6], _chainA[7], _branchpoint); expectedBlockLogs.Add((_chainA[3], _chainA[4])); Assert.Equal(_chainA[4], delayedRenderer.Tip); Assert.Empty(reorgLogs); Assert.Equal(expectedBlockLogs, blockLogs); // #4 -> gone but still is tip // #5 -> 2 confirms; #6 -> 1 confirm; #7 -> no confirm // #5' -> 2 confirms; #6' -> 1 confirm; #7' -> no confirm renderer.RenderReorg(_chainA[7], _chainB[7], _branchpoint); renderer.RenderBlock(_chainA[7], _chainB[7]); renderer.RenderReorgEnd(_chainA[7], _chainB[7], _branchpoint); Assert.Equal(_chainA[4], delayedRenderer.Tip); Assert.Empty(reorgLogs); Assert.Equal(expectedBlockLogs, blockLogs); // #4 -> gone; tip changed -> #5; render(#4, #5) // #5 -> 3 confirms; #6 -> 2 confirms; #7 -> 1 confirm; #8 -> no confirm // #5' -> 2 confirms; #6' -> 1 confirm; #7' -> no confirm renderer.RenderReorg(_chainB[7], _chainA[8], _branchpoint); renderer.RenderBlock(_chainB[7], _chainA[8]); renderer.RenderReorgEnd(_chainB[7], _chainA[8], _branchpoint); expectedBlockLogs.Add((_chainA[4], _chainA[5])); Assert.Equal(_chainA[5], delayedRenderer.Tip); Assert.Empty(reorgLogs); Assert.Equal(expectedBlockLogs, blockLogs); // tip changed -> #5'; render(#5, #5'); reorg(#5, #5', #4) // #5 -> 3 confirms; #6 -> 2 confirms; #7 -> 1 confirm; #8 -> no confirm // #5' -> 3 confirms; #6' -> 2 confirms; #7' -> 1 confirm; #8' -> no confirm renderer.RenderReorg(_chainA[8], _chainB[8], _branchpoint); renderer.RenderBlock(_chainA[8], _chainB[8]); renderer.RenderReorgEnd(_chainA[8], _chainB[8], _branchpoint); expectedBlockLogs.Add((_chainA[5], _chainB[5])); Assert.Equal(_chainB[5], delayedRenderer.Tip); Assert.Equal(new[] { (_chainA[5], _chainB[5], _branchpoint) }, reorgLogs);