private void ReportIgnoredMember(
            string propertyName, TDestination dest, MapMemberTester <TSource, TDestination> memberTester)
        {
            object actual = memberTester.GetActualValue(dest);

            _log($"\t{propertyName} = {FormatValue(actual)}");
        }
        /// <summary>
        /// Returns a String which represents the MapTester instance.
        /// </summary>
        /// <returns>a String which represents the MapTester instance.</returns>
        public override string ToString()
        {
            try
            {
                string[] ignoredProperties = _destProperties.Where(x => MemberShouldBeIgnored(x.Name)).Select(x => x.Name).ToArray();

                var sb = new StringBuilder($"\tMapTester.ForMap<{typeof(TSource).Name}, {typeof(TDestination).Name}>()");

                foreach (string propertyName in _destProperties.Select(x => x.Name).Where(x => !ignoredProperties.Contains(x)))
                {
                    if (_memberTesters.ContainsKey(propertyName))
                    {
                        MapMemberTester <TSource, TDestination> mapMemberTester = _memberTesters[propertyName];
                        string testDescription = mapMemberTester.TestDescription;
                        if (!string.IsNullOrWhiteSpace(testDescription))
                        {
                            sb.Append($"\n\t\t.WhereMember(dest => dest.{propertyName}).{testDescription}");
                        }
                    }
                }

                if (ignoredProperties.Length > 0)
                {
                    sb.Append($"\n\t\t.IgnoringMember{((ignoredProperties.Length > 1) ? "s" : "")}(dest => dest.");
                    sb.Append(string.Join(", dest => dest.", ignoredProperties));
                    sb.Append(")");
                }

                return(sb.ToString());
            }
            catch
            {
                return(GetType().FullName);
            }
        }
        /// <summary>
        /// Assert that all defined properties were successfully maped
        /// from <typeparamref name="TSource"/> to <typeparamref name="TDestination"/>.
        /// </summary>
        /// <param name="source">An instance of type <typeparamref name="TDestination"/>.</param>
        /// <param name="dest">An instance of type <typeparamref name="TDestination"/>.</param>
        /// <exception cref="ScenarioTestFailureException">if dest properties don't have expected values.</exception>
        /// <example>
        /// <code><![CDATA[
        ///     MapTester
        ///         .ForMap<Foo, Bar>()
        ///         .AssertMappedValues(foo, bar);
        /// ]]>
        /// </code>
        /// </example>
        public void AssertMappedValues(TSource source, TDestination dest)
        {
            string sourceName = typeof(TSource).FullName;
            string destName   = typeof(TDestination).FullName;

            _log($"Asserting mapping from \n{sourceName} to \n{destName}:");

            _destProperties
            .OrderBy(x => x.Name)
            .Select(x => x.Name)
            .TestEach(propertyName =>
            {
                if (_memberTesters.ContainsKey(propertyName))
                {
                    MapMemberTester <TSource, TDestination> memberTester = _memberTesters[propertyName];

                    if (memberTester.ShouldBeIgnored)
                    {
                        ReportIgnoredMember(propertyName, dest, memberTester);
                    }
                    else
                    {
                        AssertMappedValue(propertyName, source, dest, memberTester);
                    }
                }
                else
                {
                    throw new MapTesterException($"Property \"{propertyName}\" was not tested.");
                }
            });
        }
        /// <summary>
        /// Specify <typeparamref name="TDestination"/> property to be tested.
        /// </summary>
        /// <param name="destExpression">Expression to get property name.</param>
        /// <returns>New <see cref="MapMemberTester{TSource, TDestination}"/> instance.</returns>
        /// <example><![CDATA[
        ///     MapTester.ForMap<Foo, Bar>()
        ///         .WhereMember(dest => dest.Baz).ShouldEqual(src => src.Qux)
        ///         .AssertMappedValues(foo, bar);
        /// ]]></example>
        public MapMemberTester <TSource, TDestination> WhereMember(Expression <Func <TDestination, object> > destExpression)
        {
            string propertyName = GetPropertyName(destExpression);

            var memberTester = new MapMemberTester <TSource, TDestination>(this, destExpression.Compile());

            SetTesterForProperty(propertyName, memberTester);

            return(memberTester);
        }
 /// <summary>
 /// Define <see cref="MapMemberTester{TSource, TDestination}"/> for
 /// <typeparamref name="TDestination"/> property.
 /// </summary>
 /// <remarks>
 /// This method is usually called by this class's constructor and by the
 /// "WhereMember" and "IgnoreMember" extension methods.
 /// </remarks>
 /// <param name="propertyName">The <typeparamref name="TDestination"/> property name.</param>
 /// <param name="memberTester">The <see cref="MapMemberTester{TSource, TDestination}"/> to be used
 /// to test mapping for the property.</param>
 private void SetTesterForProperty(string propertyName, MapMemberTester <TSource, TDestination> memberTester)
 {
     if (_memberTesters.ContainsKey(propertyName))
     {
         _memberTesters[propertyName] = memberTester;
     }
     else
     {
         _memberTesters.Add(propertyName, memberTester);
     }
 }
        private void TestMappedProperties(TSource source, TDestination dest)
        {
            _destProperties
            .OrderBy(x => x.Name)
            .Select(x => x.Name)
            .TestEach(propertyName =>
            {
                MapMemberTester <TSource, TDestination> memberTester = (_memberTesters.ContainsKey(propertyName))
                        ? _memberTesters[propertyName]
                        : null;

                bool memberShouldBeIgnored = MemberShouldBeIgnored(propertyName, memberTester);

                if (memberTester == null)
                {
                    string message = $"Property \"{propertyName}\" was not tested.";
                    if (memberShouldBeIgnored)
                    {
                        _log(message);
                    }
                    else
                    {
                        _untestedProperties.Add(propertyName);
                        throw new MapTesterException(message);
                    }
                }
                else
                {
                    if (memberShouldBeIgnored)
                    {
                        ReportIgnoredMember(propertyName, dest, memberTester);
                    }
                    else
                    {
                        AssertMappedValue(propertyName, source, dest, memberTester);
                    }
                }
            });
        }
        private void AssertMappedValue(string propertyName, TSource source, TDestination dest,
                                       MapMemberTester <TSource, TDestination> memberTester)
        {
            if (memberTester.CustomTest == null)
            {
                object expected = memberTester.GetExpectedValue(source);
                object actual   = memberTester.GetActualValue(dest);

                if ((actual == null && expected == null) || (actual != null && actual.Equals(expected)))
                {
                    _log($"\t{propertyName} = {FormatValue(actual)}");
                }
                else
                {
                    AssertAreEqual(propertyName, expected, actual);
                }
            }
            else
            {
                object actual = memberTester.GetActualValue(dest);
                _log($"\t{propertyName} = {FormatValue(actual)}");
                memberTester.CustomTest(source, dest);
            }
        }
 private bool MemberShouldBeIgnored(string propertyName, MapMemberTester <TSource, TDestination> memberTester)
 {
     return((memberTester?.ShouldBeIgnored ?? false) ||
            _ignoringAllOtherMembers ||
            _ignoringMemberNamesStartingWith.Any(x => propertyName.StartsWith(x)));
 }