Skip to content

Commit

Permalink
TestEngine: add Checkpoints, RpcStorage and Dump to html (#904)
Browse files Browse the repository at this point in the history
* Draft Checkpoint

* fix namespaces

* Add ut

* Update src/Neo.SmartContract.Testing/Storage/EngineStorage.cs

Co-authored-by: Jimmy <[email protected]>

* Rpc Storage

* Seek

* clean

* By pass RPC Backward

* Change to testnet

* Fix bug

* clean using

* Conflicts

* Dump to html

* Fix coverage during OnFault

* print string when possible

* Coverage 100%

* fix comment

* Update README

* Update src/Neo.SmartContract.Testing/TestingApplicationEngine.cs

* format

* Improve method name

* Refactor AbiMethod constructor

* Update tests/Neo.SmartContract.Template.UnitTests/templates/neocontractnep17/CoverageContractTests.cs

---------

Co-authored-by: Jimmy <[email protected]>
  • Loading branch information
shargon and Jim8y authored Feb 19, 2024
1 parent 8434cb2 commit 470d9a8
Show file tree
Hide file tree
Showing 24 changed files with 987 additions and 91 deletions.
2 changes: 1 addition & 1 deletion src/Neo.Compiler.CSharp/Options.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public enum GenerateArtifactsKind
None,
Source,
Library,
SourceAndLibrary
All
}

public string? Output { get; set; }
Expand Down
4 changes: 2 additions & 2 deletions src/Neo.Compiler.CSharp/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -193,14 +193,14 @@ private static int ProcessOutputs(Options options, string folder, CompilationCon
{
var artifact = manifest.GetArtifactsSource(baseName);

if (options.GenerateArtifacts == Options.GenerateArtifactsKind.SourceAndLibrary || options.GenerateArtifacts == Options.GenerateArtifactsKind.Source)
if (options.GenerateArtifacts == Options.GenerateArtifactsKind.All || options.GenerateArtifacts == Options.GenerateArtifactsKind.Source)
{
path = Path.Combine(outputFolder, $"{baseName}.artifacts.cs");
File.WriteAllText(path, artifact);
Console.WriteLine($"Created {path}");
}

if (options.GenerateArtifacts == Options.GenerateArtifactsKind.SourceAndLibrary || options.GenerateArtifacts == Options.GenerateArtifactsKind.Library)
if (options.GenerateArtifacts == Options.GenerateArtifactsKind.All || options.GenerateArtifacts == Options.GenerateArtifactsKind.Library)
{
try
{
Expand Down
20 changes: 12 additions & 8 deletions src/Neo.SmartContract.Testing/Coverage/AbiMethod.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;

Expand All @@ -9,6 +10,8 @@ namespace Neo.SmartContract.Testing.Coverage
[DebuggerDisplay("{Name},{PCount}")]
public class AbiMethod : IEquatable<AbiMethod>
{
private readonly string _toString;

/// <summary>
/// Method name
/// </summary>
Expand All @@ -23,11 +26,12 @@ public class AbiMethod : IEquatable<AbiMethod>
/// Constructor
/// </summary>
/// <param name="name">Method name</param>
/// <param name="pCount">Parameters count</param>
public AbiMethod(string name, int pCount)
/// <param name="argsName">Arguments names</param>
public AbiMethod(string name, string[] argsName)
{
Name = name;
PCount = pCount;
PCount = argsName.Length;
_toString = name + $"({string.Join(",", argsName)})";
}

/// <summary>
Expand Down Expand Up @@ -55,14 +59,14 @@ public static AbiMethod[] CreateFromExpression(Expression expression)

return new AbiMethod[]
{
new AbiMethod(nameRead, 0),
new AbiMethod(nameWrite, 1)
new AbiMethod(nameRead, Array.Empty<string>()),
new AbiMethod(nameWrite, new string[]{ "value" })
};
}

// Only read property

return new AbiMethod[] { new AbiMethod(nameRead, 0) };
return new AbiMethod[] { new AbiMethod(nameRead, Array.Empty<string>()) };
}
}
}
Expand All @@ -73,7 +77,7 @@ public static AbiMethod[] CreateFromExpression(Expression expression)
var display = mInfo.GetCustomAttribute<DisplayNameAttribute>();
var name = display is not null ? display.DisplayName : mInfo.Name;

return new AbiMethod[] { new AbiMethod(name, mInfo.GetParameters().Length) };
return new AbiMethod[] { new AbiMethod(name, mInfo.GetParameters().Select(u => u.Name ?? "arg").ToArray()) };
}
}

Expand All @@ -89,6 +93,6 @@ public override bool Equals(object obj)

bool IEquatable<AbiMethod>.Equals(AbiMethod other) => PCount == other.PCount && Name == other.Name;

Check warning on line 94 in src/Neo.SmartContract.Testing/Coverage/AbiMethod.cs

View workflow job for this annotation

GitHub Actions / Test

Nullability of reference types in type of parameter 'other' doesn't match implemented member 'bool IEquatable<AbiMethod>.Equals(AbiMethod? other)' (possibly because of nullability attributes).

Check warning on line 94 in src/Neo.SmartContract.Testing/Coverage/AbiMethod.cs

View workflow job for this annotation

GitHub Actions / PublishGithub

Nullability of reference types in type of parameter 'other' doesn't match implemented member 'bool IEquatable<AbiMethod>.Equals(AbiMethod? other)' (possibly because of nullability attributes).
public override int GetHashCode() => HashCode.Combine(PCount, Name);
public override string ToString() => $"{Name},{PCount}";
public override string ToString() => _toString;
}
}
70 changes: 66 additions & 4 deletions src/Neo.SmartContract.Testing/Coverage/CoverageHit.cs
Original file line number Diff line number Diff line change
@@ -1,16 +1,23 @@
using Neo.VM;
using System;
using System.Diagnostics;
using System.Text.RegularExpressions;

namespace Neo.SmartContract.Testing.Coverage
{
[DebuggerDisplay("Offset:{Offset}, OutOfScript:{OutOfScript}, Hits:{Hits}, GasTotal:{GasTotal}, GasMin:{GasMin}, GasMax:{GasMax}, GasAvg:{GasAvg}")]
[DebuggerDisplay("Offset:{Offset}, Description:{Description}, OutOfScript:{OutOfScript}, Hits:{Hits}, GasTotal:{GasTotal}, GasMin:{GasMin}, GasMax:{GasMax}, GasAvg:{GasAvg}")]
public class CoverageHit
{
/// <summary>
/// The instruction offset
/// </summary>
public int Offset { get; }

/// <summary>
/// The instruction description
/// </summary>
public string Description { get; }

/// <summary>
/// The instruction is out of the script
/// </summary>
Expand Down Expand Up @@ -45,10 +52,12 @@ public class CoverageHit
/// Constructor
/// </summary>
/// <param name="offset">Offset</param>
/// <param name="description">Decription</param>
/// <param name="outOfScript">Out of script</param>
public CoverageHit(int offset, bool outOfScript = false)
public CoverageHit(int offset, string description, bool outOfScript = false)
{
Offset = offset;
Description = description;
OutOfScript = outOfScript;
}

Expand Down Expand Up @@ -104,7 +113,7 @@ public void Hit(CoverageHit value)
/// <returns>CoverageData</returns>
public CoverageHit Clone()
{
return new CoverageHit(Offset, OutOfScript)
return new CoverageHit(Offset, Description, OutOfScript)
{
GasMax = GasMax,
GasMin = GasMin,
Expand All @@ -113,13 +122,66 @@ public CoverageHit Clone()
};
}

/// <summary>
/// Return description from instruction
/// </summary>
/// <param name="instruction">Instruction</param>
/// <returns>Description</returns>
public static string DescriptionFromInstruction(Instruction instruction)
{
if (instruction.Operand.Length > 0)
{
var ret = instruction.OpCode.ToString() + " 0x" + instruction.Operand.ToArray().ToHexString();

switch (instruction.OpCode)
{
case OpCode.JMP:
case OpCode.JMPIF:
case OpCode.JMPIFNOT:
case OpCode.JMPEQ:
case OpCode.JMPNE:
case OpCode.JMPGT:
case OpCode.JMPGE:
case OpCode.JMPLT:
case OpCode.JMPLE: return ret + $" ({instruction.TokenI8})";
case OpCode.JMP_L:
case OpCode.JMPIF_L:
case OpCode.JMPIFNOT_L:
case OpCode.JMPEQ_L:
case OpCode.JMPNE_L:
case OpCode.JMPGT_L:
case OpCode.JMPGE_L:
case OpCode.JMPLT_L:
case OpCode.JMPLE_L: return ret + $" ({instruction.TokenI32})";
case OpCode.SYSCALL:
{
if (ApplicationEngine.Services.TryGetValue(instruction.TokenU32, out var syscall))
{
return ret + $" ('{syscall.Name}')";
}

return ret;
}
}

if (instruction.Operand.Span.TryGetString(out var str) && Regex.IsMatch(str, @"^[a-zA-Z0-9_]+$"))

Check warning on line 167 in src/Neo.SmartContract.Testing/Coverage/CoverageHit.cs

View workflow job for this annotation

GitHub Actions / Test

Possible null reference argument for parameter 'input' in 'bool Regex.IsMatch(string input, string pattern)'.

Check warning on line 167 in src/Neo.SmartContract.Testing/Coverage/CoverageHit.cs

View workflow job for this annotation

GitHub Actions / PublishGithub

Possible null reference argument for parameter 'input' in 'bool Regex.IsMatch(string input, string pattern)'.
{
return ret + $" '{str}'";
}

return ret;
}

return instruction.OpCode.ToString();
}

/// <summary>
/// String representation
/// </summary>
/// <returns></returns>
public override string ToString()
{
return $"Offset:{Offset}, OutOfScript:{OutOfScript}, Hits:{Hits}, GasTotal:{GasTotal}, GasMin:{GasMin}, GasMax:{GasMax}, GasAvg:{GasAvg}";
return $"Offset:{Offset}, Description:{Description}, OutOfScript:{OutOfScript}, Hits:{Hits}, GasTotal:{GasTotal}, GasMin:{GasMin}, GasMax:{GasMax}, GasAvg:{GasAvg}";
}
}
}
Loading

0 comments on commit 470d9a8

Please sign in to comment.