예제 #1
0
        private void Equals_DifferenceOnPropertyChange(
            LogFileMessage message,
            Expression <Func <LogFileMessage, object> > property,
            object valueToSet)
        {
            // create a copy of the message using the copy constructor
            // => the copy should equal the message
            var  otherMessage = new LogFileMessage(message);
            bool isEqual      = message.Equals(otherMessage);

            Assert.True(isEqual);

            // set property on the message copy
            SetProperty(otherMessage, property, valueToSet);

            // the messages should be different now

            // check using Equals(LogMessage other)
            isEqual = message.Equals(otherMessage);
            Assert.False(isEqual);

            // check using Equals(ILogMessage other)
            isEqual = message.Equals((ILogMessage)otherMessage);
            Assert.False(isEqual);
        }
예제 #2
0
        /// <summary>
        /// Tests getting a property of the <see cref="LogMessage"/> class.
        /// </summary>
        /// <param name="property">Property to test.</param>
        /// <param name="expectedDefaultValue">Expected default value of the property.</param>
        /// <param name="protect">true to protect the log message before setting the property; otherwise false.</param>
        private static void TestPropertyGetter(
            Expression <Func <LogFileMessage, object> > property,
            object expectedDefaultValue,
            bool protect)
        {
            var message = new LogFileMessage();

            // extract information about the property to work with
            PropertyInfo propertyInfo;

            switch (property.Body.NodeType)
            {
            case ExpressionType.MemberAccess:
                propertyInfo = (PropertyInfo)((MemberExpression)property.Body).Member;
                break;

            case ExpressionType.Convert:
                propertyInfo = (PropertyInfo)((MemberExpression)((UnaryExpression)property.Body).Operand).Member;
                break;

            default:
                throw new NotImplementedException();
            }

            // invoke the getter of the property and compare to the expected default value
            object value = propertyInfo.GetValue(message);

            Assert.Equal(expectedDefaultValue, value);
        }
예제 #3
0
        /// <summary>
        /// Creates a new log message and prepares it for asynchronous initialization.
        /// (<see cref="LogMessage.IsInitialized"/> is <c>false</c> at first and set to <c>true</c> as soon as the message is initialized).
        /// </summary>
        /// <param name="readOnly">
        /// true to create a read-only message that can only be set by the returned initializer;
        /// otherwise false.
        /// </param>
        /// <param name="initializer">Receives the initializer that allows to update the log message.</param>
        /// <returns>The created log message.</returns>
        public static LogFileMessage CreateWithAsyncInit(bool readOnly, out IFileLogMessageInitializer initializer)
        {
            var message = new LogFileMessage
            {
                IsInitializedInternal = false,
                IsAsyncInitPending    = true,
                IsReadOnlyInternal    = readOnly
            };

            initializer = message;
            return(message);
        }
            /// <summary>
            /// Reads a log message from the specified sqlite reader.
            /// </summary>
            /// <param name="reader">Sqlite reader to read from.</param>
            /// <returns>The read log message (is protected).</returns>
            private LogFileMessage ReadLogMessage(SQLiteDataReader reader)
            {
                // columns in result:
                // 0 = message id
                // 1 = timestamp
                // 2 = timezone offset
                // 3 = high precision timestamp
                // 4 = lost message count
                // 5 = process id
                // 6 = process name
                // 7 = application name
                // 8 = log writer name
                // 9 = log level name
                // 10 = has tags
                // 11 = text name

                long   messageId              = reader.GetInt64(0);
                var    timezoneOffset         = TimeSpan.FromTicks(reader.GetInt64(2));
                var    timestamp              = new DateTimeOffset(reader.GetInt64(1) + timezoneOffset.Ticks, timezoneOffset);
                long   highPrecisionTimestamp = reader.GetInt64(3);
                int    lostMessageCount       = reader.GetInt32(4);
                int    processId              = reader.GetInt32(5);
                string processName            = reader.GetString(6);
                string applicationName        = reader.GetString(7);
                string logWriterName          = reader.GetString(8);
                string logLevelName           = reader.GetString(9);
                bool   hasTags = reader.GetBoolean(10);
                string text    = reader.GetString(11);

                var message = new LogFileMessage().InitWith(
                    messageId,
                    timestamp,
                    highPrecisionTimestamp,
                    lostMessageCount,
                    StringPool.Intern(logWriterName),
                    StringPool.Intern(logLevelName),
                    TagSet.Empty,
                    StringPool.Intern(applicationName),
                    StringPool.Intern(processName),
                    processId,
                    text);

                // initialize tags, if there are tags associated with the message
                if (hasTags)
                {
                    message.Tags = GetTagsOfMessage(messageId);
                }

                // protect message from changes
                message.Protect();
                return(message);
            }
예제 #5
0
        /// <summary>
        /// Tests setting a property of the <see cref="LogFileMessage"/> class.
        /// </summary>
        /// <param name="property">Property to test.</param>
        /// <param name="expectedDefaultValue">Expected default value of the property.</param>
        /// <param name="valueToSet">Value of the property after setting it.</param>
        /// <param name="protect">true to protect the log message before setting the property; otherwise false.</param>
        private static void TestPropertySetter_WithoutPropertyChanged(
            Expression <Func <LogFileMessage, object> > property,
            object expectedDefaultValue,
            object valueToSet,
            bool protect)
        {
            var message = new LogFileMessage();

            // extract information about the property to work with
            PropertyInfo propertyInfo;

            switch (property.Body.NodeType)
            {
            case ExpressionType.MemberAccess:
                propertyInfo = (PropertyInfo)((MemberExpression)property.Body).Member;
                break;

            case ExpressionType.Convert:
                propertyInfo = (PropertyInfo)((MemberExpression)((UnaryExpression)property.Body).Operand).Member;
                break;

            default:
                throw new NotImplementedException();
            }

            object value = propertyInfo.GetValue(message);

            Assert.Equal(expectedDefaultValue, value);

            if (protect)
            {
                message.Protect();

                var ex = Assert.Throws <TargetInvocationException>(() => propertyInfo.SetValue(message, valueToSet, null));
                Assert.IsType <NotSupportedException>(ex.InnerException);
                value = propertyInfo.GetValue(message);
                Assert.Equal(expectedDefaultValue, value);
            }
            else
            {
                propertyInfo.SetValue(message, valueToSet, null);
                value = propertyInfo.GetValue(message);
                if (propertyInfo.PropertyType.IsValueType)
                {
                    Assert.Equal(valueToSet, value);
                }
                else
                {
                    Assert.Same(valueToSet, value);
                }
            }
        }
예제 #6
0
        private void CreateWithAsyncInit_CreateOnly(bool readOnly)
        {
            // create a new message with asynchronous initializer
            var message = LogFileMessage.CreateWithAsyncInit(readOnly, out _);

            CheckDefaultState(message, false, readOnly);

            // check that the message is marked for asynchronous initialization
            Assert.True(message.IsAsyncInitPending);

            // check whether the message reflects the desired read-only state
            Assert.Equal(readOnly, message.IsReadOnly);
        }
예제 #7
0
        /// <summary>
        /// Tests whether the specified log message has the expected default state.
        /// </summary>
        /// <param name="message">Log message to check.</param>
        /// <param name="inited">true, if the message is initialized; otherwise false.</param>
        /// <param name="readOnly">true, if the message is readOnly, otherwise false.</param>
        private static void CheckDefaultState(
            LogFileMessage message,
            bool inited   = true,
            bool readOnly = false)
        {
            // check administrative properties
            Assert.Equal(inited, message.IsInitialized);
            Assert.Equal(readOnly, message.IsReadOnly);

            // check message specific properties
            Assert.Equal(-1, message.Id);
            Assert.Equal(0, message.LostMessageCount);
            Assert.Equal(default, message.Timestamp);
예제 #8
0
        private void GetHashCode_DifferenceOnChangedProperty(
            LogFileMessage message,
            Expression <Func <LogFileMessage, object> > property,
            object valueToSet)
        {
            // create a copy of the message using the copy constructor
            // => the copy should have the same hash code
            int messageHashCode      = message.GetHashCode();
            var otherMessage         = new LogFileMessage(message);
            int otherMessageHashCode = otherMessage.GetHashCode();

            Assert.Equal(messageHashCode, otherMessageHashCode);

            // set property on the message copy
            SetProperty(otherMessage, property, valueToSet);

            // the hash code should be different now
            otherMessageHashCode = otherMessage.GetHashCode();
            Assert.NotEqual(messageHashCode, otherMessageHashCode);
        }
예제 #9
0
 /// <summary>
 /// Initializes a new instance of the <see cref="LogFileMessage"/> class copying the specified one.
 /// </summary>
 /// <param name="other">Message to copy.</param>
 public LogFileMessage(LogFileMessage other) : base(other)
 {
     mId = other.Id;
 }
예제 #10
0
        private async Task Protect(bool protectInSameThread, bool withPropertyChanged)
        {
            // create a new message
            var message = new LogFileMessage();

            Assert.False(message.IsReadOnly);
            CheckDefaultState(message, true, false);

            // ensure that the message is not marked for asynchronous initialization
            Assert.False(message.IsAsyncInitPending);

            // prepare data pulling some information out of the event handler
            SynchronizationContext handlerThreadSynchronizationContext = null;
            var changedPropertyNames = new List <string>();
            var handlerCalledEvent   = new ManualResetEventSlim(false);

            // the handler that is expected to be called on changes
            void PropertyChangedHandler(object sender, PropertyChangedEventArgs e)
            {
                handlerThreadSynchronizationContext = SynchronizationContext.Current;
                changedPropertyNames.Add(e.PropertyName);
                handlerCalledEvent.Set();
            }

            // run test in a separate thread that provides a synchronization context that allows to
            // marshal calls into that thread
            await mThread.Factory.Run(() => { Assert.NotNull(SynchronizationContext.Current); });

            // register the PropertyChanged event
            if (withPropertyChanged)
            {
                await mThread.Factory.Run(() => { message.PropertyChanged += PropertyChangedHandler; });
            }

            // callback that initializes the message
            void ProtectTest()
            {
                // protect the message
                message.Protect();

                // check administrative properties
                Assert.True(message.IsInitialized);                       // unchanged
                Assert.False(message.IsAsyncInitPending);                 // unchanged
                Assert.True(message.IsReadOnly);

                if (protectInSameThread)
                {
                    if (withPropertyChanged)
                    {
                        // the event handler should have been called only once in the same thread
                        // (the event handler is called directly as the registering thread is the same as the thread raising the event)
                        Assert.True(handlerCalledEvent.IsSet);
                        Assert.Equal(new[] { "IsReadOnly" }, changedPropertyNames.ToArray());
                    }
                    else
                    {
                        // the event handler should not have been called
                        Assert.False(handlerCalledEvent.IsSet);
                    }
                }
            }

            // protect the message either in the context of the thread that registered the handler
            // or in a different - the current - thread
            if (protectInSameThread)
            {
                await mThread.Factory.Run(ProtectTest);
            }
            else
            {
                ProtectTest();
            }

            if (!protectInSameThread)
            {
                // the thread registering the event and the thread protecting the message are different
                if (withPropertyChanged)
                {
                    // event handler should run in the context of the thread that registered it
                    Assert.True(handlerCalledEvent.Wait(1000));
                    Assert.Same(mThread.Context.SynchronizationContext, handlerThreadSynchronizationContext);
                    Assert.Equal(new[] { "IsReadOnly" }, changedPropertyNames.ToArray());
                }
                else
                {
                    // the event handler should not have been called
                    Assert.False(handlerCalledEvent.Wait(1000));
                }
            }
        }
예제 #11
0
        /// <summary>
        /// Tests setting a property of the <see cref="LogFileMessage"/> class.
        /// </summary>
        /// <param name="property">Property to test.</param>
        /// <param name="expectedDefaultValue">Expected default value of the property.</param>
        /// <param name="valueToSet">Value of the property after setting it.</param>
        /// <param name="protect">true to protect the log message before setting the property; otherwise false.</param>
        /// <param name="changeInSameThread">
        /// true to change the property in the thread that registers the event;
        /// false to change the property and raise the event in some other thread.
        /// </param>
        private async Task TestPropertySetter_WithPropertyChanged(
            Expression <Func <LogFileMessage, object> > property,
            object expectedDefaultValue,
            object valueToSet,
            bool protect,
            bool changeInSameThread)
        {
            var message = new LogFileMessage();

            // extract information about the property to work with
            PropertyInfo propertyInfo;

            switch (property.Body.NodeType)
            {
            case ExpressionType.MemberAccess:
                propertyInfo = (PropertyInfo)((MemberExpression)property.Body).Member;
                break;

            case ExpressionType.Convert:
                propertyInfo = (PropertyInfo)((MemberExpression)((UnaryExpression)property.Body).Operand).Member;
                break;

            default:
                throw new NotImplementedException();
            }

            // check default value of the property
            object value = propertyInfo.GetValue(message);

            Assert.Equal(expectedDefaultValue, value);

            // prepare data pulling some information out of the event handler
            SynchronizationContext handlerThreadSynchronizationContext = null;
            var changedPropertyNames = new List <string>();
            var handlerCalledEvent   = new ManualResetEventSlim(false);

            // the handler that is expected to be called on changes
            void PropertyChangedHandler(object sender, PropertyChangedEventArgs e)
            {
                handlerThreadSynchronizationContext = SynchronizationContext.Current;
                changedPropertyNames.Add(e.PropertyName);
                handlerCalledEvent.Set();
            }

            // run test in a separate thread that provides a synchronization context that allows to
            // marshal calls into that thread
            await mThread.Factory.Run(() => { Assert.NotNull(SynchronizationContext.Current); });

            // register the PropertyChanged event
            await mThread.Factory.Run(() => { message.PropertyChanged += PropertyChangedHandler; });

            // set property
            if (changeInSameThread)
            {
                // registering the event and changing the property is done in the same thread
                // => the event handler should be called directly

                await mThread.Factory.Run(
                    () =>
                {
                    if (protect)
                    {
                        // protect the message
                        // => changes the IsReadOnly property to true
                        // => event handler is called directly
                        message.Protect();
                        Assert.True(handlerCalledEvent.IsSet, "The event handler should have been called directly.");
                        Assert.Same(SynchronizationContext.Current, handlerThreadSynchronizationContext);
                        Assert.Equal(new[] { "IsReadOnly" }, changedPropertyNames.ToArray());
                        handlerCalledEvent.Reset();

                        // the message is protected and setting a property should throw an exception
                        var ex = Assert.Throws <TargetInvocationException>(() => propertyInfo.SetValue(message, valueToSet, null));
                        Assert.IsType <NotSupportedException>(ex.InnerException);

                        // the property value should not have changed
                        value = propertyInfo.GetValue(message);
                        Assert.Equal(expectedDefaultValue, value);

                        // the event handler should not have been called directly
                        Assert.False(handlerCalledEvent.IsSet);
                    }
                    else
                    {
                        // set property
                        propertyInfo.SetValue(message, valueToSet, null);

                        // the property value should not be the same as the set value
                        value = propertyInfo.GetValue(message);
                        if (propertyInfo.PropertyType.IsValueType)
                        {
                            Assert.Equal(valueToSet, value);
                        }
                        else
                        {
                            Assert.Same(valueToSet, value);
                        }

                        // the event handler should have been called directly
                        Assert.True(handlerCalledEvent.IsSet);
                        Assert.Same(SynchronizationContext.Current, handlerThreadSynchronizationContext);
                        Assert.Equal(new[] { propertyInfo.Name }, changedPropertyNames.ToArray());
                    }
                });
            }
            else
            {
                // the thread registering the event is different from the thread changing the property and raising the event
                // => the event handler should be scheduled to run on the thread that has registered the event

                if (protect)
                {
                    // protect the message
                    // => changes the IsReadOnly property to true
                    // => event handler should have been scheduled to run
                    message.Protect();
                    Assert.True(handlerCalledEvent.Wait(1000));
                    Assert.Same(mThread.Context.SynchronizationContext, handlerThreadSynchronizationContext);
                    Assert.Equal(new[] { "IsReadOnly" }, changedPropertyNames.ToArray());
                    handlerCalledEvent.Reset();

                    // the message is protected and setting a property should throw an exception
                    var ex = Assert.Throws <TargetInvocationException>(() => propertyInfo.SetValue(message, valueToSet, null));
                    Assert.IsType <NotSupportedException>(ex.InnerException);

                    // the property value should not have changed
                    value = propertyInfo.GetValue(message);
                    Assert.Equal(expectedDefaultValue, value);

                    // the event handler should neither have been called directly nor should it be scheduled to run
                    Assert.False(handlerCalledEvent.Wait(1000));
                }
                else
                {
                    // set property
                    propertyInfo.SetValue(message, valueToSet, null);

                    // the property value should not be the same as the set value
                    value = propertyInfo.GetValue(message);
                    if (propertyInfo.PropertyType.IsValueType)
                    {
                        Assert.Equal(valueToSet, value);
                    }
                    else
                    {
                        Assert.Same(valueToSet, value);
                    }

                    // the event handler should run in the context of the thread that registered the event
                    Assert.True(handlerCalledEvent.Wait(1000));
                    Assert.Same(mThread.Context.SynchronizationContext, handlerThreadSynchronizationContext);
                    Assert.Equal(new[] { propertyInfo.Name }, changedPropertyNames.ToArray());
                }
            }

            // unregister the PropertyChanged event
            await mThread.Factory.Run(() => { message.PropertyChanged -= PropertyChangedHandler; });
        }
예제 #12
0
        private void Create_Default()
        {
            var message = new LogFileMessage();

            CheckDefaultState(message);
        }
예제 #13
0
        private async Task InitWith(bool initInSameThread, bool withPropertyChanged)
        {
            // create a new message
            var message = new LogFileMessage();

            Assert.False(message.IsReadOnly);
            CheckDefaultState(message, true, false);

            // ensure that the message is not marked for asynchronous initialization
            Assert.False(message.IsAsyncInitPending);

            // prepare data pulling some information out of the event handler
            SynchronizationContext handlerThreadSynchronizationContext = null;
            var changedPropertyNames = new List <string>();
            var handlerCalledEvent   = new ManualResetEventSlim(false);

            // the handler that is expected to be called on changes
            void PropertyChangedHandler(object sender, PropertyChangedEventArgs e)
            {
                handlerThreadSynchronizationContext = SynchronizationContext.Current;
                changedPropertyNames.Add(e.PropertyName);
                handlerCalledEvent.Set();
            }

            // run test in a separate thread that provides a synchronization context that allows to
            // marshal calls into that thread
            await mThread.Factory.Run(() => { Assert.NotNull(SynchronizationContext.Current); });

            // register the PropertyChanged event
            if (withPropertyChanged)
            {
                await mThread.Factory.Run(() => { message.PropertyChanged += PropertyChangedHandler; });
            }

            // callback that initializes the message
            void InitializeTest()
            {
                // init the message
                message.InitWith(
                    1,
                    DateTimeOffset.Parse("2020-01-01T12:00:00+01:00"),
                    2,
                    3,
                    "Log Writer",
                    "Log Level",
                    new TagSet("Tag"),
                    "Application",
                    "Process",
                    42,
                    "Some Text");

                // check administrative properties
                Assert.True(message.IsInitialized);
                Assert.False(message.IsAsyncInitPending);

                // check message properties
                Assert.Equal(1, message.Id);
                Assert.Equal(DateTimeOffset.Parse("2020-01-01T12:00:00+01:00"), message.Timestamp);
                Assert.Equal(2, message.HighPrecisionTimestamp);
                Assert.Equal(3, message.LostMessageCount);
                Assert.Equal("Log Writer", message.LogWriterName);
                Assert.Equal("Log Level", message.LogLevelName);
                Assert.Equal(new TagSet("Tag"), message.Tags);
                Assert.Equal("Application", message.ApplicationName);
                Assert.Equal("Process", message.ProcessName);
                Assert.Equal(42, message.ProcessId);
                Assert.Equal("Some Text", message.Text);

                if (initInSameThread)
                {
                    if (withPropertyChanged)
                    {
                        // the event handler should have been called only once in the same thread
                        // (the event handler is called directly as the registering thread is the same as the thread raising the event)
                        Assert.True(handlerCalledEvent.IsSet);
                        Assert.Equal(new string[] { null }, changedPropertyNames.ToArray());                         // null => all properties
                    }
                    else
                    {
                        // the event handler should not have been called
                        Assert.False(handlerCalledEvent.IsSet);
                    }
                }
            }

            // initialize the message either in the context of the thread that registered the handler
            // or in a different - the current - thread
            if (initInSameThread)
            {
                await mThread.Factory.Run(InitializeTest);
            }
            else
            {
                InitializeTest();
            }

            if (!initInSameThread)
            {
                // the thread registering the event and the thread initializing the message are different
                if (withPropertyChanged)
                {
                    // event handler should run in the context of the thread that registered it
                    Assert.True(handlerCalledEvent.Wait(1000));
                    Assert.Same(mThread.Context.SynchronizationContext, handlerThreadSynchronizationContext);
                    Assert.Equal(new string[] { null }, changedPropertyNames.ToArray());
                }
                else
                {
                    // the event handler should not have been called
                    Assert.False(handlerCalledEvent.Wait(1000));
                }
            }
        }