예제 #1
0
        /// <summary>
        /// Annotates a type as being a test fixture conditional on a connection being available.
        /// </summary>
        /// <param name="target">The type where an <see cref="IDbConnection"/> instance can be retrieved.</param>
        /// <param name="propertyName">The name of the property in <paramref name="target"/> where an <see cref="IDbConnection"/> instance can be retrieved.</param>
        /// <param name="ignoreMessage">The message to display when a test has been skipped due a missing connection.</param>
        public DatabaseTestFixtureAttribute(Type target, string propertyName, string ignoreMessage)
        {
            if (target == null || string.IsNullOrWhiteSpace(propertyName) || string.IsNullOrWhiteSpace(ignoreMessage))
            {
                return;
            }

            var propCache = TypeCache.GetOrAdd(target, new ConcurrentDictionary <string, MethodInfo?>());

            var getMethod = propCache.GetOrAdd(propertyName, propName =>
            {
                var propInfo = target.GetProperty(propName, SearchFlags);
                if (propInfo != null && (!IDbConnectionFactoryType.IsAssignableFrom(propInfo.PropertyType) || propInfo.GetGetMethod() == null))
                {
                    propInfo = null;
                }

                return(propInfo?.GetGetMethod());
            });

            if (getMethod == null)
            {
                return;
            }

            if (ResultCache.TryGetValue(getMethod, out var cachedResult) && !cachedResult)
            {
                Ignore = ignoreMessage;
                return;
            }

            IDbConnectionFactory factory;

            try
            {
                var methodResult = getMethod.Invoke(null, Array.Empty <object>());
                if (methodResult is null || !(methodResult is IDbConnectionFactory connFactory))
                {
                    ResultCache.AddOrUpdate(getMethod, false, (_, __) => false);
                    Ignore = ignoreMessage;
                    return;
                }
                factory = connFactory;
            }
            catch
            {
                ResultCache.AddOrUpdate(getMethod, false, (_, __) => false);
                Ignore = ignoreMessage;
                return;
            }

            var hasConnection = false;

            try
            {
                using var connection = factory.OpenConnection();
                hasConnection        = true;
            }
            catch
            {
                // ignore any connection failure
            }

            var isEnabled = ResultCache.AddOrUpdate(getMethod, hasConnection, (_, __) => hasConnection);

            if (!isEnabled)
            {
                Ignore = ignoreMessage;
            }
        }