diff --git a/src/Neo.SmartContract.Testing/Coverage/CoverageBase.cs b/src/Neo.SmartContract.Testing/Coverage/CoverageBase.cs index 1920e0335..32a186cde 100644 --- a/src/Neo.SmartContract.Testing/Coverage/CoverageBase.cs +++ b/src/Neo.SmartContract.Testing/Coverage/CoverageBase.cs @@ -1,56 +1,89 @@ -using System.Collections; using System.Collections.Generic; using System.Linq; namespace Neo.SmartContract.Testing.Coverage { - public abstract class CoverageBase : IEnumerable + public abstract class CoverageBase { /// - /// Coverage + /// Coverage Branches /// - public abstract IEnumerable Coverage { get; } + public abstract IEnumerable Branches { get; } /// - /// Total instructions (could be different from Coverage.Count if, for example, a contract JUMPS to PUSHDATA content) + /// Coverage Lines /// - public virtual int TotalInstructions => Coverage.Where(u => !u.OutOfScript).Count(); + public abstract IEnumerable Lines { get; } /// - /// Covered Instructions (OutOfScript are not taken into account) + /// Total lines instructions (could be different from Coverage.Count if, for example, a contract JUMPS to PUSHDATA content) /// - public virtual int CoveredInstructions => Coverage.Where(u => !u.OutOfScript && u.Hits > 0).Count(); + public int TotalLines => Lines.Where(u => !u.OutOfScript).Count(); /// - /// All instructions that have been touched + /// Total branches /// - public virtual int HitsInstructions => Coverage.Where(u => u.Hits > 0).Count(); + public int TotalBranches => Branches.Where(u => !u.OutOfScript).Sum(u => u.Count); /// - /// Covered Percentage + /// Covered lines (OutOfScript are not taken into account) /// - public float CoveredPercentage + public int CoveredLines => Lines.Where(u => !u.OutOfScript && u.Hits > 0).Count(); + + /// + /// Covered lines (OutOfScript are not taken into account) + /// + public int CoveredBranches => Branches.Where(u => !u.OutOfScript && u.Hits > 0).Count(); + + /// + /// All lines that have been touched + /// + public int CoveredLinesAll => Lines.Where(u => u.Hits > 0).Count(); + + /// + /// All branches that have been touched + /// + public int CoveredBranchesAll => Branches.Where(u => u.Hits > 0).Count(); + + /// + /// Covered lines percentage + /// + public float CoveredLinesPercentage + { + get + { + var total = TotalLines; + if (total == 0) return 1F; + + return (float)CoveredLines / total; + } + } + + /// + /// Covered branch percentage + /// + public float CoveredBranchPercentage { get { - var total = TotalInstructions; - if (total == 0) return 0F; + var total = TotalBranches; + if (total == 0) return 1F; - return (float)CoveredInstructions / total; + return (float)CoveredBranches / total; } } /// - /// Get Coverage from the Contract coverage + /// Get Coverage lines from the Contract coverage /// /// Offset /// Length /// Coverage - public IEnumerable GetCoverageFrom(int offset, int length) + public IEnumerable GetCoverageLinesFrom(int offset, int length) { var to = offset + length; - foreach (var kvp in Coverage) + foreach (var kvp in Lines) { if (kvp.Offset >= offset && kvp.Offset <= to) { @@ -59,12 +92,24 @@ public IEnumerable GetCoverageFrom(int offset, int length) } } - #region IEnumerable - - public IEnumerator GetEnumerator() => Coverage.GetEnumerator(); - IEnumerator IEnumerable.GetEnumerator() => Coverage.GetEnumerator(); + /// + /// Get Coverage branch from the Contract coverage + /// + /// Offset + /// Length + /// Coverage + public IEnumerable GetCoverageBranchFrom(int offset, int length) + { + var to = offset + length; - #endregion + foreach (var kvp in Branches) + { + if (kvp.Offset >= offset && kvp.Offset <= to) + { + yield return kvp; + } + } + } // Allow to sum coverages diff --git a/src/Neo.SmartContract.Testing/Coverage/CoverageBranch.cs b/src/Neo.SmartContract.Testing/Coverage/CoverageBranch.cs new file mode 100644 index 000000000..3ea8a5f2e --- /dev/null +++ b/src/Neo.SmartContract.Testing/Coverage/CoverageBranch.cs @@ -0,0 +1,84 @@ +using System; +using System.Diagnostics; + +namespace Neo.SmartContract.Testing.Coverage +{ + [DebuggerDisplay("Offset={Offset}, Count={Count}, Hits={Hits}")] + public class CoverageBranch + { + /// + /// Offset + /// + public int Offset { get; } + + /// + /// Count + /// + public int Count { get; } + + /// + /// The branch is out of the script + /// + public bool OutOfScript { get; } + + /// + /// Hits + /// + public int Hits => (PositivePathHits > 0 ? 1 : 0) + (NegativePathHits > 0 ? 1 : 0); + + /// + /// Positive path hits + /// + public int PositivePathHits { get; set; } + + /// + /// Negative Path hits + /// + public int NegativePathHits { get; set; } + + /// + /// Constructor + /// + /// Offset + /// Out of script + public CoverageBranch(int offset, bool outOfScript = false) + { + Offset = offset; + Count = 2; + OutOfScript = outOfScript; + } + + /// + /// Hit branch + /// + /// Value + public void Hit(bool value) + { + if (value) PositivePathHits++; + else NegativePathHits++; + } + + /// + /// Hit branch + /// + /// Value + public void Hit(CoverageBranch value) + { + PositivePathHits += value.PositivePathHits; + NegativePathHits += value.NegativePathHits; + } + + /// + /// Clone branch + /// + /// CoverageBranch + public CoverageBranch Clone() + { + return new CoverageBranch(Offset, OutOfScript) + { + NegativePathHits = NegativePathHits, + PositivePathHits = PositivePathHits + }; + } + } +} diff --git a/src/Neo.SmartContract.Testing/Coverage/CoveredCollection.cs b/src/Neo.SmartContract.Testing/Coverage/CoveredCollection.cs index d5ebb27fc..523f51984 100644 --- a/src/Neo.SmartContract.Testing/Coverage/CoveredCollection.cs +++ b/src/Neo.SmartContract.Testing/Coverage/CoveredCollection.cs @@ -10,17 +10,34 @@ public class CoveredCollection : CoverageBase public CoverageBase[] Entries { get; } /// - /// Coverage + /// Coverage Lines /// - public override IEnumerable Coverage + public override IEnumerable Lines { get { - foreach (var method in Entries) + foreach (var entry in Entries) { - foreach (var entry in method.Coverage) + foreach (var line in entry.Lines) { - yield return entry; + yield return line; + } + } + } + } + + /// + /// Coverage Branches + /// + public override IEnumerable Branches + { + get + { + foreach (var entry in Entries) + { + foreach (var branch in entry.Branches) + { + yield return branch; } } } diff --git a/src/Neo.SmartContract.Testing/Coverage/CoveredContract.cs b/src/Neo.SmartContract.Testing/Coverage/CoveredContract.cs index 2047ed253..ca2e11ca3 100644 --- a/src/Neo.SmartContract.Testing/Coverage/CoveredContract.cs +++ b/src/Neo.SmartContract.Testing/Coverage/CoveredContract.cs @@ -14,7 +14,8 @@ public class CoveredContract : CoverageBase { #region Internal - private readonly Dictionary _coverageData = new(); + private readonly Dictionary _lines = new(); + private readonly Dictionary _branches = new(); #endregion @@ -29,9 +30,14 @@ public class CoveredContract : CoverageBase public CoveredMethod[] Methods { get; private set; } /// - /// Coverage + /// Coverage Lines /// - public override IEnumerable Coverage => _coverageData.Values; + public override IEnumerable Lines => _lines.Values; + + /// + /// Coverage Branches + /// + public override IEnumerable Branches => _branches.Values; /// /// CoveredContract @@ -65,9 +71,9 @@ internal void GenerateMethods(MethodDetectionMechanism mechanism, ContractState { var instruction = script.GetInstruction(ip); - if (!_coverageData.ContainsKey(ip)) + if (!_lines.ContainsKey(ip)) { - _coverageData[ip] = new CoverageHit(ip, CoverageHit.DescriptionFromInstruction(instruction, state.Nef.Tokens), false); + AddLine(instruction, new CoverageHit(ip, CoverageHit.DescriptionFromInstruction(instruction, state.Nef.Tokens), false)); } if (mechanism == MethodDetectionMechanism.NextMethod) @@ -120,6 +126,37 @@ internal void GenerateMethods(MethodDetectionMechanism mechanism, ContractState .ToArray()!; } + private void AddLine(Instruction instruction, CoverageHit hit) + { + _lines[hit.Offset] = hit; + + // Check if we should add a branc + + switch (instruction.OpCode) + { + case OpCode.JMPIF: + case OpCode.JMPIF_L: + case OpCode.JMPIFNOT: + case OpCode.JMPIFNOT_L: + case OpCode.JMPEQ: + case OpCode.JMPEQ_L: + case OpCode.JMPNE: + case OpCode.JMPNE_L: + case OpCode.JMPGT: + case OpCode.JMPGT_L: + case OpCode.JMPGE: + case OpCode.JMPGE_L: + case OpCode.JMPLT: + case OpCode.JMPLT_L: + case OpCode.JMPLE: + case OpCode.JMPLE_L: + { + _branches[hit.Offset] = new CoverageBranch(hit.Offset, hit.OutOfScript); + break; + } + } + } + private CoveredMethod CreateMethod( MethodDetectionMechanism mechanism, Script script, List allMethods, ContractMethodDescriptor abiMethod @@ -182,25 +219,44 @@ private CoveredMethod CreateMethod( /// Join coverage /// /// Coverage - public void Join(IEnumerable? coverage) + public void Join(CoverageBase? coverage) { - if (coverage is null || coverage.Any() == false) return; + if (coverage is null) return; + + // Join the coverage lines + + foreach (var c in coverage.Lines) + { + if (c.Hits == 0) continue; - // Join the coverage between them + lock (_lines) + { + if (_lines.TryGetValue(c.Offset, out var kvpValue)) + { + kvpValue.Hit(c); + } + else + { + _lines.Add(c.Offset, c.Clone()); + } + } + } - foreach (var c in coverage) + // Join the coverage branches + + foreach (var c in coverage.Branches) { if (c.Hits == 0) continue; - lock (_coverageData) + lock (_branches) { - if (_coverageData.TryGetValue(c.Offset, out var kvpValue)) + if (_branches.TryGetValue(c.Offset, out var kvpValue)) { kvpValue.Hit(c); } else { - _coverageData.Add(c.Offset, c.Clone()); + _branches.Add(c.Offset, c.Clone()); } } } @@ -231,31 +287,34 @@ internal string Dump(DumpFormat format, params CoveredMethod[] methods) { case DumpFormat.Console: { - var cover = $"{CoveredPercentage:P2}"; - sourceCode.WriteLine($"{Hash} [{cover}]"); + var coverLines = $"{CoveredLinesPercentage:P2}"; + var coverBranch = $"{CoveredBranchPercentage:P2}"; + sourceCode.WriteLine($"{Hash} [{coverLines} - {coverBranch}]"); List rows = new(); - var max = new int[] { "Method".Length, "Line ".Length }; + var max = new int[] { "Method".Length, "Line ".Length, "Branch".Length }; - foreach (var method in methods.OrderBy(u => u.Method.Name).OrderByDescending(u => u.CoveredPercentage)) + foreach (var method in methods.OrderBy(u => u.Method.Name).OrderByDescending(u => u.CoveredLinesPercentage)) { - cover = $"{method.CoveredPercentage:P2}"; - rows.Add(new string[] { method.Method.ToString(), cover }); + coverLines = $"{method.CoveredLinesPercentage:P2}"; + coverBranch = $"{method.CoveredBranchPercentage:P2}"; + rows.Add(new string[] { method.Method.ToString(), coverLines, coverBranch }); max[0] = Math.Max(method.Method.ToString().Length, max[0]); - max[1] = Math.Max(cover.Length, max[1]); + max[1] = Math.Max(coverLines.Length, max[1]); + max[2] = Math.Max(coverLines.Length, max[2]); } - sourceCode.WriteLine($"┌-{"─".PadLeft(max[0], '─')}-┬-{"─".PadLeft(max[1], '─')}-┐"); - sourceCode.WriteLine($"│ {string.Format($"{{0,-{max[0]}}}", "Method", max[0])} │ {string.Format($"{{0,{max[1]}}}", "Line ", max[1])} │"); - sourceCode.WriteLine($"├-{"─".PadLeft(max[0], '─')}-┼-{"─".PadLeft(max[1], '─')}-┤"); + sourceCode.WriteLine($"┌-{"─".PadLeft(max[0], '─')}-┬-{"─".PadLeft(max[1], '─')}-┬-{"─".PadLeft(max[1], '─')}-┐"); + sourceCode.WriteLine($"│ {string.Format($"{{0,-{max[0]}}}", "Method", max[0])} │ {string.Format($"{{0,{max[1]}}}", "Line ", max[1])} │ {string.Format($"{{0,{max[2]}}}", "Branch", max[1])} │"); + sourceCode.WriteLine($"├-{"─".PadLeft(max[0], '─')}-┼-{"─".PadLeft(max[1], '─')}-┼-{"─".PadLeft(max[1], '─')}-┤"); foreach (var print in rows) { - sourceCode.WriteLine($"│ {string.Format($"{{0,-{max[0]}}}", print[0], max[0])} │ {string.Format($"{{0,{max[1]}}}", print[1], max[1])} │"); + sourceCode.WriteLine($"│ {string.Format($"{{0,-{max[0]}}}", print[0], max[0])} │ {string.Format($"{{0,{max[1]}}}", print[1], max[1])} │ {string.Format($"{{0,{max[1]}}}", print[2], max[2])} │"); } - sourceCode.WriteLine($"└-{"─".PadLeft(max[0], '─')}-┴-{"─".PadLeft(max[1], '─')}-┘"); + sourceCode.WriteLine($"└-{"─".PadLeft(max[0], '─')}-┴-{"─".PadLeft(max[1], '─')}-┴-{"─".PadLeft(max[2], '─')}-┘"); break; } case DumpFormat.Html: @@ -271,7 +330,7 @@ internal string Dump(DumpFormat format, params CoveredMethod[] methods) .bar { background-color: #f2f2f2; padding: 10px; cursor: pointer; } .hash { float: left; } .method-name { float: left; } - .coverage { float: right; } + .coverage { float: right; display: inline-block; width: 100px; text-align: right; } .method { cursor: pointer; margin-top: 5px; padding: 2px; } .details { display: none; padding-left: 20px; } .container { padding-left: 20px; } @@ -279,6 +338,7 @@ internal string Dump(DumpFormat format, params CoveredMethod[] methods) .hit { background-color: #eafaea; } /* Light green for hits */ .no-hit { background-color: #ffcccc; } /* Light red for no hits */ .hits { margin-left: 5px; font-size: 0.6em; margin-right: 10px; } + .branch { margin-left: 5px; font-size: 0.6em; margin-right: } .icon { margin-right: 5px; } .high-coverage { background-color: #ccffcc; } /* Lighter green for high coverage */ @@ -292,33 +352,41 @@ internal string Dump(DumpFormat format, params CoveredMethod[] methods) sourceCode.WriteLine($@"
{Hash}
-
{CoveredPercentage:P2}
+
 {CoveredBranchPercentage:P2} 
+
 {CoveredLinesPercentage:P2} 
"); - foreach (var method in methods.OrderBy(u => u.Method.Name).OrderByDescending(u => u.CoveredPercentage)) + foreach (var method in methods.OrderBy(u => u.Method.Name).OrderByDescending(u => u.CoveredLinesPercentage)) { var kind = "low"; - if (method.CoveredPercentage > 0.7) kind = "medium"; - if (method.CoveredPercentage > 0.8) kind = "high"; + if (method.CoveredLinesPercentage > 0.7) kind = "medium"; + if (method.CoveredLinesPercentage > 0.8) kind = "high"; sourceCode.WriteLine($@"
{method.Method}
-
{method.CoveredPercentage:P2}
+
 {method.CoveredBranchPercentage:P2} 
+
 {method.CoveredLinesPercentage:P2} 
"); sourceCode.WriteLine($@"
"); - foreach (var hit in method.Coverage) + foreach (var hit in method.Lines) { var noHit = hit.Hits == 0 ? "no-" : ""; var icon = hit.Hits == 0 ? "✘" : "✔"; + var branch = ""; + + if (_branches.TryGetValue(hit.Offset, out var b)) + { + branch = $" [ᛦ {b.Hits}/{b.Count}]"; + } - sourceCode.WriteLine($@"
{icon}{hit.Hits} Hits{hit.Description}
"); + sourceCode.WriteLine($@"
{icon}{hit.Hits} Hits{hit.Description}{branch}
"); } sourceCode.WriteLine($@"
@@ -361,16 +429,24 @@ internal string Dump(DumpFormat format, params CoveredMethod[] methods) /// Instruction pointer /// Instruction /// Gas - public void Hit(int instructionPointer, Instruction instruction, long gas) + /// Branch path + public void Hit(int instructionPointer, Instruction instruction, long gas, bool? branchPath) { - lock (_coverageData) + lock (_lines) { - if (!_coverageData.TryGetValue(instructionPointer, out var coverage)) + if (!_lines.TryGetValue(instructionPointer, out var coverage)) { // Note: This call is unusual, out of the expected - _coverageData[instructionPointer] = coverage = new(instructionPointer, CoverageHit.DescriptionFromInstruction(instruction), true); + coverage = new(instructionPointer, CoverageHit.DescriptionFromInstruction(instruction), true); + AddLine(instruction, coverage); } + + if (branchPath is not null && _branches.TryGetValue(instructionPointer, out var branch)) + { + branch.Hit(branchPath.Value); + } + coverage.Hit(gas); } } diff --git a/src/Neo.SmartContract.Testing/Coverage/CoveredMethod.cs b/src/Neo.SmartContract.Testing/Coverage/CoveredMethod.cs index 0ed2aa80f..05b99c4dc 100644 --- a/src/Neo.SmartContract.Testing/Coverage/CoveredMethod.cs +++ b/src/Neo.SmartContract.Testing/Coverage/CoveredMethod.cs @@ -29,9 +29,14 @@ public class CoveredMethod : CoverageBase public int MethodLength { get; } /// - /// Coverage + /// Coverage Lines /// - public override IEnumerable Coverage => Contract.GetCoverageFrom(Offset, MethodLength); + public override IEnumerable Lines => Contract.GetCoverageLinesFrom(Offset, MethodLength); + + /// + /// Coverage Branches + /// + public override IEnumerable Branches => Contract.GetCoverageBranchFrom(Offset, MethodLength); /// /// Constructor diff --git a/src/Neo.SmartContract.Testing/TestEngine.cs b/src/Neo.SmartContract.Testing/TestEngine.cs index 550dfcc6e..4ffb2ccaf 100644 --- a/src/Neo.SmartContract.Testing/TestEngine.cs +++ b/src/Neo.SmartContract.Testing/TestEngine.cs @@ -305,6 +305,22 @@ public T Deploy(NefFile nef, ContractManifest manifest, object? data = null, { // Deploy + if (EnableCoverageCapture) + { + UInt160 expectedHash = GetDeployHash(nef, manifest); + + if (!Coverage.ContainsKey(expectedHash)) + { + var coveredContract = new CoveredContract(MethodDetection, expectedHash, new ContractState() + { + Nef = nef, + Hash = expectedHash, + Manifest = manifest + }); + Coverage[coveredContract.Hash] = coveredContract; + } + } + var state = Native.ContractManagement.Deploy(nef.ToArray(), Encoding.UTF8.GetBytes(manifest.ToJson().ToString(false)), data); if (state is null) @@ -314,7 +330,6 @@ public T Deploy(NefFile nef, ContractManifest manifest, object? data = null, // Mock contract - //UInt160 hash = Helper.GetContractHash(Sender, nef.CheckSum, manifest.Name); var ret = MockContract(state.Hash, state.Id, customMock); // We cache the coverage contract during `_deploy` diff --git a/src/Neo.SmartContract.Testing/TestingApplicationEngine.cs b/src/Neo.SmartContract.Testing/TestingApplicationEngine.cs index d4988db76..5be6dda57 100644 --- a/src/Neo.SmartContract.Testing/TestingApplicationEngine.cs +++ b/src/Neo.SmartContract.Testing/TestingApplicationEngine.cs @@ -17,6 +17,7 @@ internal class TestingApplicationEngine : ApplicationEngine private ExecutionContext? InstructionContext; private int? InstructionPointer; private long PreExecuteInstructionGasConsumed; + private bool? BranchPath; /// /// Testing engine @@ -41,6 +42,74 @@ protected override void PreExecuteInstruction(Instruction instruction) InstructionPointer = InstructionContext?.InstructionPointer; } + // Calculate branch path + + BranchPath = null; + + switch (instruction.OpCode) + { + case OpCode.JMPIF: + case OpCode.JMPIF_L: + case OpCode.JMPIFNOT: + case OpCode.JMPIFNOT_L: + { + if (CurrentContext!.EvaluationStack.Count >= 1) + { + // We don't care about the positive or negative path + // for coverage is the same + BranchPath = Peek(0).GetBoolean(); + } + break; + } + case OpCode.JMPEQ: + case OpCode.JMPEQ_L: + case OpCode.JMPNE: + case OpCode.JMPNE_L: + { + if (CurrentContext!.EvaluationStack.Count >= 2) + { + BranchPath = Peek(0).GetInteger() == Peek(1).GetInteger(); + } + break; + } + case OpCode.JMPGT: + case OpCode.JMPGT_L: + { + if (CurrentContext!.EvaluationStack.Count >= 2) + { + BranchPath = Peek(0).GetInteger() > Peek(1).GetInteger(); + } + break; + } + case OpCode.JMPGE: + case OpCode.JMPGE_L: + { + if (CurrentContext!.EvaluationStack.Count >= 2) + { + BranchPath = Peek(0).GetInteger() >= Peek(1).GetInteger(); + } + break; + } + case OpCode.JMPLT: + case OpCode.JMPLT_L: + { + if (CurrentContext!.EvaluationStack.Count >= 2) + { + BranchPath = Peek(0).GetInteger() < Peek(1).GetInteger(); + } + break; + } + case OpCode.JMPLE: + case OpCode.JMPLE_L: + { + if (CurrentContext!.EvaluationStack.Count >= 2) + { + BranchPath = Peek(0).GetInteger() <= Peek(1).GetInteger(); + } + break; + } + } + // Regular action base.PreExecuteInstruction(instruction); @@ -85,8 +154,9 @@ private void RecoverCoverage(Instruction instruction) if (InstructionPointer is null) return; - coveredContract.Hit(InstructionPointer.Value, instruction, GasConsumed - PreExecuteInstructionGasConsumed); + coveredContract.Hit(InstructionPointer.Value, instruction, GasConsumed - PreExecuteInstructionGasConsumed, BranchPath); + BranchPath = null; PreInstruction = null; InstructionContext = null; InstructionPointer = null; diff --git a/tests/Neo.SmartContract.Template.UnitTests/templates/neocontractnep17/CoverageContractTests.cs b/tests/Neo.SmartContract.Template.UnitTests/templates/neocontractnep17/CoverageContractTests.cs index 606af9e30..b55ad5e80 100644 --- a/tests/Neo.SmartContract.Template.UnitTests/templates/neocontractnep17/CoverageContractTests.cs +++ b/tests/Neo.SmartContract.Template.UnitTests/templates/neocontractnep17/CoverageContractTests.cs @@ -24,7 +24,7 @@ public static void EnsureCoverage() Console.WriteLine(coverage.Dump()); File.WriteAllText("coverage.html", coverage.Dump(Testing.Coverage.DumpFormat.Html)); - Assert.IsTrue(coverage.CoveredPercentage >= RequiredCoverage, $"Coverage is less than {RequiredCoverage:P2}"); + Assert.IsTrue(coverage.CoveredLinesPercentage >= RequiredCoverage, $"Coverage is less than {RequiredCoverage:P2}"); } } } diff --git a/tests/Neo.SmartContract.Testing.UnitTests/Coverage/CoverageDataTests.cs b/tests/Neo.SmartContract.Testing.UnitTests/Coverage/CoverageDataTests.cs index 3af364092..1039ea855 100644 --- a/tests/Neo.SmartContract.Testing.UnitTests/Coverage/CoverageDataTests.cs +++ b/tests/Neo.SmartContract.Testing.UnitTests/Coverage/CoverageDataTests.cs @@ -18,39 +18,39 @@ public void TestDump() Assert.AreEqual(100_000_000, engine.Native.NEO.TotalSupply); Assert.AreEqual(@" -0xef4073a0f2b305a38ec4050e4d3d28bc40ea63f5 [5.26%] -┌-───────────────────────────────-┬-───────-┐ -│ Method │ Line │ -├-───────────────────────────────-┼-───────-┤ -│ totalSupply() │ 100.00% │ -│ balanceOf(account) │ 0.00% │ -│ decimals() │ 0.00% │ -│ getAccountState(account) │ 0.00% │ -│ getAllCandidates() │ 0.00% │ -│ getCandidates() │ 0.00% │ -│ getCandidateVote(pubKey) │ 0.00% │ -│ getCommittee() │ 0.00% │ -│ getGasPerBlock() │ 0.00% │ -│ getNextBlockValidators() │ 0.00% │ -│ getRegisterPrice() │ 0.00% │ -│ registerCandidate(pubkey) │ 0.00% │ -│ setGasPerBlock(gasPerBlock) │ 0.00% │ -│ setRegisterPrice(registerPrice) │ 0.00% │ -│ symbol() │ 0.00% │ -│ transfer(from,to,amount,data) │ 0.00% │ -│ unclaimedGas(account,end) │ 0.00% │ -│ unregisterCandidate(pubkey) │ 0.00% │ -│ vote(account,voteTo) │ 0.00% │ -└-───────────────────────────────-┴-───────-┘ +0xef4073a0f2b305a38ec4050e4d3d28bc40ea63f5 [5.26% - 100.00%] +┌-───────────────────────────────-┬-───────-┬-───────-┐ +│ Method │ Line │ Branch │ +├-───────────────────────────────-┼-───────-┼-───────-┤ +│ totalSupply() │ 100.00% │ 100.00% │ +│ balanceOf(account) │ 0.00% │ 100.00% │ +│ decimals() │ 0.00% │ 100.00% │ +│ getAccountState(account) │ 0.00% │ 100.00% │ +│ getAllCandidates() │ 0.00% │ 100.00% │ +│ getCandidates() │ 0.00% │ 100.00% │ +│ getCandidateVote(pubKey) │ 0.00% │ 100.00% │ +│ getCommittee() │ 0.00% │ 100.00% │ +│ getGasPerBlock() │ 0.00% │ 100.00% │ +│ getNextBlockValidators() │ 0.00% │ 100.00% │ +│ getRegisterPrice() │ 0.00% │ 100.00% │ +│ registerCandidate(pubkey) │ 0.00% │ 100.00% │ +│ setGasPerBlock(gasPerBlock) │ 0.00% │ 100.00% │ +│ setRegisterPrice(registerPrice) │ 0.00% │ 100.00% │ +│ symbol() │ 0.00% │ 100.00% │ +│ transfer(from,to,amount,data) │ 0.00% │ 100.00% │ +│ unclaimedGas(account,end) │ 0.00% │ 100.00% │ +│ unregisterCandidate(pubkey) │ 0.00% │ 100.00% │ +│ vote(account,voteTo) │ 0.00% │ 100.00% │ +└-───────────────────────────────-┴-───────-┴-───────-┘ ".Trim(), engine.GetCoverage(engine.Native.NEO)?.Dump().Trim()); Assert.AreEqual(@" -0xef4073a0f2b305a38ec4050e4d3d28bc40ea63f5 [5.26%] -┌-─────────────-┬-───────-┐ -│ Method │ Line │ -├-─────────────-┼-───────-┤ -│ totalSupply() │ 100.00% │ -└-─────────────-┴-───────-┘ +0xef4073a0f2b305a38ec4050e4d3d28bc40ea63f5 [5.26% - 100.00%] +┌-─────────────-┬-───────-┬-───────-┐ +│ Method │ Line │ Branch │ +├-─────────────-┼-───────-┼-───────-┤ +│ totalSupply() │ 100.00% │ 100.00% │ +└-─────────────-┴-───────-┴-───────-┘ ".Trim(), (engine.Native.NEO.GetCoverage(o => o.TotalSupply) as CoveredMethod)?.Dump().Trim()); } @@ -74,17 +74,17 @@ public void TestCoverageByEngine() Assert.AreEqual(100_000_000, engine.Native.NEO.TotalSupply); Assert.AreEqual(engine.Native.NEO.Hash, engine.GetCoverage(engine.Native.NEO)?.Hash); - Assert.AreEqual(57, engine.GetCoverage(engine.Native.NEO)?.TotalInstructions); - Assert.AreEqual(3, engine.GetCoverage(engine.Native.NEO)?.CoveredInstructions); - Assert.AreEqual(3, engine.GetCoverage(engine.Native.NEO)?.HitsInstructions); + Assert.AreEqual(57, engine.GetCoverage(engine.Native.NEO)?.TotalLines); + Assert.AreEqual(3, engine.GetCoverage(engine.Native.NEO)?.CoveredLines); + Assert.AreEqual(3, engine.GetCoverage(engine.Native.NEO)?.CoveredLinesAll); // Check balanceOf Assert.AreEqual(0, engine.Native.NEO.BalanceOf(engine.Native.NEO.Hash)); - Assert.AreEqual(57, engine.GetCoverage(engine.Native.NEO)?.TotalInstructions); - Assert.AreEqual(6, engine.GetCoverage(engine.Native.NEO)?.CoveredInstructions); - Assert.AreEqual(6, engine.GetCoverage(engine.Native.NEO)?.HitsInstructions); + Assert.AreEqual(57, engine.GetCoverage(engine.Native.NEO)?.TotalLines); + Assert.AreEqual(6, engine.GetCoverage(engine.Native.NEO)?.CoveredLines); + Assert.AreEqual(6, engine.GetCoverage(engine.Native.NEO)?.CoveredLinesAll); // Check coverage by method and expression @@ -92,20 +92,20 @@ public void TestCoverageByEngine() Assert.IsNotNull(methodCovered); methodCovered = engine.GetCoverage(engine.Native.NEO, o => o.TotalSupply); - Assert.AreEqual(3, methodCovered?.TotalInstructions); - Assert.AreEqual(3, methodCovered?.CoveredInstructions); + Assert.AreEqual(3, methodCovered?.TotalLines); + Assert.AreEqual(3, methodCovered?.CoveredLines); methodCovered = engine.GetCoverage(engine.Native.NEO, o => o.RegisterPrice); - Assert.AreEqual(6, methodCovered?.TotalInstructions); - Assert.AreEqual(0, methodCovered?.CoveredInstructions); + Assert.AreEqual(6, methodCovered?.TotalLines); + Assert.AreEqual(0, methodCovered?.CoveredLines); methodCovered = engine.GetCoverage(engine.Native.NEO, o => o.BalanceOf(It.IsAny())); - Assert.AreEqual(3, methodCovered?.TotalInstructions); - Assert.AreEqual(3, methodCovered?.CoveredInstructions); + Assert.AreEqual(3, methodCovered?.TotalLines); + Assert.AreEqual(3, methodCovered?.CoveredLines); methodCovered = engine.GetCoverage(engine.Native.NEO, o => o.Transfer(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())); - Assert.AreEqual(3, methodCovered?.TotalInstructions); - Assert.AreEqual(0, methodCovered?.CoveredInstructions); + Assert.AreEqual(3, methodCovered?.TotalLines); + Assert.AreEqual(0, methodCovered?.CoveredLines); // Check coverage by raw method @@ -113,16 +113,16 @@ public void TestCoverageByEngine() Assert.IsNotNull(methodCovered); methodCovered = engine.GetCoverage(engine.Native.NEO, "totalSupply", 0); - Assert.AreEqual(3, methodCovered?.TotalInstructions); - Assert.AreEqual(3, methodCovered?.CoveredInstructions); + Assert.AreEqual(3, methodCovered?.TotalLines); + Assert.AreEqual(3, methodCovered?.CoveredLines); methodCovered = engine.GetCoverage(engine.Native.NEO, "balanceOf", 1); - Assert.AreEqual(3, methodCovered?.TotalInstructions); - Assert.AreEqual(3, methodCovered?.CoveredInstructions); + Assert.AreEqual(3, methodCovered?.TotalLines); + Assert.AreEqual(3, methodCovered?.CoveredLines); methodCovered = engine.GetCoverage(engine.Native.NEO, "transfer", 4); - Assert.AreEqual(3, methodCovered?.TotalInstructions); - Assert.AreEqual(0, methodCovered?.CoveredInstructions); + Assert.AreEqual(3, methodCovered?.TotalLines); + Assert.AreEqual(0, methodCovered?.CoveredLines); } [TestMethod] @@ -145,17 +145,17 @@ public void TestCoverageByExtension() Assert.AreEqual(100_000_000, engine.Native.NEO.TotalSupply); Assert.AreEqual(engine.Native.NEO.Hash, engine.Native.NEO.GetCoverage()?.Hash); - Assert.AreEqual(57, engine.Native.NEO.GetCoverage()?.TotalInstructions); - Assert.AreEqual(3, engine.Native.NEO.GetCoverage()?.CoveredInstructions); - Assert.AreEqual(3, engine.Native.NEO.GetCoverage()?.HitsInstructions); + Assert.AreEqual(57, engine.Native.NEO.GetCoverage()?.TotalLines); + Assert.AreEqual(3, engine.Native.NEO.GetCoverage()?.CoveredLines); + Assert.AreEqual(3, engine.Native.NEO.GetCoverage()?.CoveredLinesAll); // Check balanceOf Assert.AreEqual(0, engine.Native.NEO.BalanceOf(engine.Native.NEO.Hash)); - Assert.AreEqual(57, engine.Native.NEO.GetCoverage()?.TotalInstructions); - Assert.AreEqual(6, engine.Native.NEO.GetCoverage()?.CoveredInstructions); - Assert.AreEqual(6, engine.Native.NEO.GetCoverage()?.HitsInstructions); + Assert.AreEqual(57, engine.Native.NEO.GetCoverage()?.TotalLines); + Assert.AreEqual(6, engine.Native.NEO.GetCoverage()?.CoveredLines); + Assert.AreEqual(6, engine.Native.NEO.GetCoverage()?.CoveredLinesAll); // Check coverage by method and expression @@ -163,16 +163,16 @@ public void TestCoverageByExtension() Assert.IsNotNull(methodCovered); methodCovered = engine.Native.NEO.GetCoverage(o => o.TotalSupply); - Assert.AreEqual(3, methodCovered?.TotalInstructions); - Assert.AreEqual(3, methodCovered?.CoveredInstructions); + Assert.AreEqual(3, methodCovered?.TotalLines); + Assert.AreEqual(3, methodCovered?.CoveredLines); methodCovered = engine.Native.NEO.GetCoverage(o => o.BalanceOf(It.IsAny())); - Assert.AreEqual(3, methodCovered?.TotalInstructions); - Assert.AreEqual(3, methodCovered?.CoveredInstructions); + Assert.AreEqual(3, methodCovered?.TotalLines); + Assert.AreEqual(3, methodCovered?.CoveredLines); methodCovered = engine.Native.NEO.GetCoverage(o => o.Transfer(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())); - Assert.AreEqual(3, methodCovered?.TotalInstructions); - Assert.AreEqual(0, methodCovered?.CoveredInstructions); + Assert.AreEqual(3, methodCovered?.TotalLines); + Assert.AreEqual(0, methodCovered?.CoveredLines); // Check coverage by raw method @@ -180,16 +180,16 @@ public void TestCoverageByExtension() Assert.IsNotNull(methodCovered); methodCovered = engine.GetCoverage(engine.Native.NEO, "totalSupply", 0); - Assert.AreEqual(3, methodCovered?.TotalInstructions); - Assert.AreEqual(3, methodCovered?.CoveredInstructions); + Assert.AreEqual(3, methodCovered?.TotalLines); + Assert.AreEqual(3, methodCovered?.CoveredLines); methodCovered = engine.GetCoverage(engine.Native.NEO, "balanceOf", 1); - Assert.AreEqual(3, methodCovered?.TotalInstructions); - Assert.AreEqual(3, methodCovered?.CoveredInstructions); + Assert.AreEqual(3, methodCovered?.TotalLines); + Assert.AreEqual(3, methodCovered?.CoveredLines); methodCovered = engine.GetCoverage(engine.Native.NEO, "transfer", 4); - Assert.AreEqual(3, methodCovered?.TotalInstructions); - Assert.AreEqual(0, methodCovered?.CoveredInstructions); + Assert.AreEqual(3, methodCovered?.TotalLines); + Assert.AreEqual(0, methodCovered?.CoveredLines); } [TestMethod] diff --git a/tests/Neo.SmartContract.Testing.UnitTests/NativeArtifactsTests.cs b/tests/Neo.SmartContract.Testing.UnitTests/NativeArtifactsTests.cs index ce45fa1bb..4ee79d8ff 100644 --- a/tests/Neo.SmartContract.Testing.UnitTests/NativeArtifactsTests.cs +++ b/tests/Neo.SmartContract.Testing.UnitTests/NativeArtifactsTests.cs @@ -34,9 +34,9 @@ public void TestInitialize() // Check coverage - Assert.AreEqual(1F, engine.Native.NEO.GetCoverage(o => o.Symbol).CoveredPercentage); - Assert.AreEqual(1F, engine.Native.NEO.GetCoverage(o => o.TotalSupply).CoveredPercentage); - Assert.AreEqual(1F, engine.Native.NEO.GetCoverage(o => o.BalanceOf(It.IsAny())).CoveredPercentage); + Assert.AreEqual(1F, engine.Native.NEO.GetCoverage(o => o.Symbol).CoveredLinesPercentage); + Assert.AreEqual(1F, engine.Native.NEO.GetCoverage(o => o.TotalSupply).CoveredLinesPercentage); + Assert.AreEqual(1F, engine.Native.NEO.GetCoverage(o => o.BalanceOf(It.IsAny())).CoveredLinesPercentage); } [TestMethod]