Skip to content

Commit 7c38ae0

Browse files
authored
TestEngine: Some fixes in TestEngine (#934)
* Fix mock properties * Simplify class conversion * Add value types and fix namespace
1 parent afdfa2b commit 7c38ae0

File tree

18 files changed

+198
-68
lines changed

18 files changed

+198
-68
lines changed
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
using System;
2+
3+
namespace Neo.SmartContract.Testing.Attributes;
4+
5+
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
6+
public sealed class FieldOrderAttribute : Attribute
7+
{
8+
/// <summary>
9+
/// Gets the deserialization order of the property.
10+
/// </summary>
11+
public int Order { get; }
12+
13+
/// <summary>
14+
/// Constructor
15+
/// </summary>
16+
/// <param name="order">Order</param>
17+
public FieldOrderAttribute(int order)
18+
{
19+
Order = order;
20+
}
21+
}

src/Neo.SmartContract.Testing/Extensions/TestExtensions.cs

Lines changed: 102 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1+
using Akka.Util;
12
using Neo.Cryptography.ECC;
2-
using Neo.SmartContract.Iterators;
3+
using Neo.SmartContract.Testing.Attributes;
34
using Neo.VM.Types;
45
using System;
56
using System.Collections.Generic;
@@ -11,6 +12,9 @@ namespace Neo.SmartContract.Testing.Extensions
1112
{
1213
public static class TestExtensions
1314
{
15+
private static readonly Dictionary<Type, Dictionary<int, PropertyInfo>> _propertyCache = new();
16+
private static readonly Dictionary<Type, FieldInfo[]> _fieldCache = new();
17+
1418
/// <summary>
1519
/// Convert Array stack item to dotnet array
1620
/// </summary>
@@ -75,18 +79,84 @@ public static class TestExtensions
7579
_ when type == typeof(UInt160) => new UInt160(stackItem.GetSpan().ToArray()),
7680
_ when type == typeof(UInt256) => new UInt256(stackItem.GetSpan().ToArray()),
7781
_ when type == typeof(ECPoint) => ECPoint.FromBytes(stackItem.GetSpan().ToArray(), ECCurve.Secp256r1),
78-
_ when type == typeof(IDictionary<object, object>) && stackItem is Map mp => ToDictionary(mp), // SubItems in StackItem type
79-
_ when type == typeof(Dictionary<object, object>) && stackItem is Map mp => ToDictionary(mp), // SubItems in StackItem type
80-
_ when type == typeof(IList<object>) && stackItem is CompoundType cp => new List<object>(cp.SubItems), // SubItems in StackItem type
81-
_ when type == typeof(List<object>) && stackItem is CompoundType cp => new List<object>(cp.SubItems), // SubItems in StackItem type
8282
_ when typeof(IInteroperable).IsAssignableFrom(type) => CreateInteroperable(stackItem, type),
83-
_ when type.IsArray && stackItem is CompoundType cp => CreateTypeArray(cp.SubItems, type.GetElementType()!),
8483
_ when stackItem is InteropInterface it && it.GetInterface().GetType() == type => it.GetInterface(),
8584

85+
_ when stackItem is VM.Types.Array ar => type switch
86+
{
87+
_ when type == typeof(IList<object>) => new List<object>(ar.SubItems), // SubItems in StackItem type
88+
_ when type == typeof(List<object>) => new List<object>(ar.SubItems), // SubItems in StackItem type
89+
_ when type.IsArray => CreateTypeArray(ar.SubItems, type.GetElementType()!),
90+
_ when type.IsClass => CreateObject(ar.SubItems, type),
91+
_ when type.IsValueType => CreateValueType(ar.SubItems, type),
92+
_ => throw new FormatException($"Impossible to convert {stackItem} to {type}"),
93+
},
94+
_ when stackItem is Map mp => type switch
95+
{
96+
_ when type == typeof(IDictionary<object, object>) => ToDictionary(mp), // SubItems in StackItem type
97+
_ when type == typeof(Dictionary<object, object>) => ToDictionary(mp), // SubItems in StackItem type
98+
_ => throw new FormatException($"Impossible to convert {stackItem} to {type}"),
99+
},
100+
86101
_ => throw new FormatException($"Impossible to convert {stackItem} to {type}"),
87102
};
88103
}
89104

105+
private static object CreateObject(IEnumerable<StackItem> subItems, Type type)
106+
{
107+
var index = 0;
108+
var obj = Activator.CreateInstance(type) ?? throw new FormatException($"Impossible create {type}");
109+
110+
// Cache the object properties by offset
111+
112+
if (!_propertyCache.TryGetValue(type, out var cache))
113+
{
114+
cache = new Dictionary<int, PropertyInfo>();
115+
116+
foreach (var property in type.GetProperties())
117+
{
118+
var fieldOffset = property.GetCustomAttribute<FieldOrderAttribute>();
119+
if (fieldOffset is null) continue;
120+
if (!property.CanWrite) continue;
121+
122+
cache.Add(fieldOffset.Order, property);
123+
}
124+
125+
if (cache.Count == 0)
126+
{
127+
// Without FieldOrderAttribute, by order
128+
129+
foreach (var property in type.GetProperties())
130+
{
131+
if (!property.CanWrite) continue;
132+
cache.Add(index, property);
133+
index++;
134+
}
135+
index = 0;
136+
}
137+
138+
_propertyCache[type] = cache;
139+
}
140+
141+
// Fill the object
142+
143+
foreach (var item in subItems)
144+
{
145+
if (cache.TryGetValue(index, out var property))
146+
{
147+
property.SetValue(obj, ConvertTo(item, property.PropertyType));
148+
}
149+
else
150+
{
151+
throw new FormatException($"Error converting {type}, the property with the offset {index} was not found.");
152+
}
153+
154+
index++;
155+
}
156+
157+
return obj;
158+
}
159+
90160
private static IDictionary<object, object> ToDictionary(Map map)
91161
{
92162
Dictionary<object, object> dictionary = new();
@@ -99,6 +169,32 @@ private static IDictionary<object, object> ToDictionary(Map map)
99169
return dictionary;
100170
}
101171

172+
private static object CreateValueType(IEnumerable<StackItem> objects, Type valueType)
173+
{
174+
var arr = objects.ToArray();
175+
var value = Activator.CreateInstance(valueType);
176+
177+
// Cache the object properties by offset
178+
179+
if (!_fieldCache.TryGetValue(valueType, out var cache))
180+
{
181+
cache = valueType.GetFields().ToArray();
182+
_fieldCache[valueType] = cache;
183+
}
184+
185+
if (cache.Length != arr.Length)
186+
{
187+
throw new FormatException($"Error converting {valueType}, field count doesn't match.");
188+
}
189+
190+
for (int x = 0; x < arr.Length; x++)
191+
{
192+
cache[x].SetValue(value, ConvertTo(arr[x], cache[x].FieldType));
193+
}
194+
195+
return value;
196+
}
197+
102198
private static object CreateTypeArray(IEnumerable<StackItem> objects, Type elementType)
103199
{
104200
var obj = objects.ToArray();

src/Neo.SmartContract.Testing/Native/ContractManagement.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
using System.ComponentModel;
33
using System.Numerics;
44

5-
namespace Neo.SmartContract.Testing;
5+
namespace Neo.SmartContract.Testing.Native;
66

77
public abstract class ContractManagement : SmartContract
88
{

src/Neo.SmartContract.Testing/Native/CryptoLib.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
using System.ComponentModel;
22
using System.Numerics;
33

4-
namespace Neo.SmartContract.Testing;
4+
namespace Neo.SmartContract.Testing.Native;
55

66
public abstract class CryptoLib : SmartContract
77
{

src/Neo.SmartContract.Testing/Native/GasToken.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
using System.ComponentModel;
22
using System.Numerics;
33

4-
namespace Neo.SmartContract.Testing;
4+
namespace Neo.SmartContract.Testing.Native;
55

66
public abstract class GasToken : SmartContract
77
{

src/Neo.SmartContract.Testing/Native/LedgerContract.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
using System.ComponentModel;
55
using System.Numerics;
66

7-
namespace Neo.SmartContract.Testing;
7+
namespace Neo.SmartContract.Testing.Native;
88

99
public abstract class LedgerContract : SmartContract
1010
{

src/Neo.SmartContract.Testing/Native/NeoToken.cs

Lines changed: 8 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,26 @@
11
using Neo.Cryptography.ECC;
2-
using Neo.IO;
32
using Neo.SmartContract.Iterators;
4-
using Neo.VM;
5-
using Neo.VM.Types;
6-
using System;
3+
using Neo.SmartContract.Testing.Attributes;
74
using System.ComponentModel;
85
using System.Numerics;
9-
using Neo.SmartContract.Testing.Extensions;
10-
using System.Linq;
116

12-
namespace Neo.SmartContract.Testing;
7+
namespace Neo.SmartContract.Testing.Native;
138

149
public abstract class NeoToken : SmartContract
1510
{
16-
public class Candidate : IInteroperable
11+
public class Candidate
1712
{
1813
/// <summary>
1914
/// Public key
2015
/// </summary>
21-
public ECPoint? PublicKey { get; private set; }
16+
[FieldOrder(0)]
17+
public ECPoint? PublicKey { get; set; }
2218

2319
/// <summary>
2420
/// Votes
2521
/// </summary>
26-
public BigInteger Votes { get; private set; } = BigInteger.Zero;
27-
28-
public void FromStackItem(StackItem stackItem)
29-
{
30-
if (stackItem is not CompoundType cp) throw new FormatException();
31-
if (cp.Count < 2) throw new FormatException();
32-
33-
var items = cp.SubItems.ToArray();
34-
35-
PublicKey = (ECPoint)items[0].ConvertTo(typeof(ECPoint))!;
36-
Votes = (BigInteger)items[1].ConvertTo(typeof(BigInteger))!;
37-
}
38-
39-
public StackItem ToStackItem(ReferenceCounter referenceCounter)
40-
{
41-
return new VM.Types.Array(new StackItem[] { PublicKey.ToArray(), Votes });
42-
}
22+
[FieldOrder(1)]
23+
public BigInteger Votes { get; set; }
4324
}
4425

4526
#region Events
@@ -119,7 +100,7 @@ public StackItem ToStackItem(ReferenceCounter referenceCounter)
119100
/// Safe method
120101
/// </summary>
121102
[DisplayName("getAccountState")]
122-
public abstract Native.NeoToken.NeoAccountState GetAccountState(UInt160? account);
103+
public abstract Neo.SmartContract.Native.NeoToken.NeoAccountState GetAccountState(UInt160? account);
123104

124105
/// <summary>
125106
/// Safe method

src/Neo.SmartContract.Testing/Native/OracleContract.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
using System.ComponentModel;
22
using System.Numerics;
33

4-
namespace Neo.SmartContract.Testing;
4+
namespace Neo.SmartContract.Testing.Native;
55

66
public abstract class OracleContract : SmartContract
77
{

src/Neo.SmartContract.Testing/Native/PolicyContract.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
using System.ComponentModel;
22
using System.Numerics;
33

4-
namespace Neo.SmartContract.Testing;
4+
namespace Neo.SmartContract.Testing.Native;
55

66
public abstract class PolicyContract : SmartContract
77
{

src/Neo.SmartContract.Testing/Native/RoleManagement.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
using System.ComponentModel;
44
using System.Numerics;
55

6-
namespace Neo.SmartContract.Testing;
6+
namespace Neo.SmartContract.Testing.Native;
77

88
public abstract class RoleManagement : SmartContract
99
{

0 commit comments

Comments
 (0)