Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
d425281
update
chenzhitong Jun 24, 2024
fb0af0e
remove binary file
chenzhitong Jun 24, 2024
4f87c75
Merge branch 'master' into Add-UI
cschuchardt88 Jun 24, 2024
66226af
Add UT and fixed bug
chenzhitong Jun 24, 2024
42e6131
Add UT and fixed bug
chenzhitong Jun 24, 2024
d2e2e47
Add UT
chenzhitong Jun 24, 2024
ebf0289
Add UT
chenzhitong Jun 25, 2024
3297a47
Merge branch 'master' into Add-UI
chenzhitong Jun 26, 2024
552c257
Add UT
chenzhitong Jun 26, 2024
8aa9210
Merge branch 'master' into Add-UI
chenzhitong Jun 26, 2024
19505bf
Update src/Neo/SmartContract/Manifest/ContractManifest.cs
chenzhitong Jul 2, 2024
b2db473
Update src/Neo/SmartContract/Manifest/ContractManifest.cs
chenzhitong Jul 2, 2024
d1bb544
Update src/Neo/SmartContract/Manifest/ContractManifest.cs
chenzhitong Jul 2, 2024
30669cb
Merge branch 'master' into Add-UI
chenzhitong Jul 2, 2024
9f3db1a
Update Base58.cs
chenzhitong Jul 3, 2024
90124cd
Update UT_Cryptography_Helper.cs
chenzhitong Jul 3, 2024
2b1c197
Update Base58.cs
chenzhitong Jul 3, 2024
553935d
Merge branch 'master' into Add-UI
Jul 4, 2024
eeaacb1
Merge branch 'master' into Add-UI
chenzhitong Jul 4, 2024
162cbd5
update
chenzhitong Jul 4, 2024
77d7ba7
Update ContractManifest.cs
chenzhitong Jul 4, 2024
becb1ac
Revert change that affect a syscall
shargon Jul 4, 2024
1b1fc30
Revert try
shargon Jul 4, 2024
c861b84
Remove using
shargon Jul 4, 2024
dd8ce83
Merge branch 'master' into Add-UI
chenzhitong Jul 4, 2024
bc2726e
Update src/Neo/SmartContract/Manifest/ContractManifest.cs
chenzhitong Jul 5, 2024
d6cb9cd
Update src/Neo/SmartContract/Manifest/ContractAbi.cs
chenzhitong Jul 5, 2024
34a8680
Update src/Neo/SmartContract/Manifest/ContractManifest.cs
chenzhitong Jul 5, 2024
4ada44e
Merge branch 'master' into Add-UI
chenzhitong Jul 5, 2024
5aaaae8
Merge branch 'master' into Add-UI
shargon Jul 5, 2024
cc6fbc0
Merge branch 'master' into Add-UI
chenzhitong Jul 9, 2024
981bf73
Merge branch 'master' into Add-UI
chenzhitong Jul 11, 2024
200bbd4
Merge branch 'master' into Add-UI
NGDAdmin Jul 11, 2024
5bede19
Merge branch 'master' into Add-UI
NGDAdmin Jul 11, 2024
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
6 changes: 3 additions & 3 deletions src/Neo/SmartContract/Manifest/ContractAbi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,10 @@ public static ContractAbi FromJson(JObject json)
{
ContractAbi abi = new()
{
Methods = ((JArray)json["methods"]).Select(u => ContractMethodDescriptor.FromJson((JObject)u)).ToArray(),
Events = ((JArray)json["events"]).Select(u => ContractEventDescriptor.FromJson((JObject)u)).ToArray()
Methods = ((JArray)json?["methods"])?.Select(u => ContractMethodDescriptor.FromJson((JObject)u)).ToArray(),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think those are actually required, should not use ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Followed by a line of code
if (abi.Methods == null || abi.Methods.Length == 0) throw new FormatException();
I add ? to avoid null reference exceptions. When adding unit tests, I found that this is prone to null reference exceptions

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you do json["methods"] as JArray it can be null vs. the other way, Unless you do (JArray?)

Events = ((JArray)json?["events"])?.Select(u => ContractEventDescriptor.FromJson((JObject)u)).ToArray()
};
if (abi.Methods.Length == 0) throw new FormatException();
if (abi.Methods == null || abi.Methods.Length == 0) throw new FormatException();
return abi;
}

Expand Down
25 changes: 15 additions & 10 deletions src/Neo/SmartContract/Manifest/ContractManifest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -110,22 +110,27 @@ public StackItem ToStackItem(ReferenceCounter referenceCounter)
/// <returns>The converted manifest.</returns>
public static ContractManifest FromJson(JObject json)
{
ContractManifest manifest = new()
ContractManifest manifest = new();
try
{
Name = json["name"].GetString(),
Groups = ((JArray)json["groups"]).Select(u => ContractGroup.FromJson((JObject)u)).ToArray(),
SupportedStandards = ((JArray)json["supportedstandards"]).Select(u => u.GetString()).ToArray(),
Abi = ContractAbi.FromJson((JObject)json["abi"]),
Permissions = ((JArray)json["permissions"]).Select(u => ContractPermission.FromJson((JObject)u)).ToArray(),
Trusts = WildcardContainer<ContractPermissionDescriptor>.FromJson(json["trusts"], u => ContractPermissionDescriptor.FromJson((JString)u)),
Extra = (JObject)json["extra"]
};
manifest.Name = json["name"]?.GetString();
manifest.Groups = ((JArray)json["groups"])?.Select(u => ContractGroup.FromJson((JObject)u)).ToArray();
manifest.SupportedStandards = ((JArray)json["supportedstandards"])?.Select(u => u.GetString()).ToArray();
manifest.Abi = ContractAbi.FromJson((JObject)json["abi"]);
manifest.Permissions = ((JArray)json["permissions"])?.Select(u => ContractPermission.FromJson((JObject)u)).ToArray();
manifest.Trusts = WildcardContainer<ContractPermissionDescriptor>.FromJson(json["trusts"], u => ContractPermissionDescriptor.FromJson((JString)u));
manifest.Extra = (JObject)json["extra"];
}
catch (FormatException)
{
throw;
}
if (string.IsNullOrEmpty(manifest.Name))
throw new FormatException();
_ = manifest.Groups.ToDictionary(p => p.PubKey);
if (json["features"] is not JObject features || features.Count != 0)
throw new FormatException();
if (manifest.SupportedStandards.Any(p => string.IsNullOrEmpty(p)))
if (manifest.SupportedStandards.Any(string.IsNullOrEmpty))
throw new FormatException();
_ = manifest.SupportedStandards.ToDictionary(p => p);
_ = manifest.Permissions.ToDictionary(p => p.Contract);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,8 @@ public bool Equals(ContractPermissionDescriptor other)
if (this == other) return true;
if (IsWildcard == other.IsWildcard) return true;
if (IsHash) return Hash.Equals(other.Hash);
else return Group.Equals(other.Group);
if (IsGroup) return Group.Equals(other.Group);
return false;
}

public override int GetHashCode()
Expand Down
3 changes: 3 additions & 0 deletions tests/Neo.UnitTests/Neo.UnitTests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@
</ItemGroup>

<ItemGroup>
<None Update="SmartContract\Manifest\TestFile\**">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="test.config.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"name":"SampleContractCall","groups":[],"features":{},"supportedstandards":[],"abi":{"methods":[{"name":"onNEP17Payment","parameters":[{"name":"from","type":"Hash160"},{"name":"amount","type":"Integer"},{"name":"data","type":"Integer"}],"returntype":"Void","offset":0,"safe":false},{"name":"_initialize","parameters":[],"returntype":"Void","offset":91,"safe":false}],"events":[]},"permissions":[],"trusts":[],"extra":{"Author":"core-dev","Version":"0.0.1","Description":"A sample contract to demonstrate how to call a contract","Sourcecode":"https://github.com/neo-project/neo-devpack-dotnet/tree/master/examples/"}}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"name":"SampleEvent","groups":[],"features":{},"supportedstandards":[],"abi":{"methods":[{"name":"main","parameters":[],"returntype":"Boolean","offset":0,"safe":false}],"events":[{"name":"new_event_name","parameters":[{"name":"arg1","type":"ByteArray"},{"name":"arg2","type":"String"},{"name":"arg3","type":"Integer"}]},{"name":"event2","parameters":[{"name":"arg1","type":"ByteArray"},{"name":"arg2","type":"Integer"}]}]},"permissions":[{"contract":"*","methods":"*"}],"trusts":[],"extra":{"Author":"code-dev","Description":"A sample contract that demonstrates how to use Events","Version":"0.0.1","Sourcecode":"https://github.com/neo-project/neo-devpack-dotnet/tree/master/examples/"}}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"name":"SampleException","groups":[],"features":{},"supportedstandards":[],"abi":{"methods":[{"name":"try01","parameters":[],"returntype":"Any","offset":0,"safe":false},{"name":"try02","parameters":[],"returntype":"Any","offset":77,"safe":false},{"name":"try03","parameters":[],"returntype":"Any","offset":166,"safe":false},{"name":"tryNest","parameters":[],"returntype":"Any","offset":259,"safe":false},{"name":"tryFinally","parameters":[],"returntype":"Any","offset":404,"safe":false},{"name":"tryFinallyAndRethrow","parameters":[],"returntype":"Any","offset":474,"safe":false},{"name":"tryCatch","parameters":[],"returntype":"Any","offset":550,"safe":false},{"name":"tryWithTwoFinally","parameters":[],"returntype":"Any","offset":628,"safe":false},{"name":"tryecpointCast","parameters":[],"returntype":"Any","offset":920,"safe":false},{"name":"tryvalidByteString2Ecpoint","parameters":[],"returntype":"Any","offset":1010,"safe":false},{"name":"tryinvalidByteArray2UInt160","parameters":[],"returntype":"Any","offset":1100,"safe":false},{"name":"tryvalidByteArray2UInt160","parameters":[],"returntype":"Any","offset":1190,"safe":false},{"name":"tryinvalidByteArray2UInt256","parameters":[],"returntype":"Any","offset":1280,"safe":false},{"name":"tryvalidByteArray2UInt256","parameters":[],"returntype":"Any","offset":1370,"safe":false},{"name":"tryNULL2Ecpoint_1","parameters":[],"returntype":"Array","offset":1476,"safe":false},{"name":"tryNULL2Uint160_1","parameters":[],"returntype":"Array","offset":1652,"safe":false},{"name":"tryNULL2Uint256_1","parameters":[],"returntype":"Array","offset":1828,"safe":false},{"name":"tryNULL2Bytestring_1","parameters":[],"returntype":"Array","offset":1990,"safe":false},{"name":"tryUncatchableException","parameters":[],"returntype":"Any","offset":2141,"safe":false},{"name":"_initialize","parameters":[],"returntype":"Void","offset":2219,"safe":false}],"events":[]},"permissions":[{"contract":"*","methods":"*"}],"trusts":[],"extra":{"Author":"core-dev","Description":"A sample contract to demonstrate how to handle exception","Version":"0.0.1","Sourcecode":"https://github.com/neo-project/neo-devpack-dotnet/tree/master/examples/"}}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"name":"SampleHelloWorld","groups":[],"features":{},"supportedstandards":[],"abi":{"methods":[{"name":"sayHello","parameters":[],"returntype":"String","offset":0,"safe":true}],"events":[]},"permissions":[{"contract":"*","methods":"*"}],"trusts":[],"extra":{"Description":"A simple \u0060hello world\u0060 contract","E-mail":"[email protected]","Version":"0.0.1","Sourcecode":"https://github.com/neo-project/neo-devpack-dotnet/tree/master/examples/"}}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"name":"SampleNep17Token","groups":[],"features":{},"supportedstandards":["NEP-17"],"abi":{"methods":[{"name":"symbol","parameters":[],"returntype":"String","offset":1333,"safe":true},{"name":"decimals","parameters":[],"returntype":"Integer","offset":1348,"safe":true},{"name":"totalSupply","parameters":[],"returntype":"Integer","offset":52,"safe":true},{"name":"balanceOf","parameters":[{"name":"owner","type":"Hash160"}],"returntype":"Integer","offset":98,"safe":true},{"name":"transfer","parameters":[{"name":"from","type":"Hash160"},{"name":"to","type":"Hash160"},{"name":"amount","type":"Integer"},{"name":"data","type":"Any"}],"returntype":"Boolean","offset":362,"safe":false},{"name":"getOwner","parameters":[],"returntype":"Hash160","offset":808,"safe":true},{"name":"setOwner","parameters":[{"name":"newOwner","type":"Any"}],"returntype":"Void","offset":877,"safe":false},{"name":"getMinter","parameters":[],"returntype":"Hash160","offset":980,"safe":true},{"name":"setMinter","parameters":[{"name":"newMinter","type":"Hash160"}],"returntype":"Void","offset":1025,"safe":false},{"name":"mint","parameters":[{"name":"to","type":"Hash160"},{"name":"amount","type":"Integer"}],"returntype":"Void","offset":1103,"safe":false},{"name":"burn","parameters":[{"name":"account","type":"Hash160"},{"name":"amount","type":"Integer"}],"returntype":"Void","offset":1158,"safe":false},{"name":"verify","parameters":[],"returntype":"Boolean","offset":1216,"safe":true},{"name":"update","parameters":[{"name":"nefFile","type":"ByteArray"},{"name":"manifest","type":"String"}],"returntype":"Boolean","offset":1222,"safe":false},{"name":"_initialize","parameters":[],"returntype":"Void","offset":1271,"safe":false}],"events":[{"name":"Transfer","parameters":[{"name":"from","type":"Hash160"},{"name":"to","type":"Hash160"},{"name":"amount","type":"Integer"}]},{"name":"SetOwner","parameters":[{"name":"newOwner","type":"Hash160"}]},{"name":"SetMinter","parameters":[{"name":"newMinter","type":"Hash160"}]}]},"permissions":[{"contract":"*","methods":"*"}],"trusts":[],"extra":{"Author":"core-dev","Version":"0.0.1","Description":"A sample NEP-17 token","Sourcecode":"https://github.com/neo-project/neo-devpack-dotnet/tree/master/examples/"}}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"name":"SampleOracle","groups":[],"features":{},"supportedstandards":[],"abi":{"methods":[{"name":"getResponse","parameters":[],"returntype":"String","offset":0,"safe":true},{"name":"doRequest","parameters":[],"returntype":"Void","offset":35,"safe":false},{"name":"onOracleResponse","parameters":[{"name":"requestedUrl","type":"String"},{"name":"userData","type":"Any"},{"name":"oracleResponse","type":"Integer"},{"name":"jsonString","type":"String"}],"returntype":"Void","offset":333,"safe":false}],"events":[]},"permissions":[{"contract":"*","methods":"*"}],"trusts":[],"extra":{"Author":"code-dev","Description":"A sample contract to demonstrate how to use Example.SmartContract.Oracle Service","Version":"0.0.1","Sourcecode":"https://github.com/neo-project/neo-devpack-dotnet/tree/master/examples/"}}
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,12 @@
// Redistribution and use in source and binary forms with or without
// modifications are permitted.

using FluentAssertions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Neo.Json;
using Neo.SmartContract.Manifest;
using Neo.Wallets;
using System;
using System.Security.Cryptography;

namespace Neo.UnitTests.SmartContract.Manifest
Expand All @@ -33,7 +36,7 @@ public void TestCreateByECPointAndIsWildcard()
}

[TestMethod]
public void TestFromAndToJson()
public void TestContractPermissionDescriptorFromAndToJson()
{
byte[] privateKey = new byte[32];
RandomNumberGenerator rng = RandomNumberGenerator.Create();
Expand All @@ -43,6 +46,20 @@ public void TestFromAndToJson()
ContractPermissionDescriptor result = ContractPermissionDescriptor.FromJson(temp.ToJson());
Assert.AreEqual(null, result.Hash);
Assert.AreEqual(result.Group, result.Group);
Assert.ThrowsException<FormatException>(() => ContractPermissionDescriptor.FromJson(string.Empty));
}

[TestMethod]
public void TestContractManifestFromJson()
{
Assert.ThrowsException<FormatException>(() => ContractManifest.FromJson(new Json.JObject()));
var jsonFiles = System.IO.Directory.GetFiles(System.IO.Path.Combine("SmartContract", "Manifest", "TestFile"));
foreach (var item in jsonFiles)
{
var json = JObject.Parse(System.IO.File.ReadAllText(item)) as JObject;
var manifest = ContractManifest.FromJson(json);
manifest.ToJson().ToString().Should().Be(json.ToString());
}
}
}
}
122 changes: 122 additions & 0 deletions tests/Neo.UnitTests/SmartContract/UT_InteropService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
// modifications are permitted.

using Akka.TestKit.Xunit2;
using Akka.Util.Internal;
using FluentAssertions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Neo.Cryptography;
Expand Down Expand Up @@ -507,6 +508,41 @@ public void TestBlockchain_GetContract()
NativeContract.ContractManagement.GetContract(engine.Snapshot, state.Hash).Hash.Should().Be(state.Hash);
}

[TestMethod]
public void TestBlockchain_GetContractById()
{
var engine = GetEngine(true, true);
var contract = NativeContract.ContractManagement.GetContractById(engine.Snapshot, -1);
contract.Id.Should().Be(-1);
contract.Manifest.Name.Should().Be(nameof(ContractManagement));
}

[TestMethod]
public void TestBlockchain_HasMethod()
{
var engine = GetEngine(true, true);
NativeContract.ContractManagement.HasMethod(engine.Snapshot, NativeContract.NEO.Hash, "symbol", 0).Should().Be(true);
NativeContract.ContractManagement.HasMethod(engine.Snapshot, NativeContract.NEO.Hash, "transfer", 4).Should().Be(true);
}

[TestMethod]
public void TestBlockchain_ListContracts()
{
var engine = GetEngine(true, true);
var list = NativeContract.ContractManagement.ListContracts(engine.Snapshot);
list.ForEach(p => p.Id.Should().BeLessThan(0));

var snapshot = TestBlockchain.GetTestSnapshot();
var state = TestUtils.GetContract();
snapshot.AddContract(state.Hash, state);
engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot);
engine.LoadScript(new byte[] { 0x01 });
NativeContract.ContractManagement.GetContract(engine.Snapshot, state.Hash).Hash.Should().Be(state.Hash);

var list2 = NativeContract.ContractManagement.ListContracts(engine.Snapshot);
list2.Count().Should().Be(list.Count() + 1);
}

[TestMethod]
public void TestStorage_GetContext()
{
Expand Down Expand Up @@ -739,5 +775,91 @@ private static ApplicationEngine GetEngine(bool hasContainer = false, bool hasSn
if (addScript) engine.LoadScript(new byte[] { 0x01 });
return engine;
}

[TestMethod]
public void TestVerifyWithECDsaV0()
{
var privateKey = new byte[32];
using var rng = System.Security.Cryptography.RandomNumberGenerator.Create();
rng.GetBytes(privateKey);
var publicKeyR1 = new KeyPair(privateKey).PublicKey.ToArray();
var publicKeyK1 = (Neo.Cryptography.ECC.ECCurve.Secp256k1.G * privateKey).ToArray();
var hexMessage = "Hello, world!"u8.ToArray();
var signatureR1 = Crypto.Sign(hexMessage, privateKey, Neo.Cryptography.ECC.ECCurve.Secp256r1);
var signatureK1 = Crypto.Sign(hexMessage, privateKey, Neo.Cryptography.ECC.ECCurve.Secp256k1);

var result = CryptoLib.VerifyWithECDsaV0(hexMessage, publicKeyR1, signatureR1, NamedCurveHash.secp256r1SHA256);
result.Should().BeTrue();
result = CryptoLib.VerifyWithECDsaV0(hexMessage, publicKeyK1, signatureK1, NamedCurveHash.secp256k1SHA256);
result.Should().BeTrue();
result = CryptoLib.VerifyWithECDsaV0(hexMessage, publicKeyK1, new byte[0], NamedCurveHash.secp256k1SHA256);
result.Should().BeFalse();
Assert.ThrowsException<ArgumentOutOfRangeException>(() => CryptoLib.VerifyWithECDsaV0(hexMessage, publicKeyK1, new byte[64], NamedCurveHash.secp256r1Keccak256));
}

[TestMethod]
public void TestSha256()
{
var input = "Hello, world!"u8.ToArray();
var actualHash = CryptoLib.Sha256(input);
var expectedHash = "315f5bdb76d078c43b8ac0064e4a0164612b1fce77c869345bfc94c75894edd3";
actualHash.ToHexString().Should().Be(expectedHash);
}

[TestMethod]
public void TestRIPEMD160()
{
var input = "Hello, world!"u8.ToArray();
var actualHash = CryptoLib.RIPEMD160(input);
var expectedHash = "58262d1fbdbe4530d8865d3518c6d6e41002610f";
actualHash.ToHexString().Should().Be(expectedHash);
}

[TestMethod]
public void TestMurmur32()
{
var input = "Hello, world!"u8.ToArray();
var actualHash = CryptoLib.Murmur32(input, 0);
var expectedHash = "433e36c0";
actualHash.ToHexString().Should().Be(expectedHash);
}

[TestMethod]
public void TestGetBlockHash()
{
var snapshot = GetEngine(true, true).Snapshot;
var hash = LedgerContract.Ledger.GetBlockHash(snapshot, 0);
var hash2 = LedgerContract.Ledger.GetBlock(snapshot, 0).Hash;
var hash3 = LedgerContract.Ledger.GetHeader(snapshot, 0).Hash;
hash.ToString().Should().Be(hash2.ToString());
hash.ToString().Should().Be(hash3.ToString());
hash.ToString().Should().Be("0x1f4d1defa46faa5e7b9b8d3f79a06bec777d7c26c4aa5f6f5899a291daa87c15");
LedgerContract.Ledger.ContainsBlock(snapshot, hash).Should().BeTrue();
}

[TestMethod]
public void TestGetCandidateVote()
{
var snapshot = GetEngine(true, true).Snapshot;
var vote = LedgerContract.NEO.GetCandidateVote(snapshot, new ECPoint());
vote.Should().Be(-1);
}

[TestMethod]
public void TestContractPermissionDescriptorEquals()
{
var descriptor1 = ContractPermissionDescriptor.CreateWildcard();
descriptor1.Equals(null).Should().BeFalse();
var descriptor2 = ContractPermissionDescriptor.Create(LedgerContract.NEO.Hash);
descriptor1.Equals(descriptor2).Should().BeFalse();
var descriptor3 = ContractPermissionDescriptor.Create(hash: null);
descriptor1.Equals(descriptor3).Should().BeTrue();
var descriptor4 = ContractPermissionDescriptor.Create(group: null);
var descriptor5 = ContractPermissionDescriptor.Create(group: new ECPoint());
descriptor1.Equals(descriptor4).Should().BeTrue();
descriptor2.Equals(descriptor3).Should().BeFalse();
descriptor5.Equals(descriptor3).Should().BeFalse();
descriptor5.Equals(descriptor5).Should().BeTrue();
}
}
}