Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TestEngine: Some fixes in TestEngine #934

Merged
merged 4 commits into from
Feb 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions src/Neo.SmartContract.Testing/Attributes/FieldOrderAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using System;

namespace Neo.SmartContract.Testing.Attributes;

[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public sealed class FieldOrderAttribute : Attribute
{
/// <summary>
/// Gets the deserialization order of the property.
/// </summary>
public int Order { get; }

/// <summary>
/// Constructor
/// </summary>
/// <param name="order">Order</param>
public FieldOrderAttribute(int order)
{
Order = order;
}
}
108 changes: 102 additions & 6 deletions src/Neo.SmartContract.Testing/Extensions/TestExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using Akka.Util;
using Neo.Cryptography.ECC;
using Neo.SmartContract.Iterators;
using Neo.SmartContract.Testing.Attributes;
using Neo.VM.Types;
using System;
using System.Collections.Generic;
Expand All @@ -11,6 +12,9 @@
{
public static class TestExtensions
{
private static readonly Dictionary<Type, Dictionary<int, PropertyInfo>> _propertyCache = new();
private static readonly Dictionary<Type, FieldInfo[]> _fieldCache = new();

/// <summary>
/// Convert Array stack item to dotnet array
/// </summary>
Expand Down Expand Up @@ -75,18 +79,84 @@
_ when type == typeof(UInt160) => new UInt160(stackItem.GetSpan().ToArray()),
_ when type == typeof(UInt256) => new UInt256(stackItem.GetSpan().ToArray()),
_ when type == typeof(ECPoint) => ECPoint.FromBytes(stackItem.GetSpan().ToArray(), ECCurve.Secp256r1),
_ when type == typeof(IDictionary<object, object>) && stackItem is Map mp => ToDictionary(mp), // SubItems in StackItem type
_ when type == typeof(Dictionary<object, object>) && stackItem is Map mp => ToDictionary(mp), // SubItems in StackItem type
_ when type == typeof(IList<object>) && stackItem is CompoundType cp => new List<object>(cp.SubItems), // SubItems in StackItem type
_ when type == typeof(List<object>) && stackItem is CompoundType cp => new List<object>(cp.SubItems), // SubItems in StackItem type
_ when typeof(IInteroperable).IsAssignableFrom(type) => CreateInteroperable(stackItem, type),
_ when type.IsArray && stackItem is CompoundType cp => CreateTypeArray(cp.SubItems, type.GetElementType()!),
_ when stackItem is InteropInterface it && it.GetInterface().GetType() == type => it.GetInterface(),

_ when stackItem is VM.Types.Array ar => type switch
{
_ when type == typeof(IList<object>) => new List<object>(ar.SubItems), // SubItems in StackItem type
_ when type == typeof(List<object>) => new List<object>(ar.SubItems), // SubItems in StackItem type
_ when type.IsArray => CreateTypeArray(ar.SubItems, type.GetElementType()!),
_ when type.IsClass => CreateObject(ar.SubItems, type),
_ when type.IsValueType => CreateValueType(ar.SubItems, type),
_ => throw new FormatException($"Impossible to convert {stackItem} to {type}"),
},
_ when stackItem is Map mp => type switch
{
_ when type == typeof(IDictionary<object, object>) => ToDictionary(mp), // SubItems in StackItem type
_ when type == typeof(Dictionary<object, object>) => ToDictionary(mp), // SubItems in StackItem type
_ => throw new FormatException($"Impossible to convert {stackItem} to {type}"),
},

_ => throw new FormatException($"Impossible to convert {stackItem} to {type}"),
};
}

private static object CreateObject(IEnumerable<StackItem> subItems, Type type)
{
var index = 0;
var obj = Activator.CreateInstance(type) ?? throw new FormatException($"Impossible create {type}");

// Cache the object properties by offset

if (!_propertyCache.TryGetValue(type, out var cache))
{
cache = new Dictionary<int, PropertyInfo>();

foreach (var property in type.GetProperties())
{
var fieldOffset = property.GetCustomAttribute<FieldOrderAttribute>();
if (fieldOffset is null) continue;
if (!property.CanWrite) continue;

cache.Add(fieldOffset.Order, property);
}

if (cache.Count == 0)
{
// Without FieldOrderAttribute, by order

foreach (var property in type.GetProperties())
{
if (!property.CanWrite) continue;
cache.Add(index, property);
index++;
}
index = 0;
}

_propertyCache[type] = cache;
}

// Fill the object

foreach (var item in subItems)
{
if (cache.TryGetValue(index, out var property))
{
property.SetValue(obj, ConvertTo(item, property.PropertyType));
}
else
{
throw new FormatException($"Error converting {type}, the property with the offset {index} was not found.");
}

index++;
}

return obj;
}

private static IDictionary<object, object> ToDictionary(Map map)
{
Dictionary<object, object> dictionary = new();
Expand All @@ -99,6 +169,32 @@
return dictionary;
}

private static object CreateValueType(IEnumerable<StackItem> objects, Type valueType)
{
var arr = objects.ToArray();
var value = Activator.CreateInstance(valueType);

// Cache the object properties by offset

if (!_fieldCache.TryGetValue(valueType, out var cache))
{
cache = valueType.GetFields().ToArray();
_fieldCache[valueType] = cache;
}

if (cache.Length != arr.Length)
{
throw new FormatException($"Error converting {valueType}, field count doesn't match.");
}

for (int x = 0; x < arr.Length; x++)
{
cache[x].SetValue(value, ConvertTo(arr[x], cache[x].FieldType));
}

return value;

Check warning on line 195 in src/Neo.SmartContract.Testing/Extensions/TestExtensions.cs

View workflow job for this annotation

GitHub Actions / Test

Possible null reference return.
}

private static object CreateTypeArray(IEnumerable<StackItem> objects, Type elementType)
{
var obj = objects.ToArray();
Expand Down
2 changes: 1 addition & 1 deletion src/Neo.SmartContract.Testing/Native/ContractManagement.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
using System.ComponentModel;
using System.Numerics;

namespace Neo.SmartContract.Testing;
namespace Neo.SmartContract.Testing.Native;

public abstract class ContractManagement : SmartContract
{
Expand All @@ -11,11 +11,11 @@
public delegate void delDeploy(UInt160 Hash);

[DisplayName("Deploy")]
public event delDeploy? OnDeploy;

Check warning on line 14 in src/Neo.SmartContract.Testing/Native/ContractManagement.cs

View workflow job for this annotation

GitHub Actions / Test

The event 'ContractManagement.OnDeploy' is never used
public delegate void delDestroy(UInt160 Hash);

[DisplayName("Destroy")]
public event delDestroy? OnDestroy;

Check warning on line 18 in src/Neo.SmartContract.Testing/Native/ContractManagement.cs

View workflow job for this annotation

GitHub Actions / Test

The event 'ContractManagement.OnDestroy' is never used
public delegate void delUpdate(UInt160 Hash);

[DisplayName("Update")]
Expand Down
2 changes: 1 addition & 1 deletion src/Neo.SmartContract.Testing/Native/CryptoLib.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
using System.ComponentModel;
using System.Numerics;

namespace Neo.SmartContract.Testing;
namespace Neo.SmartContract.Testing.Native;

public abstract class CryptoLib : SmartContract
{
Expand Down
2 changes: 1 addition & 1 deletion src/Neo.SmartContract.Testing/Native/GasToken.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
using System.ComponentModel;
using System.Numerics;

namespace Neo.SmartContract.Testing;
namespace Neo.SmartContract.Testing.Native;

public abstract class GasToken : SmartContract
{
Expand Down
2 changes: 1 addition & 1 deletion src/Neo.SmartContract.Testing/Native/LedgerContract.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
using System.ComponentModel;
using System.Numerics;

namespace Neo.SmartContract.Testing;
namespace Neo.SmartContract.Testing.Native;

public abstract class LedgerContract : SmartContract
{
Expand Down
35 changes: 8 additions & 27 deletions src/Neo.SmartContract.Testing/Native/NeoToken.cs
Original file line number Diff line number Diff line change
@@ -1,52 +1,33 @@
using Neo.Cryptography.ECC;
using Neo.IO;
using Neo.SmartContract.Iterators;
using Neo.VM;
using Neo.VM.Types;
using System;
using Neo.SmartContract.Testing.Attributes;
using System.ComponentModel;
using System.Numerics;
using Neo.SmartContract.Testing.Extensions;
using System.Linq;

namespace Neo.SmartContract.Testing;
namespace Neo.SmartContract.Testing.Native;

public abstract class NeoToken : SmartContract
{
public class Candidate : IInteroperable
public class Candidate
{
/// <summary>
/// Public key
/// </summary>
public ECPoint? PublicKey { get; private set; }
[FieldOrder(0)]
public ECPoint? PublicKey { get; set; }

/// <summary>
/// Votes
/// </summary>
public BigInteger Votes { get; private set; } = BigInteger.Zero;

public void FromStackItem(StackItem stackItem)
{
if (stackItem is not CompoundType cp) throw new FormatException();
if (cp.Count < 2) throw new FormatException();

var items = cp.SubItems.ToArray();

PublicKey = (ECPoint)items[0].ConvertTo(typeof(ECPoint))!;
Votes = (BigInteger)items[1].ConvertTo(typeof(BigInteger))!;
}

public StackItem ToStackItem(ReferenceCounter referenceCounter)
{
return new VM.Types.Array(new StackItem[] { PublicKey.ToArray(), Votes });
}
[FieldOrder(1)]
public BigInteger Votes { get; set; }
}

#region Events

public delegate void delCandidateStateChanged(ECPoint pubkey, bool registered, BigInteger votes);
[DisplayName("CandidateStateChanged")]
public event delCandidateStateChanged? OnCandidateStateChanged;

Check warning on line 30 in src/Neo.SmartContract.Testing/Native/NeoToken.cs

View workflow job for this annotation

GitHub Actions / Test

The event 'NeoToken.OnCandidateStateChanged' is never used

public delegate void delTransfer(UInt160 from, UInt160 to, BigInteger amount);
[DisplayName("Transfer")]
Expand Down Expand Up @@ -119,7 +100,7 @@
/// Safe method
/// </summary>
[DisplayName("getAccountState")]
public abstract Native.NeoToken.NeoAccountState GetAccountState(UInt160? account);
public abstract Neo.SmartContract.Native.NeoToken.NeoAccountState GetAccountState(UInt160? account);

/// <summary>
/// Safe method
Expand Down
2 changes: 1 addition & 1 deletion src/Neo.SmartContract.Testing/Native/OracleContract.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
using System.ComponentModel;
using System.Numerics;

namespace Neo.SmartContract.Testing;
namespace Neo.SmartContract.Testing.Native;

public abstract class OracleContract : SmartContract
{
Expand Down
2 changes: 1 addition & 1 deletion src/Neo.SmartContract.Testing/Native/PolicyContract.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
using System.ComponentModel;
using System.Numerics;

namespace Neo.SmartContract.Testing;
namespace Neo.SmartContract.Testing.Native;

public abstract class PolicyContract : SmartContract
{
Expand Down
2 changes: 1 addition & 1 deletion src/Neo.SmartContract.Testing/Native/RoleManagement.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
using System.ComponentModel;
using System.Numerics;

namespace Neo.SmartContract.Testing;
namespace Neo.SmartContract.Testing.Native;

public abstract class RoleManagement : SmartContract
{
Expand Down
2 changes: 1 addition & 1 deletion src/Neo.SmartContract.Testing/Native/StdLib.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
using System.ComponentModel;
using System.Numerics;

namespace Neo.SmartContract.Testing;
namespace Neo.SmartContract.Testing.Native;

public abstract class StdLib : SmartContract
{
Expand Down
29 changes: 15 additions & 14 deletions src/Neo.SmartContract.Testing/NativeArtifacts.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Neo.Persistence;
using Neo.SmartContract.Testing.Native;
using System;
using System.Reflection;

Expand Down Expand Up @@ -64,15 +65,15 @@ public NativeArtifacts(TestEngine engine)
{
_engine = engine;

ContractManagement = _engine.FromHash<ContractManagement>(Native.NativeContract.ContractManagement.Hash, Native.NativeContract.ContractManagement.Id);
CryptoLib = _engine.FromHash<CryptoLib>(Native.NativeContract.CryptoLib.Hash, Native.NativeContract.CryptoLib.Id);
GAS = _engine.FromHash<GasToken>(Native.NativeContract.GAS.Hash, Native.NativeContract.GAS.Id);
NEO = _engine.FromHash<NeoToken>(Native.NativeContract.NEO.Hash, Native.NativeContract.NEO.Id);
Ledger = _engine.FromHash<LedgerContract>(Native.NativeContract.Ledger.Hash, Native.NativeContract.Ledger.Id);
Oracle = _engine.FromHash<OracleContract>(Native.NativeContract.Oracle.Hash, Native.NativeContract.Oracle.Id);
Policy = _engine.FromHash<PolicyContract>(Native.NativeContract.Policy.Hash, Native.NativeContract.Policy.Id);
RoleManagement = _engine.FromHash<RoleManagement>(Native.NativeContract.RoleManagement.Hash, Native.NativeContract.RoleManagement.Id);
StdLib = _engine.FromHash<StdLib>(Native.NativeContract.StdLib.Hash, Native.NativeContract.StdLib.Id);
ContractManagement = _engine.FromHash<ContractManagement>(Neo.SmartContract.Native.NativeContract.ContractManagement.Hash, Neo.SmartContract.Native.NativeContract.ContractManagement.Id);
CryptoLib = _engine.FromHash<CryptoLib>(Neo.SmartContract.Native.NativeContract.CryptoLib.Hash, Neo.SmartContract.Native.NativeContract.CryptoLib.Id);
GAS = _engine.FromHash<GasToken>(Neo.SmartContract.Native.NativeContract.GAS.Hash, Neo.SmartContract.Native.NativeContract.GAS.Id);
NEO = _engine.FromHash<NeoToken>(Neo.SmartContract.Native.NativeContract.NEO.Hash, Neo.SmartContract.Native.NativeContract.NEO.Id);
Ledger = _engine.FromHash<LedgerContract>(Neo.SmartContract.Native.NativeContract.Ledger.Hash, Neo.SmartContract.Native.NativeContract.Ledger.Id);
Oracle = _engine.FromHash<OracleContract>(Neo.SmartContract.Native.NativeContract.Oracle.Hash, Neo.SmartContract.Native.NativeContract.Oracle.Id);
Policy = _engine.FromHash<PolicyContract>(Neo.SmartContract.Native.NativeContract.Policy.Hash, Neo.SmartContract.Native.NativeContract.Policy.Id);
RoleManagement = _engine.FromHash<RoleManagement>(Neo.SmartContract.Native.NativeContract.RoleManagement.Hash, Neo.SmartContract.Native.NativeContract.RoleManagement.Id);
StdLib = _engine.FromHash<StdLib>(Neo.SmartContract.Native.NativeContract.StdLib.Hash, Neo.SmartContract.Native.NativeContract.StdLib.Id);
}

/// <summary>
Expand All @@ -92,12 +93,12 @@ public void Initialize(bool commit = false)

// Process native contracts

foreach (var native in new Native.NativeContract[]
foreach (var native in new Neo.SmartContract.Native.NativeContract[]
{
Native.NativeContract.ContractManagement,
Native.NativeContract.Ledger,
Native.NativeContract.NEO,
Native.NativeContract.GAS
Neo.SmartContract.Native.NativeContract.ContractManagement,
Neo.SmartContract.Native.NativeContract.Ledger,
Neo.SmartContract.Native.NativeContract.NEO,
Neo.SmartContract.Native.NativeContract.GAS
}
)
{
Expand Down
2 changes: 1 addition & 1 deletion src/Neo.SmartContract.Testing/Storage/EngineStorage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ namespace Neo.SmartContract.Testing.Storage
public class EngineStorage
{
// Key to check if native contracts are initialized, by default: Neo.votersCountPrefix
private static readonly StorageKey _initKey = new() { Id = Native.NativeContract.NEO.Id, Key = new byte[] { 1 } };
private static readonly StorageKey _initKey = new() { Id = Neo.SmartContract.Native.NativeContract.NEO.Id, Key = new byte[] { 1 } };

/// <summary>
/// Store
Expand Down
5 changes: 4 additions & 1 deletion src/Neo.SmartContract.Testing/TestEngine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
using Neo.VM.Types;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Linq.Expressions;
Expand Down Expand Up @@ -394,7 +395,9 @@ private T MockContract<T>(UInt160 hash, int? contractId = null, Action<Mock<T>>?

if (mock.IsMocked(method))
{
var mockName = method.Name + ";" + method.GetParameters().Length;
var display = method.GetCustomAttribute<DisplayNameAttribute>();
var name = display is not null ? display.DisplayName : method.Name;
var mockName = name + ";" + method.GetParameters().Length;
var cm = new CustomMock(mock.Object, method);

if (_customMocks.TryGetValue(hash, out var mocks))
Expand Down
3 changes: 2 additions & 1 deletion src/Neo.SmartContract.Testing/TestingApplicationEngine.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using Neo.Network.P2P.Payloads;
using Neo.Persistence;
using Neo.SmartContract.Native;
using Neo.SmartContract.Testing.Extensions;
using Neo.VM;
using Neo.VM.Types;
Expand Down Expand Up @@ -76,7 +77,7 @@ private void RecoverCoverage(Instruction instruction)
{
// We need the contract state without pay gas

var state = Native.NativeContract.ContractManagement.GetContract(Engine.Storage.Snapshot, contractHash);
var state = NativeContract.ContractManagement.GetContract(Engine.Storage.Snapshot, contractHash);

coveredContract = new(Engine.MethodDetection, contractHash, state);
Engine.Coverage[contractHash] = coveredContract;
Expand Down
Loading
Loading