123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157 |
- // ***********************************************************************
- // Copyright (c) 2017 Unity Technologies. All rights reserved.
- //
- // Licensed under the ##LICENSENAME##.
- // See LICENSE.md file in the project root for full license information.
- // ***********************************************************************
- using NUnit.Framework;
- using Autodesk.Fbx;
- using System.Collections.Generic;
- namespace Autodesk.Fbx.UnitTests
- {
- static class CppMatchingHelper
- {
- public delegate T Factory<T>(double[] expected);
- public delegate T TestCommand<T>(T a, T b);
- public delegate bool AreSimilar<T>(T a, T b);
- /**
- * Help tests like FbxVector4Test verify that the FbxSharp
- * implementation of an arithmetic type matches the C++ FBX SDK
- * implementation.
- *
- * E.g. test that the C# and C++ FbxVector4 implementations match.
- *
- * The scheme is that the build system compiles and runs a C++ test
- * program (e.g. Vectors.cpp), which outputs lines in a format:
- * context:command:double,double,double...
- *
- * Context and command are strings with no colons in them.
- *
- * Each double is encoded as
- * description/exact
- * where 'description' is a human-readable double (e.g. 1.002452) and
- * exact is a 64-bit hex string that has the exact bits of a double.
- *
- * Each line is the result of running a unary or binary operation (the
- * command) on two variables 'a' and 'b'. The command "a" should be
- * issued first, followed by the command "b". These initialize the two
- * variables.
- *
- * Callers must provide:
- * - the file that the C++ test program creates
- * - the 'context' that matches; we ignore lines for other contexts
- * - a factory to convert an array of doubles to the type you are
- * testing (e.g. FbxVector4)
- * - a map from 'command' values to lambda functions that implement that command
- * (except for commands "a" and "b")
- * - optionally, a map from 'command' values to lambda functions that
- * compare results (to allow inexact comparison, e.g. for differences
- * in rounding).
- *
- * The C++ test must call every command.
- */
- public static void MatchingTest<T>(
- string filename,
- string test_context,
- Factory<T> factory,
- Dictionary<string, TestCommand<T>> commands,
- Dictionary<string, AreSimilar<T>> custom_comparators = null
- ) where T : new()
- {
- var a = new T();
- var b = new T();
- var commands_used = new HashSet<string>();
- using (var file = new System.IO.StreamReader(filename)) {
- string line;
- while ( null != (line = file.ReadLine()) ) {
- string context;
- string command;
- double [] expectedArray;
- ParseLine(line, out context, out command, out expectedArray);
- if (context != test_context) {
- continue;
- }
- var expected = factory(expectedArray);
- // Perform the command, depending what it is.
- T actualValue;
- if (command == "a") {
- actualValue = a = expected;
- } else if (command == "b") {
- actualValue = b = expected;
- } else {
- commands_used.Add(command);
- Assert.IsTrue(commands.ContainsKey(command), "unknown command " + command);
- actualValue = commands[command](a, b);
- }
- // Make sure we got the expected result.
- if (custom_comparators != null && custom_comparators.ContainsKey(command)) {
- var comp = custom_comparators[command];
- Assert.IsTrue(comp(expected, actualValue), command);
- } else {
- Assert.AreEqual(expected, actualValue, command);
- }
- }
- }
- // Make sure we actually called all those commands.
- Assert.That(commands_used, Is.EquivalentTo(commands.Keys));
- }
- #if ENABLE_COVERAGE_TEST
- /**
- * The coverage tester won't track the calls that MatchingTests makes
- * to the lambda functions. Call this function in your static
- * constructor to register the calls.
- *
- * This is accurate because MatchingTests makes sure that every command
- * was actually issued.
- */
- public static void RegisterLambdaCalls<T>(
- System.Reflection.MethodInfo caller,
- Dictionary<string, TestCommand<T>> commands)
- {
- foreach(var lambda in commands.Values) {
- CoverageTester.RegisterReflectionCall(caller, lambda.Method);
- }
- }
- #endif
- // Parse one line in the file.
- static void ParseLine(string line,
- out string out_context,
- out string out_command,
- out double [] out_expected)
- {
- // Parse the whole colon-separated line:
- // file.cpp:5:a + 2:6.71089e+07/0x419000000c000000,6.7...
- var items = line.Split(':');
- Assert.AreEqual(items.Length, 3);
- out_context = items[0];
- out_command = items[1];
- // now parse the comma-separated doubles:
- // 6.71089e+07/0x419000000c000000,6.71089e+07/0x4190000010000000,...
- var doubles = items[2];
- items = doubles.Split(',');
- out_expected = new double[items.Length];
- for(int i = 0, n = items.Length; i < n; ++i) {
- // parse one double: 6.71089e+07/0x419000000c000000
- // we ignore the printed double, just take its exact 64-bit representation.
- var pair = items[i].Split('/');
- Assert.AreEqual(2, pair.Length);
- var asInt = System.Convert.ToInt64(pair[1], 16);
- var asDouble = System.BitConverter.Int64BitsToDouble(asInt);
- out_expected[i] = asDouble;
- }
- }
- }
- }
|