diff --git a/Jenkinsfile b/Jenkinsfile
index 2979f70..82d7851 100644
--- a/Jenkinsfile
+++ b/Jenkinsfile
@@ -17,6 +17,23 @@ node {
bat "\"${tool 'MSBuild'}msbuild\" Torch.sln /p:Configuration=Release /p:Platform=x64"
}
+ stage('Test') {
+ bat "\"packages/xunit.runner.console.2.2.0/tools/xunit.console.exe\" \"bin-test/x64/Release/Torch.Tests.dll\" -parallel none -nunit \"reports/Torch.Tests.xml\""
+ step([
+ $class: 'XUnitBuilder',
+ thresholdMode: 1,
+ thresholds: [[$class: 'FailedThreshold', failureThreshold: '1']],
+ tools: [[
+ $class: 'XUnitDotNetTestType',
+ deleteOutputFiles: true,
+ failIfNotNew: true,
+ pattern: 'reports/*.xml',
+ skipNoTestFiles: false,
+ stopProcessingIfError: true
+ ]]
+ ])
+ }
+
stage('Archive') {
archive 'bin/x64/Release/Torch.*'
}
diff --git a/Torch.Client/Torch.Client.csproj b/Torch.Client/Torch.Client.csproj
index d6054c8..77e51d5 100644
--- a/Torch.Client/Torch.Client.csproj
+++ b/Torch.Client/Torch.Client.csproj
@@ -122,7 +122,6 @@
True
-
diff --git a/Torch.Tests/Properties/AssemblyInfo.cs b/Torch.Tests/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..089d989
--- /dev/null
+++ b/Torch.Tests/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("Torch.Tests")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("Torch.Tests")]
+[assembly: AssemblyCopyright("Copyright © 2017")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("c3c8b671-6ad1-44aa-a8da-e0c0dc0fedf5")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/Torch.Tests/ReflectionTests.cs b/Torch.Tests/ReflectionTests.cs
new file mode 100644
index 0000000..eec6ab7
--- /dev/null
+++ b/Torch.Tests/ReflectionTests.cs
@@ -0,0 +1,306 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using System.Threading.Tasks;
+using Torch.API;
+using Torch.Client;
+using Torch.Managers;
+using Xunit;
+using Xunit.Abstractions;
+
+namespace Torch.Tests
+{
+ public class ReflectionTests
+ {
+ private static string GetGameBinaries()
+ {
+ string dir = Environment.CurrentDirectory;
+ while (!string.IsNullOrWhiteSpace(dir))
+ {
+ string gameBin = Path.Combine(dir, "GameBinaries");
+ if (Directory.Exists(gameBin))
+ return gameBin;
+
+ dir = Path.GetDirectoryName(dir);
+ }
+ throw new Exception("GetGameBinaries failed to find a folder named GameBinaries in the directory tree");
+ }
+
+ private static readonly TorchAssemblyResolver _torchResolver =
+ new TorchAssemblyResolver(GetGameBinaries());
+
+ #region Binding
+ [Theory]
+ [MemberData(nameof(Getters))]
+ public void TestBindingGetter(FieldRef field)
+ {
+ Assert.True(ReflectionManager.Process(field.Field));
+ if (field.Field.IsStatic)
+ Assert.NotNull(field.Field.GetValue(null));
+ }
+
+ [Theory]
+ [MemberData(nameof(Setters))]
+ public void TestBindingSetter(FieldRef field)
+ {
+ Assert.True(ReflectionManager.Process(field.Field));
+ if (field.Field.IsStatic)
+ Assert.NotNull(field.Field.GetValue(null));
+ }
+
+ [Theory]
+ [MemberData(nameof(Invokers))]
+ public void TestBindingInvoker(FieldRef field)
+ {
+ Assert.True(ReflectionManager.Process(field.Field));
+ if (field.Field.IsStatic)
+ Assert.NotNull(field.Field.GetValue(null));
+ }
+ #endregion
+
+ #region Results
+ #region Dummy
+ private class ReflectionTestTarget
+ {
+ public int TestField;
+ public int TestProperty { get; set; }
+
+ ///
+ /// Return true when greater or equal than 0
+ ///
+ public bool TestCall(int k)
+ {
+ return k >= 0;
+ }
+
+ public static int TestFieldStatic;
+ public static int TestPropertyStatic { get; set; }
+
+ ///
+ /// Return true when greater or equal than 0
+ ///
+ public static bool TestCallStatic(int k)
+ {
+ return k >= 0;
+ }
+ }
+
+ private class ReflectionTestBinding
+ {
+ [ReflectedGetter(Name = "TestField")]
+ public static Func TestFieldGetter;
+ [ReflectedSetter(Name = "TestField")]
+ public static Action TestFieldSetter;
+
+ [ReflectedGetter(Name = "TestProperty")]
+ public static Func TestPropertyGetter;
+ [ReflectedSetter(Name = "TestProperty")]
+ public static Action TestPropertySetter;
+
+ [ReflectedMethod]
+ public static Func TestCall;
+
+
+ [ReflectedGetter(Name = "TestFieldStatic", Type = typeof(ReflectionTestTarget))]
+ public static Func TestStaticFieldGetter;
+ [ReflectedSetter(Name = "TestFieldStatic", Type = typeof(ReflectionTestTarget))]
+ public static Action TestStaticFieldSetter;
+
+ [ReflectedGetter(Name = "TestPropertyStatic", Type = typeof(ReflectionTestTarget))]
+ public static Func TestStaticPropertyGetter;
+ [ReflectedSetter(Name = "TestPropertyStatic", Type = typeof(ReflectionTestTarget))]
+ public static Action TestStaticPropertySetter;
+
+ [ReflectedStaticMethod(Type = typeof(ReflectionTestTarget))]
+ public static Func TestCallStatic;
+ }
+ #endregion
+
+ private readonly Random _rand = new Random();
+ private int AcquireRandomNum()
+ {
+ return _rand.Next();
+ }
+
+ #region Instance
+ [Fact]
+ public void TestInstanceFieldGet()
+ {
+ ReflectionManager.Process(typeof(ReflectionTestBinding));
+ int testNumber = AcquireRandomNum();
+ var target = new ReflectionTestTarget
+ {
+ TestField = testNumber
+ };
+ Assert.Equal(testNumber, ReflectionTestBinding.TestFieldGetter.Invoke(target));
+ }
+ [Fact]
+ public void TestInstanceFieldSet()
+ {
+ ReflectionManager.Process(typeof(ReflectionTestBinding));
+ int testNumber = AcquireRandomNum();
+ var target = new ReflectionTestTarget();
+ ReflectionTestBinding.TestFieldSetter.Invoke(target, testNumber);
+ Assert.Equal(testNumber, target.TestField);
+ }
+
+ [Fact]
+ public void TestInstancePropertyGet()
+ {
+ ReflectionManager.Process(typeof(ReflectionTestBinding));
+ int testNumber = AcquireRandomNum();
+ var target = new ReflectionTestTarget
+ {
+ TestProperty = testNumber
+ };
+ Assert.Equal(testNumber, ReflectionTestBinding.TestPropertyGetter.Invoke(target));
+ }
+
+ [Fact]
+ public void TestInstancePropertySet()
+ {
+ ReflectionManager.Process(typeof(ReflectionTestBinding));
+ int testNumber = AcquireRandomNum();
+ var target = new ReflectionTestTarget();
+ ReflectionTestBinding.TestPropertySetter.Invoke(target, testNumber);
+ Assert.Equal(testNumber, target.TestProperty);
+ }
+
+ [Fact]
+ public void TestInstanceInvoke()
+ {
+ ReflectionManager.Process(typeof(ReflectionTestBinding));
+ var target = new ReflectionTestTarget();
+ Assert.True(ReflectionTestBinding.TestCall.Invoke(target, 1));
+ Assert.False(ReflectionTestBinding.TestCall.Invoke(target, -1));
+ }
+ #endregion
+
+ #region Static
+ [Fact]
+ public void TestStaticFieldGet()
+ {
+ ReflectionManager.Process(typeof(ReflectionTestBinding));
+ int testNumber = AcquireRandomNum();
+ ReflectionTestTarget.TestFieldStatic = testNumber;
+ Assert.Equal(testNumber, ReflectionTestBinding.TestStaticFieldGetter.Invoke());
+ }
+ [Fact]
+ public void TestStaticFieldSet()
+ {
+ ReflectionManager.Process(typeof(ReflectionTestBinding));
+ int testNumber = AcquireRandomNum();
+ ReflectionTestBinding.TestStaticFieldSetter.Invoke(testNumber);
+ Assert.Equal(testNumber, ReflectionTestTarget.TestFieldStatic);
+ }
+
+ [Fact]
+ public void TestStaticPropertyGet()
+ {
+ ReflectionManager.Process(typeof(ReflectionTestBinding));
+ int testNumber = AcquireRandomNum();
+ ReflectionTestTarget.TestPropertyStatic = testNumber;
+ Assert.Equal(testNumber, ReflectionTestBinding.TestStaticPropertyGetter.Invoke());
+ }
+
+ [Fact]
+ public void TestStaticPropertySet()
+ {
+ ReflectionManager.Process(typeof(ReflectionTestBinding));
+ int testNumber = AcquireRandomNum();
+ ReflectionTestBinding.TestStaticPropertySetter.Invoke(testNumber);
+ Assert.Equal(testNumber, ReflectionTestTarget.TestPropertyStatic);
+ }
+
+ [Fact]
+ public void TestStaticInvoke()
+ {
+ ReflectionManager.Process(typeof(ReflectionTestBinding));
+ Assert.True(ReflectionTestBinding.TestCallStatic.Invoke(1));
+ Assert.False(ReflectionTestBinding.TestCallStatic.Invoke(-1));
+ }
+ #endregion
+ #endregion
+
+ #region FieldProvider
+ public struct FieldRef
+ {
+ public FieldInfo Field;
+
+ public FieldRef(FieldInfo f)
+ {
+ Field = f;
+ }
+
+ public override string ToString()
+ {
+ return Field.DeclaringType?.FullName + "." + Field.Name;
+ }
+ }
+
+ private static bool _init = false;
+ private static HashSet