/// <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);
            }
Exemplo n.º 2
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);
                }
            }
        }
Exemplo n.º 3
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));
                }
            }
        }
Exemplo n.º 4
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; });
        }