// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. using System; using System.Reflection; using System.Runtime.InteropServices; namespace FlaxEngine.Tests { /// /// Tests for scripting. /// public class TestScripting { /// /// Tests all usages in the engine to verify all bindigns are correct to work with P/Invoke. /// public static int TestLibraryImports() { var result = 0; var libraryName = "FlaxEngine"; var library = NativeLibrary.Load(NativeInterop.nativeLibraryPaths[libraryName]); if (library == IntPtr.Zero) return -1; var types = typeof(FlaxEngine.Object).Assembly.GetTypes(); foreach (var type in types) { var methods = type.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); foreach (var method in methods) { var libraryImport = method.GetCustomAttribute(); if (libraryImport == null || libraryImport.LibraryName != libraryName || libraryImport.EntryPoint == null) continue; bool found = NativeLibrary.TryGetExport(library, libraryImport.EntryPoint, out var addr); if (!found) { Debug.LogError("Missing library import: " + libraryImport.EntryPoint + " on " + type.FullName + "::" + method.Name); result++; } } } NativeLibrary.Free(library); return result; } } } namespace FlaxEngine { partial struct TestStruct : System.IEquatable { /// public static bool operator ==(TestStruct left, TestStruct right) { return left.Equals(right); } /// public static bool operator !=(TestStruct left, TestStruct right) { return !left.Equals(right); } /// public bool Equals(TestStruct other) { return Vector.Equals(other.Vector) && Equals(Object, other.Object); } /// public override bool Equals(object obj) { return obj is TestStruct other && Equals(other); } /// public override int GetHashCode() { unchecked { return (Vector.GetHashCode() * 397) ^ (Object != null ? Object.GetHashCode() : 0); } } /// public override string ToString() { return $"Vector={Vector}, Object={Object?.ToString() ?? "null"}"; } } /// /// Test class. /// public class TestClassManaged : TestClassNative { TestClassManaged() { // Test setting C++ values from C# SimpleField = 2; SimpleStruct = new TestStruct { Vector = Float3.UnitX, Object = this, }; SimpleEvent += OnSimpleEvent; } /// public override int TestMethod(string str, ref TestStructPOD pod, TestStruct[] struct1, ref TestStruct[] struct2, out Object[] objects) { objects = new Object[3]; if (struct1 == null || struct1.Length != 1) return -1; if (struct2 == null || struct2.Length != 1) return -2; if (pod.Vector != Float3.One) return -3; struct2 = new TestStruct[2] { struct1[0], SimpleStruct, }; pod.Vector = Float3.Half; // Test C++ base method invocation return str.Length + base.TestMethod(str, ref pod, struct1, ref struct2, out _); } /// public override int TestInterfaceMethod(string str) { // Test C++ base method invocation return str.Length + base.TestInterfaceMethod(str); } private void OnSimpleEvent(int arg1, Float3 arg2, string arg3, ref string arg4, TestStruct[] arg5, ref TestStruct[] arg6) { // Verify that C++ passed proper data to C# via event bindings if (arg1 == 1 && arg2 == Float3.One && arg3 == "1" && arg4 == "2" && arg5 != null && arg5.Length == 1 && arg5[0] == SimpleStruct && arg6 != null && arg6.Length == 1 && arg6[0] == SimpleStruct) { // Test passing data back from C# to C++ SimpleField = 4; arg4 = "4"; arg6 = new TestStruct[2] { new TestStruct { Vector = Float3.Half, Object = null, }, SimpleStruct, }; } } } /// /// Test interface in C#. /// public class TestInterfaceManaged : Object, ITestInterface { /// public int TestInterfaceMethod(string str) { return str.Length; } } }