Skip to content

Commit

Permalink
TestEngine: added Ownable and Oracle templates uts & fixes (#950)
Browse files Browse the repository at this point in the history
* Ownable unit tests

* Allow to export html and console joined coverage

* Fix preview lang

* fix html too

* update workflow to generate artifacts

* Clean format method selection

* Export nef and manifest as static

* Rename

* replace typeof

* clean csproj

* Fix moq with transfer

* Test oracle

---------

Co-authored-by: Jimmy <[email protected]>
  • Loading branch information
shargon and Jim8y authored Feb 26, 2024
1 parent 8d23ee1 commit 6f230f4
Show file tree
Hide file tree
Showing 37 changed files with 714 additions and 327 deletions.
16 changes: 12 additions & 4 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,19 @@ jobs:
run: |
dotnet pack ./src/Neo.SmartContract.Template/Neo.SmartContract.Template.csproj
dotnet new install ./src/Neo.SmartContract.Template/bin/Debug/Neo.SmartContract.Template.*.nupkg
dotnet new neocontractnep17 -n Nep17Contract -o ./src/Neo.SmartContract.Template/bin/Debug/ --force
dotnet new neocontractnep17 -n Nep17Contract -o ./src/Neo.SmartContract.Template/bin/Debug/nep17/ --force
dotnet new neocontractowner -n Ownable -o ./src/Neo.SmartContract.Template/bin/Debug/ownable/ --force
dotnet new neocontractoracle -n OracleRequest -o ./src/Neo.SmartContract.Template/bin/Debug/oracle/ --force
dotnet new uninstall Neo.SmartContract.Template
dotnet remove ./src/Neo.SmartContract.Template/bin/Debug/Nep17Contract.csproj package Neo.SmartContract.Framework
dotnet add ./src/Neo.SmartContract.Template/bin/Debug/Nep17Contract.csproj reference ./src/Neo.SmartContract.Framework/Neo.SmartContract.Framework.csproj
dotnet ./src/Neo.Compiler.CSharp/bin/Debug/net7.0/nccs.dll ./src/Neo.SmartContract.Template/bin/Debug/Nep17Contract.csproj -o ./tests/Neo.SmartContract.Template.UnitTests/templates/neocontractnep17/Artifacts/ --generate-artifacts source --debug
dotnet remove ./src/Neo.SmartContract.Template/bin/Debug/nep17/Nep17Contract.csproj package Neo.SmartContract.Framework
dotnet add ./src/Neo.SmartContract.Template/bin/Debug/nep17/Nep17Contract.csproj reference ./src/Neo.SmartContract.Framework/Neo.SmartContract.Framework.csproj
dotnet remove ./src/Neo.SmartContract.Template/bin/Debug/ownable/Ownable.csproj package Neo.SmartContract.Framework
dotnet add ./src/Neo.SmartContract.Template/bin/Debug/ownable/Ownable.csproj reference ./src/Neo.SmartContract.Framework/Neo.SmartContract.Framework.csproj
dotnet remove ./src/Neo.SmartContract.Template/bin/Debug/oracle/OracleRequest.csproj package Neo.SmartContract.Framework
dotnet add ./src/Neo.SmartContract.Template/bin/Debug/oracle/OracleRequest.csproj reference ./src/Neo.SmartContract.Framework/Neo.SmartContract.Framework.csproj
dotnet ./src/Neo.Compiler.CSharp/bin/Debug/net7.0/nccs.dll ./src/Neo.SmartContract.Template/bin/Debug/nep17/Nep17Contract.csproj -o ./tests/Neo.SmartContract.Template.UnitTests/templates/neocontractnep17/Artifacts/ --generate-artifacts source --debug
dotnet ./src/Neo.Compiler.CSharp/bin/Debug/net7.0/nccs.dll ./src/Neo.SmartContract.Template/bin/Debug/ownable/Ownable.csproj -o ./tests/Neo.SmartContract.Template.UnitTests/templates/neocontractowner/Artifacts/ --generate-artifacts source --debug
dotnet ./src/Neo.Compiler.CSharp/bin/Debug/net7.0/nccs.dll ./src/Neo.SmartContract.Template/bin/Debug/oracle/OracleRequest.csproj -o ./tests/Neo.SmartContract.Template.UnitTests/templates/neocontractoracle/Artifacts/ --generate-artifacts source --debug
- name: Build Solution
run: dotnet build ./neo-devpack-dotnet.sln
- name: Add package coverlet.msbuild
Expand Down
2 changes: 1 addition & 1 deletion src/Neo.Compiler.CSharp/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ private static int ProcessOutput(Options options, string folder, CompilationCont

if (options.GenerateArtifacts != Options.GenerateArtifactsKind.None)
{
var artifact = manifest.GetArtifactsSource(baseName);
var artifact = manifest.GetArtifactsSource(baseName, nef, debugInfo);

if (options.GenerateArtifacts == Options.GenerateArtifactsKind.All || options.GenerateArtifacts == Options.GenerateArtifactsKind.Source)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
using System.ComponentModel;
using System.Numerics;

namespace ProjectName
namespace Neo.SmartContract.Template
{
[DisplayName(nameof(Nep17Contract))]
[ManifestExtra("Author", "<Your Name Or Company Here>")]
Expand Down Expand Up @@ -109,7 +109,7 @@ public static void _deploy(object data, bool update)
Storage.Put(Storage.CurrentContext, "Hello", "World");
}

public static void Update(ByteString nefFile, string manifest, object data)
public static void Update(ByteString nefFile, string manifest, object? data = null)
{
if (IsOwner() == false)
throw new InvalidOperationException("No authorization.");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"language": "C#",
"type": "project"
},
"sourceName": "ProjectName",
"sourceName": "OracleRequest",
"symbols": {
"NeoVersion": {
"type": "parameter",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,67 +6,26 @@
using Neo.SmartContract.Framework.Services;

using System;
using System.Collections.Generic;
using System.ComponentModel;

namespace ProjectName
namespace Neo.SmartContract.Template
{
[DisplayName(nameof(Contract1))]
[DisplayName(nameof(OracleRequest))]
[ManifestExtra("Author", "<Your Name Or Company Here>")]
[ManifestExtra("Description", "<Description Here>")]
[ManifestExtra("Email", "<Your Public Email Here>")]
[ManifestExtra("Version", "<Version String Here>")]
[ContractSourceCode("https://github.com/neo-project/neo-devpack-dotnet/tree/master/src/Neo.SmartContract.Template")]
[ContractSourceCode("https://github.com/neo-project/neo-devpack-dotnet/tree/master/src/Neo.SmartContract.Template/templates/neocontractoracle/OracleRequest.cs")]
[ContractPermission("*", "*")]
public class Contract1 : SmartContract
public class OracleRequest : Neo.SmartContract.Framework.SmartContract
{
public delegate void OnRequestSuccessfulDelegate(string requestedUrl, object jsonValue);

[DisplayName("RequestSuccessful")]
public static event OnRequestSuccessfulDelegate OnRequestSuccessful;

// TODO: Replace it with your own address.
[InitialValue("<Your Address Here>", ContractParameterType.Hash160)]
static readonly UInt160 Owner = default;

private static bool IsOwner() => Runtime.CheckWitness(Owner);

// When this contract address is included in the transaction signature,
// this method will be triggered as a VerificationTrigger to verify that the signature is correct.
// For example, this method needs to be called when withdrawing token from the contract.
[Safe]
public static bool Verify() => IsOwner();

// TODO: Replace it with your methods.
public static string MyMethod()
{
return Storage.Get(Storage.CurrentContext, "Hello");
}

public static void _deploy(object data, bool update)
{
if (update)
{
// This will be executed during update
return;
}

// This will be executed during deploy
Storage.Put(Storage.CurrentContext, "Hello", "World");
}

public static void Update(ByteString nefFile, string manifest)
{
if (!IsOwner()) throw new Exception("No authorization.");
ContractManagement.Update(nefFile, manifest, null);
}

public static void Destroy()
public static string GetResponse()
{
if (!IsOwner()) throw new Exception("No authorization.");
ContractManagement.Destroy();
return Storage.Get(Storage.CurrentContext, "Response");
}

// TODO: Add your own logic
public static void DoRequest()
{
/*
Expand All @@ -93,7 +52,7 @@ JSON DATA EXAMPLE
}

// This method is called after the Oracle receives response from requested URL
public static void OnOracleResponse(string requestedUrl, object userData, OracleResponseCode oracleResponse, string jsonString)
public static void onOracleResponse(string requestedUrl, object userData, OracleResponseCode oracleResponse, string jsonString)
{
if (Runtime.CallingScriptHash != Oracle.Hash)
throw new InvalidOperationException("No Authorization!");
Expand All @@ -103,7 +62,7 @@ public static void OnOracleResponse(string requestedUrl, object userData, Oracle
var jsonArrayValues = (object[])StdLib.JsonDeserialize(jsonString);
var jsonFirstValue = (string)jsonArrayValues[0];

OnRequestSuccessful(requestedUrl, jsonFirstValue);
Storage.Put(Storage.CurrentContext, "Response", jsonFirstValue);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"language": "C#",
"type": "project"
},
"sourceName": "ProjectName",
"sourceName": "Ownable",
"symbols": {
"NeoVersion": {
"type": "parameter",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,42 +8,56 @@
using System;
using System.ComponentModel;

namespace ProjectName
namespace Neo.SmartContract.Template
{
[DisplayName(nameof(Contract1))]
[DisplayName(nameof(Ownable))]
[ManifestExtra("Author", "<Your Name Or Company Here>")]
[ManifestExtra("Description", "<Description Here>")]
[ManifestExtra("Email", "<Your Public Email Here>")]
[ManifestExtra("Version", "<Version String Here>")]
[ContractSourceCode("https://github.com/neo-project/neo-devpack-dotnet/tree/master/src/Neo.SmartContract.Template")]
[ContractSourceCode("https://github.com/neo-project/neo-devpack-dotnet/tree/master/src/Neo.SmartContract.Template/templates/neocontractowner/Ownable.cs")]
[ContractPermission("*", "*")]
public class Contract1 : SmartContract
public class Ownable : Neo.SmartContract.Framework.SmartContract
{
#region Owner

private const byte Prefix_Owner = 0xff;

public delegate void OnSetOwnerDelegate(UInt160 newOwner);
[Safe]
public static UInt160 GetOwner()
{
return (UInt160)Storage.Get(new[] { Prefix_Owner });
}

private static bool IsOwner() =>
Runtime.CheckWitness(GetOwner());

public delegate void OnSetOwnerDelegate(UInt160 previousOwner, UInt160 newOwner);

[DisplayName("SetOwner")]
public static event OnSetOwnerDelegate OnSetOwner;

// TODO: Replace it with your own address.
[InitialValue("<Your Address Here>", ContractParameterType.Hash160)]
private static readonly UInt160 InitialOwner = default;
public static void SetOwner(UInt160 newOwner)
{
if (IsOwner() == false)
throw new InvalidOperationException("No Authorization!");

private static bool IsOwner() => Runtime.CheckWitness(GetOwner());
ExecutionEngine.Assert(newOwner.IsValid && !newOwner.IsZero, "owner must be valid");

// When this contract address is included in the transaction signature,
// this method will be triggered as a VerificationTrigger to verify that the signature is correct.
// For example, this method needs to be called when withdrawing token from the contract.
[Safe]
public static bool Verify() => IsOwner();
UInt160 previous = GetOwner();
Storage.Put(new[] { Prefix_Owner }, newOwner);
OnSetOwner(previous, newOwner);
}

#endregion

// TODO: Replace it with your methods.
public static string MyMethod()
{
return Storage.Get(Storage.CurrentContext, "Hello");
}

// This will be executed during deploy
public static void _deploy(object data, bool update)
{
if (update)
Expand All @@ -52,43 +66,30 @@ public static void _deploy(object data, bool update)
return;
}

// This will be executed during deploy
// Init method, you must deploy the contract with the owner as an argument, or it will take the sender
if (data is null) data = Runtime.Transaction.Sender;

UInt160 initialOwner = (UInt160)data;

ExecutionEngine.Assert(initialOwner.IsValid && !initialOwner.IsZero, "owner must exists");

Storage.Put(new[] { Prefix_Owner }, initialOwner);
OnSetOwner(null, initialOwner);
Storage.Put(Storage.CurrentContext, "Hello", "World");
}

public static void Update(ByteString nefFile, string manifest)
public static void Update(ByteString nefFile, string manifest, object? data = null)
{
if (!IsOwner()) throw new Exception("No authorization.");
ContractManagement.Update(nefFile, manifest, null);
if (IsOwner() == false)
throw new InvalidOperationException("No authorization.");
ContractManagement.Update(nefFile, manifest, data);
}

public static void Destroy()
{
if (!IsOwner()) throw new Exception("No authorization.");
if (!IsOwner())
throw new InvalidOperationException("No authorization.");
ContractManagement.Destroy();
}

// Safe is for read operations Or Safe to call by everyone
[Safe]
public static UInt160 GetOwner()
{
var currentOwner = Storage.Get(new[] { Prefix_Owner });

if (currentOwner == null)
return InitialOwner;

return (UInt160)currentOwner;
}

public static void SetOwner(UInt160 newOwner)
{
if (IsOwner() == false)
throw new InvalidOperationException("No Authorization!");
if (newOwner != null && newOwner.IsValid)
{
Storage.Put(new[] { Prefix_Owner }, newOwner);
OnSetOwner(newOwner);
}
}
}
}
7 changes: 7 additions & 0 deletions src/Neo.SmartContract.Testing/Coverage/CoverageBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,13 @@ public IEnumerable<CoverageBranch> GetCoverageBranchFrom(int offset, int length)
}
}

/// <summary>
/// Dump coverage
/// </summary>
/// <param name="format">Format</param>
/// <returns>Coverage dump</returns>
public abstract string Dump(DumpFormat format = DumpFormat.Console);

public static decimal CalculateHitRate(int total, int hits)
=> total == 0 ? 1m : new decimal(hits) / new decimal(total);

Expand Down
35 changes: 35 additions & 0 deletions src/Neo.SmartContract.Testing/Coverage/CoveredCollection.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
using Neo.SmartContract.Testing.Coverage.Formats;
using System;
using System.Collections.Generic;
using System.Linq;

namespace Neo.SmartContract.Testing.Coverage
{
Expand Down Expand Up @@ -51,5 +54,37 @@ public CoveredCollection(params CoverageBase[] entries)
{
Entries = entries;
}

/// <summary>
/// Dump coverage
/// </summary>
/// <param name="format">Format</param>
/// <returns>Coverage dump</returns>
public override string Dump(DumpFormat format = DumpFormat.Console)
{
IEnumerable<(CoveredContract, Func<CoveredMethod, bool>?)> entries = Entries.Select(u =>
{
if (u is CoveredContract co) return (co, (Func<CoveredMethod, bool>?)null);
if (u is CoveredMethod cm) return (cm.Contract, new Func<CoveredMethod, bool>((CoveredMethod method) => ReferenceEquals(method, cm)));
throw new NotImplementedException();
})!;

switch (format)
{
case DumpFormat.Console:
{
return new ConsoleFormat(entries).Dump();
}
case DumpFormat.Html:
{
return new IntructionHtmlFormat(entries).Dump();
}
default:
{
throw new NotImplementedException();
}
}
}
}
}
Loading

0 comments on commit 6f230f4

Please sign in to comment.