Skip to content

Commit 0837784

Browse files
shargoncschuchardt88
authored andcommitted
TestEngine: Fix some snapshots problems (neo-project#941)
* Fix some snapshots problems * Clean ut * Update src/Neo.SmartContract.Testing/TestingApplicationEngine.cs * clean method * Update Readme * Apply suggestions from code review * Fix method name * format * replace interface
1 parent dfafba0 commit 0837784

File tree

7 files changed

+126
-42
lines changed

7 files changed

+126
-42
lines changed

src/Neo.SmartContract.Testing/README.md

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,7 @@ Assert.AreEqual(123, neo.BalanceOf(engine.ValidatorsAddress));
242242
243243
using (ScriptBuilder script = new())
244244
{
245-
script.EmitDynamicCall(neo.Hash, nameof(neo.BalanceOf), engine.ValidatorsAddress);
245+
script.EmitDynamicCall(neo.Hash, "balanceOf", engine.ValidatorsAddress);
246246

247247
Assert.AreEqual(123, engine.Execute(script.ToArray()).GetInteger());
248248
}
@@ -416,7 +416,53 @@ public class CoverageContractTests
416416
}
417417
```
418418

419-
Keep in mind that the coverage is at the instruction level.
419+
Keep in mind that the coverage is at the instruction level, but you can also get the project's coverage on the source code using the debug file (`*.nefdbgnfo`) generated by `Neo.Compiler.CSharp`. To do this, you need to compile the project with the `-d` or `--debug` argument, and set up a unit test like the following:
420+
421+
```csharp
422+
[TestClass]
423+
public class CoverageContractTests
424+
{
425+
/// <summary>
426+
/// Required coverage to be success
427+
/// </summary>
428+
public static decimal RequiredCoverage { get; set; } = 1M;
429+
430+
[AssemblyCleanup]
431+
public static void EnsureCoverage()
432+
{
433+
// Join here all of your coverage sources
434+
435+
var coverage = Nep17ContractTests.Coverage;
436+
coverage?.Join(OwnerContractTests.Coverage);
437+
438+
// Dump coverage to console
439+
440+
Assert.IsNotNull(coverage, "Coverage can't be null");
441+
Console.WriteLine(coverage.Dump());
442+
443+
// Write basic instruction html coverage
444+
445+
File.WriteAllText("coverage.instruction.html", coverage.Dump(DumpFormat.Html));
446+
447+
// Load our debug file
448+
449+
if (NeoDebugInfo.TryLoad("templates/neocontractnep17/Artifacts/Nep17Contract.nefdbgnfo", out var dbg))
450+
{
451+
// Write the cobertura format
452+
453+
File.WriteAllText("coverage.cobertura.xml", coverage.Dump(new CoberturaFormat((coverage, dbg))));
454+
455+
// Write the report to the specific path
456+
457+
CoverageReporting.CreateReport("coverage.cobertura.xml", "./coverageReport/");
458+
}
459+
460+
// Ensure that the coverage is more than X% at the end of the tests
461+
462+
Assert.IsTrue(coverage.CoveredLinesPercentage >= RequiredCoverage, $"Coverage is less than {RequiredCoverage:P2}");
463+
}
464+
}
465+
```
420466

421467
### Known limitations
422468

src/Neo.SmartContract.Testing/Storage/EngineCheckpoint.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ public class EngineCheckpoint
1919
/// Constructor
2020
/// </summary>
2121
/// <param name="snapshot">Snapshot</param>
22-
public EngineCheckpoint(SnapshotCache snapshot)
22+
public EngineCheckpoint(DataCache snapshot)
2323
{
2424
var list = new List<(byte[], byte[])>();
2525

@@ -63,7 +63,7 @@ public EngineCheckpoint(Stream stream)
6363
/// Restore
6464
/// </summary>
6565
/// <param name="snapshot">Snapshot</param>
66-
public void Restore(SnapshotCache snapshot)
66+
public void Restore(DataCache snapshot)
6767
{
6868
// Clean snapshot
6969

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

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ public class EngineStorage
2222
/// <summary>
2323
/// Snapshot
2424
/// </summary>
25-
public SnapshotCache Snapshot { get; private set; }
25+
public DataCache Snapshot { get; private set; }
2626

2727
/// <summary>
2828
/// Return true if native contract are initialized
@@ -33,10 +33,17 @@ public class EngineStorage
3333
/// Constructor
3434
/// </summary>
3535
/// <param name="store">Store</param>
36-
public EngineStorage(IStore store)
36+
public EngineStorage(IStore store) : this(store, new SnapshotCache(store.GetSnapshot())) { }
37+
38+
/// <summary>
39+
/// Constructor
40+
/// </summary>
41+
/// <param name="store">Store</param>
42+
/// <param name="snapshotCache">Snapshot cache</param>
43+
internal EngineStorage(IStore store, DataCache snapshotCache)
3744
{
3845
Store = store;
39-
Snapshot = new SnapshotCache(Store.GetSnapshot());
46+
Snapshot = snapshotCache;
4047
}
4148

4249
/// <summary>
@@ -52,7 +59,10 @@ public void Commit()
5259
/// </summary>
5360
public void Rollback()
5461
{
55-
Snapshot.Dispose();
62+
if (Snapshot is IDisposable sp)
63+
{
64+
sp.Dispose();
65+
}
5666
Snapshot = new SnapshotCache(Store.GetSnapshot());
5767
}
5868

src/Neo.SmartContract.Testing/TestEngine.cs

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -305,22 +305,7 @@ public T Deploy<T>(NefFile nef, ContractManifest manifest, object? data = null,
305305
{
306306
// Deploy
307307

308-
if (EnableCoverageCapture)
309-
{
310-
UInt160 expectedHash = GetDeployHash(nef, manifest);
311-
312-
if (!Coverage.ContainsKey(expectedHash))
313-
{
314-
var coveredContract = new CoveredContract(MethodDetection, expectedHash, new ContractState()
315-
{
316-
Nef = nef,
317-
Hash = expectedHash,
318-
Manifest = manifest
319-
});
320-
Coverage[coveredContract.Hash] = coveredContract;
321-
}
322-
}
323-
308+
//UInt160 expectedHash = GetDeployHash(nef, manifest);
324309
var state = Native.ContractManagement.Deploy(nef.ToArray(), Encoding.UTF8.GetBytes(manifest.ToJson().ToString(false)), data);
325310

326311
if (state is null)

src/Neo.SmartContract.Testing/TestingApplicationEngine.cs

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using Neo.Persistence;
33
using Neo.SmartContract.Native;
44
using Neo.SmartContract.Testing.Extensions;
5+
using Neo.SmartContract.Testing.Storage;
56
using Neo.VM;
67
using Neo.VM.Types;
78
using System;
@@ -146,7 +147,7 @@ private void RecoverCoverage(Instruction instruction)
146147
{
147148
// We need the contract state without pay gas
148149

149-
var state = NativeContract.ContractManagement.GetContract(Engine.Storage.Snapshot, contractHash);
150+
var state = NativeContract.ContractManagement.GetContract(Snapshot, contractHash);
150151

151152
coveredContract = new(Engine.MethodDetection, contractHash, state);
152153
Engine.Coverage[contractHash] = coveredContract;
@@ -208,9 +209,30 @@ protected override void OnSysCall(InteropDescriptor descriptor)
208209

209210
// Invoke
210211

211-
var hasReturnValue = customMock.Method.ReturnType != typeof(void);
212-
var returnValue = customMock.Method.Invoke(customMock.Contract, parameters);
213-
if (hasReturnValue)
212+
object? returnValue;
213+
EngineStorage backup = Engine.Storage;
214+
215+
try
216+
{
217+
// We need to switch the Engine's snapshot in case
218+
// that a mock want to query the storage, it's not committed
219+
220+
Engine.Storage = new EngineStorage(backup.Store, Snapshot);
221+
222+
// Invoke snapshot
223+
224+
returnValue = customMock.Method.Invoke(customMock.Contract, parameters);
225+
}
226+
catch
227+
{
228+
throw;
229+
}
230+
finally
231+
{
232+
Engine.Storage = backup;
233+
}
234+
235+
if (customMock.Method.ReturnType != typeof(void))
214236
Push(Convert(returnValue));
215237
else
216238
Push(StackItem.Null);

src/Neo.SmartContract.Testing/TestingStandards/Nep17Tests.cs

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -204,22 +204,37 @@ public virtual void TestTransfer()
204204

205205
UInt160? calledFrom = null;
206206
BigInteger? calledAmount = null;
207-
byte[]? calledData = null;
207+
UInt160? calledData = null;
208208

209209
var mock = Engine.Deploy<onNEP17PaymentContract>(NefFile, manifest.ToJson().ToString(), null, m =>
210210
{
211211
m
212212
.Setup(s => s.onNEP17Payment(It.IsAny<UInt160>(), It.IsAny<BigInteger>(), It.IsAny<object>()))
213213
.Callback(new InvocationAction((i) =>
214214
{
215+
// Set variables
216+
217+
var me = new UInt160((i.Arguments[2] as ByteString)!.GetSpan().ToArray());
215218
calledFrom = i.Arguments[0] as UInt160;
216219
calledAmount = (BigInteger)i.Arguments[1];
217-
calledData = (i.Arguments[2] as ByteString)!.GetSpan().ToArray();
220+
221+
// Ensure the balance
222+
223+
Assert.AreEqual(3, Contract.BalanceOf(me));
218224

219225
// Ensure the event was called
220226

221-
var me = new UInt160(calledData);
222227
AssertTransferEvent(Alice.Account, me, calledAmount);
228+
229+
// Return the money back
230+
231+
Engine.SetTransactionSigners(me);
232+
Assert.IsTrue(Contract.Transfer(me, calledFrom, calledAmount));
233+
AssertTransferEvent(me, Alice.Account, calledAmount);
234+
235+
// Set success flag
236+
237+
calledData = me;
223238
}));
224239
});
225240

@@ -229,14 +244,9 @@ public virtual void TestTransfer()
229244
Assert.IsTrue(Contract.Transfer(Alice.Account, mock.Hash, 3, mock.Hash.ToArray()));
230245

231246
Assert.AreEqual(Alice.Account, calledFrom);
232-
Assert.AreEqual(mock.Hash, new UInt160(calledData));
247+
Assert.AreEqual(mock.Hash, calledData);
233248
Assert.AreEqual(3, calledAmount);
234-
235-
// Return the money back
236-
237-
Engine.SetTransactionSigners(mock);
238-
Assert.IsTrue(Contract.Transfer(mock.Hash, calledFrom, calledAmount));
239-
AssertTransferEvent(mock.Hash, Alice.Account, calledAmount);
249+
Assert.AreEqual(0, Contract.BalanceOf(mock.Hash));
240250
}
241251

242252
#endregion

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

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,24 +15,35 @@ public class CoverageContractTests
1515
[AssemblyCleanup]
1616
public static void EnsureCoverage()
1717
{
18-
// Join here all of your Coverage sources
18+
// Join here all of your coverage sources
1919

2020
var coverage = Nep17ContractTests.Coverage;
2121
coverage?.Join(OwnerContractTests.Coverage);
2222

23-
// Ensure that the coverage is more than X% at the end of the tests
23+
// Dump coverage to console
2424

25-
Assert.IsNotNull(coverage);
25+
Assert.IsNotNull(coverage, "Coverage can't be null");
2626
Console.WriteLine(coverage.Dump());
2727

28-
File.WriteAllText("instruction-coverage.html", coverage.Dump(DumpFormat.Html));
28+
// Write basic instruction html coverage
29+
30+
File.WriteAllText("coverage.instruction.html", coverage.Dump(DumpFormat.Html));
31+
32+
// Load our debug file
2933

3034
if (NeoDebugInfo.TryLoad("templates/neocontractnep17/Artifacts/Nep17Contract.nefdbgnfo", out var dbg))
3135
{
36+
// Write the cobertura format
37+
3238
File.WriteAllText("coverage.cobertura.xml", coverage.Dump(new CoberturaFormat((coverage, dbg))));
39+
40+
// Write the report to the specific path
41+
3342
CoverageReporting.CreateReport("coverage.cobertura.xml", "./coverageReport/");
3443
}
3544

45+
// Ensure that the coverage is more than X% at the end of the tests
46+
3647
Assert.IsTrue(coverage.CoveredLinesPercentage >= RequiredCoverage, $"Coverage is less than {RequiredCoverage:P2}");
3748
}
3849
}

0 commit comments

Comments
 (0)