Example #1
0
        public static bool AreEqual(object expected, object actual, out string messageIfNot)
        {
            // There might be a better way to do this (a testing library for that could be made to work with H5 but this should suffice for now)
            var jsonExpected = JsonSerialiserForComparison.ToJson(expected);
            var jsonActual   = JsonSerialiserForComparison.ToJson(actual);

            if (jsonExpected == jsonActual)
            {
                messageIfNot = null;
                return(true);
            }

            // 2020-06-08 DWR: There is a bug in in Bridge/H5 where the precision/interpretation of floating point numbers goes awry - this is accounted for when we read the values when decoding but if we're comparing those results to expected values that
            // are suffering from precision issues then we'll get a false negative. The way around it is to apply the same hack to the expected value (if both it and the actual are floats and if they weren't already found to match) and then compare again.
            if ((expected is float expectedFloat) && (actual is float actualFloat))
            {
                expectedFloat = float.Parse(((float)expectedFloat).ToString());
                if (expectedFloat.ToString() == actualFloat.ToString())
                {
                    messageIfNot = null;
                    return(true);
                }
                messageIfNot = $"Floating point number values were too far apart - expected {expected} vs {actual}";
                return(false);
            }

            // 2020-06-08 DWR: We're going to have to accept that the DateTime's Kind value may not be maintained (it doesn't survive a roundtrip via MessagePack-CSharp) and so we'll need some extra logic
            if ((expected is DateTime expectedDateTime) && (actual is DateTime actualDateTime))
            {
                if (expectedDateTime.Ticks == actualDateTime.Ticks)
                {
                    messageIfNot = null;
                    return(true);
                }
                messageIfNot = $"DateTime values did not match - expected {expected} vs {actual}";
                return(false);
            }

            messageIfNot = $"Expected {jsonExpected} but received {jsonActual}";
            return(false);
        }
Example #2
0
        private static bool ExecuteTest(TestDetails testItemDetails, bool showDetailedInformation, HTMLElement appendSuccessesTo, HTMLElement appendFailuresTo)
        {
            try
            {
                var testItem = TestItemInstanceCreator.GetInstance(testItemDetails.FullName);
                var decoder  = GetTypedMessagePackSerializerDeserializeCall(testItem.DeserialiseAs, testItem.DecodeOptions);
                if (testItemDetails.ExpectedError is null)
                {
                    var clone          = decoder(testItemDetails.Serialised);
                    var expectedResult = (testItemDetails.AlternateResultJson is null) ? testItem.Value : DeserialiseAlternateResultJson(testItemDetails.AlternateResultJson);
                    if (ObjectComparer.AreEqual(expectedResult, clone, out var messageIfNotEqual))
                    {
                        RenderSuccess($"Expected and received: {JsonSerialiserForComparison.ToJson(expectedResult)}");
                        return(true);
                    }
                    RenderFailure(messageIfNotEqual);
                    return(false);
                }
                else
                {
                    try
                    {
                        decoder(testItemDetails.Serialised);
                        RenderFailure("No exception was thrown but expected: " + testItemDetails.ExpectedError.ExceptionType.FullName);
                        return(false);
                    }
                    catch (Exception deserialisationException)
                    {
                        if (deserialisationException.GetType() != testItemDetails.ExpectedError.ExceptionType)
                        {
                            var additionalInfo = $"Expected exception {testItemDetails.ExpectedError.ExceptionType.FullName} but {deserialisationException.GetType().FullName} was thrown";
                            if (showDetailedInformation)
                            {
                                additionalInfo += "\n\n" + deserialisationException.ToString();
                            }
                            RenderFailure(additionalInfo);
                            return(false);
                        }
                        if (deserialisationException.Message != testItemDetails.ExpectedError.Message)
                        {
                            var additionalInfo = $"Expected exception message \"{testItemDetails.ExpectedError.Message}\" but received \"{deserialisationException.Message}\"";
                            if (showDetailedInformation)
                            {
                                additionalInfo += "\n\n" + deserialisationException.ToString();
                            }
                            RenderFailure(additionalInfo);
                            return(false);
                        }
                        RenderSuccess($"Expected and received error: {deserialisationException.Message}");
                        return(true);
                    }
                }
            }
            catch (Exception e)
            {
                RenderFailure(e.Message, e.ToString());
            }
            return(false);

            void RenderSuccess(string extendedAdditionalInfo)
            {
                appendSuccessesTo.appendChild(
                    GetMessage(testItemDetails.DisplayName, hrefIfTextShouldLink: GetHrefForFilteringToTest(testItemDetails.DisplayName), isSuccess: true, additionalInfo: showDetailedInformation ? extendedAdditionalInfo : null)
                    );
            }

            void RenderFailure(string summary, string extendedAdditionalInfo = null)
            {
                appendFailuresTo.appendChild(
                    GetMessage(testItemDetails.DisplayName, hrefIfTextShouldLink: GetHrefForFilteringToTest(testItemDetails.DisplayName), isSuccess: false, additionalInfo: showDetailedInformation ? (extendedAdditionalInfo ?? summary) : summary)
                    );
            }
        }
Example #3
0
        private static void Main(string[] args)
        {
            var projectFolderName = args?.FirstOrDefault();

            if (string.IsNullOrWhiteSpace(projectFolderName))
            {
                projectFolderName = Directory.GetCurrentDirectory();
                Console.WriteLine("Expected command line argument to specify project folder location to write Test Data file to - default to build folder " + projectFolderName);
            }
            var projectFolder = new DirectoryInfo(projectFolderName);

            if (!projectFolder.Exists)
            {
                throw new Exception("The specified project folder must already exist but it does not: " + projectFolderName);
            }

            var allTestItemTypes = AppDomain.CurrentDomain.GetAssemblies()
                                   .SelectMany(a => a.GetTypes())
                                   .Where(t => !t.IsAbstract && typeof(ITestItem).IsAssignableFrom(t))
                                   .OrderBy(t => t.FullName);
            var testItemEntries = new List <(string testItemTypeNameLiteral, string byteArrayRepresentation, string alternateResultJson, string errorRepresentation)>();

            foreach (var testItemType in allTestItemTypes)
            {
                var    testItem = TestItemInstanceCreator.GetInstance(testItemType.FullName);
                var    serialised = MessagePackSerializer.Serialize(type: testItem.SerialiseAs, obj: testItem.Value);
                string alternateResultJson, errorRepresentation;
                try
                {
                    var deserialised = MessagePackSerializer.Deserialize(type: testItem.DeserialiseAs, bytes: serialised);
                    if (!ObjectComparer.AreEqual(deserialised, testItem.Value, out _))
                    {
                        // h5's JsonConvert has a bug where JsonConvert.DeserializeObject<object>("12") will be deserialised into a string instead of an Int64 and this would cause a problem if we were, for example, serialising a byte and then wanting to deserialise
                        // it into an int because the ObjectComparer will be able to tell that they are not precisely the same value and so an alternateResultJson value would be written but this would cause a problem because the h5 Unit Tests code would see that
                        // alternateResultJson and think that the result should be a string (due to that bug). So, to avoid that confusion, if the JSON of the original value matches the JSON of the different value - because that indicates a case that we don't
                        // care about (which should only be primitives).
                        if (JsonSerialiserForComparison.ToJson(deserialised) == JsonSerialiserForComparison.ToJson(testItem.Value))
                        {
                            alternateResultJson = null;
                        }
                        else
                        {
                            alternateResultJson = ToLiteral(JsonSerialiserForComparison.ToJson(deserialised));
                        }
                    }
                    else
                    {
                        alternateResultJson = "null";
                    }
                    errorRepresentation = "null";
                }
                catch (Exception e)
                {
                    // 2020-07-25 DWR: By only checking the top level exception type and its messages, it means that the inner exception content may not be precisely the same but I'm happy with that (eg. when trying to deserialise to a type that has multiple
                    // properties with the same Key then the .NET library will throw a MessagePackSerializationException that has an InnerException that references the FormatterCache`1 and that will have an InnerException that describes the repeated key issue
                    // whereas this library will throw a MessagePackSerializationException with the same message as the C# version but wrap a RepeatedKeyValueException instance - that's close enough to like-for-like behaviour for me)
                    errorRepresentation = $"new ExceptionSummary(TypeRetriever.Get(\"{e.GetType().FullName}\"), {ToLiteral(e.Message)})";
                    alternateResultJson = null;
                }
                testItemEntries.Add((
                                        "\"" + testItem.GetType().FullName + "\"",
                                        "new byte[] { " + string.Join(", ", serialised) + " }",
                                        alternateResultJson,
                                        errorRepresentation
                                        ));
            }

            var content = new StringBuilder();

            content.AppendLine("using System.Collections.Generic;");
            content.AppendLine();
            content.AppendLine("namespace " + _testDataNamespace);
            content.AppendLine("{");
            content.AppendLine("    // This file is generated by the UnitTestDataGenerator project - run that to ensure that this is current (includes all of the tests in the SharedTestItems project) and then run the Unit Tests project");
            content.AppendLine("    internal static class TestData");
            content.AppendLine("    {");
            content.AppendLine("        public static IEnumerable<(string TestItemName, byte[] Serialised, string AlternateResultJson, ExceptionSummary ExpectedError)> GetItems()");
            content.AppendLine("        {");
            foreach (var(testItemTypeNameLiteral, byteArrayRepresentation, alternateResultJson, errorRepresentation) in testItemEntries)
            {
                content.AppendLine($"            yield return ({testItemTypeNameLiteral}, {byteArrayRepresentation}, {alternateResultJson ?? "null"}, {errorRepresentation});");
            }
            content.AppendLine("        }");
            content.AppendLine("    }");
            content.AppendLine("}");

            File.WriteAllText(
                Path.Combine(projectFolder.FullName, _testDataFilename),
                content.ToString()
                );
        }