public void TestAppendTwoSourcesWrongOrderSeparateChangesPartialInvalidation()
        {
            var source1 = new InMemoryLogSource();

            source1.AddEntry("A", LevelFlags.Other, new DateTime(2019, 5, 28, 00, 34, 0));
            source1.AddEntry("C", LevelFlags.Other, new DateTime(2019, 5, 28, 00, 36, 0));
            var source2 = new InMemoryLogSource();

            source2.AddEntry("B", LevelFlags.Other, new DateTime(2019, 5, 28, 00, 35, 0));
            source2.AddEntry("D", LevelFlags.Other, new DateTime(2019, 5, 28, 00, 37, 0));

            var index   = new MergedLogSourceIndex(source1, source2);
            var changes = index.Process(new MergedLogSourcePendingModification(source1, LogSourceModification.Appended(0, 2)));

            changes.Should().Equal(new object[]
            {
                LogSourceModification.Appended(0, 2)
            });

            changes = index.Process(new MergedLogSourcePendingModification(source2, LogSourceModification.Appended(0, 2)));
            changes.Should().Equal(new object[]
            {
                LogSourceModification.Removed(1, 1),
                LogSourceModification.Appended(1, 3)
            });
        }
示例#2
0
        public void TestDeadlockWhenRemovingAnActiveDataSource()
        {
            var logFile    = new Mock <ILogSource>();
            var dataSource = new Mock <IDataSource>();

            dataSource.Setup(x => x.UnfilteredLogSource).Returns(logFile.Object);

            var collection = new BookmarkCollection(_bookmarks.Object, TimeSpan.Zero);

            logFile.Setup(x => x.RemoveListener(It.IsAny <ILogSourceListener>()))
            .Callback((ILogSourceListener unused) =>
            {
                // In order to produce the deadlock, we have to simulate what's happening in reality.
                // Any ILogFile implementation will hold a lock both while invoking listeners and while
                // Removing them. Waiting on the OnLogFileModified() call simulates exactly that...
                var task = new Task(() =>
                {
                    collection.OnLogFileModified(logFile.Object, LogSourceModification.Removed(0, 10));
                }, TaskCreationOptions.LongRunning);

                task.Start();
                task.Wait();
            });

            collection.AddDataSource(dataSource.Object);

            var removeTask = Task.Factory.StartNew(() => collection.RemoveDataSource(dataSource.Object));

            removeTask.Wait(TimeSpan.FromSeconds(1)).Should().BeTrue("because RemoveDataSource should not block at all");

            logFile.Verify(x => x.RemoveListener(collection), Times.Once);
        }
        public void TestTail_WriteTwoLines_Changes2()
        {
            var encoding = Encoding.UTF8;
            var fileName = GetUniqueNonExistingFileName();

            var line1 = "The sky crawlers";
            var line2 = "is awesome!";

            using (var stream = new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.Read))
                using (var writer = new StreamWriter(stream, encoding))
                    using (var logFile = Create(fileName, encoding))
                    {
                        var changes = AddListener(logFile, 1000);
                        changes.Should().Equal(new object[] { LogSourceModification.Reset() });

                        writer.Write(line1);
                        writer.Flush();
                        _taskScheduler.RunOnce();

                        changes.Should().Equal(new object[] { LogSourceModification.Reset(), LogSourceModification.Appended(0, 1) },
                                               "because the file consists of one line");

                        writer.Write("\n" + line2);
                        writer.Flush();
                        _taskScheduler.RunOnce();

                        //changes.Should().Equal(new object[] {LogSourceModification.Reset(), new LogFileSection(0, 1), new LogFileSection(1, 1)});
                        // The current behavior won't cause wrong behavior in upstream listeners, but it will cause them unnecessary work.
                        changes.Should().Equal(new object[] { LogSourceModification.Reset(), LogSourceModification.Appended(0, 1),
                                                              LogSourceModification.Removed(0, 1), LogSourceModification.Appended(0, 2) });
                    }
        }
示例#4
0
        public void TestReadOneLine4()
        {
            _streamWriter.Write("A");
            _streamWriter.Flush();
            _taskScheduler.RunOnce();

            _streamWriter.Write("B");
            _streamWriter.Flush();
            _taskScheduler.RunOnce();

            _streamWriter.Write("C");
            _streamWriter.Flush();
            _taskScheduler.RunOnce();

            _modifications.Should().Equal(new object[]
            {
                LogSourceModification.Reset(),
                LogSourceModification.Appended(0, 1),
                LogSourceModification.Removed(0, 1),
                LogSourceModification.Appended(0, 1),
                LogSourceModification.Removed(0, 1),
                LogSourceModification.Appended(0, 1)
            }, "because the log file should've sent invalidations for the 2nd and 3rd read (because the same line was modified)");

            _source.GetProperty(Properties.LogEntryCount).Should().Be(1);
            var entry = _source.GetEntry(0);

            entry.Index.Should().Be(0);
            entry.LogEntryIndex.Should().Be(0);
            entry.RawContent.Should().Be("ABC");
        }
示例#5
0
        public void TestEquality()
        {
            var appendModification        = LogSourceModification.Appended(10, 41);
            var equalAppendModification   = LogSourceModification.Appended(10, 41);
            var anotherAppendModification = LogSourceModification.Appended(9, 41);

            appendModification.Should().Be(equalAppendModification);
            equalAppendModification.Should().Be(appendModification);

            appendModification.Should().NotBe(anotherAppendModification);
            anotherAppendModification.Should().NotBe(equalAppendModification);

            var removedModification = LogSourceModification.Removed(10, 41);

            appendModification.Should().NotBe(removedModification);
            equalAppendModification.Should().NotBe(removedModification);
            anotherAppendModification.Should().NotBe(removedModification);

            var anotherRemovedModification = LogSourceModification.Removed(9, 41);

            removedModification.Should().NotBe(anotherRemovedModification);
            anotherRemovedModification.Should().NotBe(anotherAppendModification);

            appendModification.Should().NotBe(LogSourceModification.Reset());
            appendModification.Should().NotBe(LogSourceModification.PropertiesChanged());

            removedModification.Should().NotBe(LogSourceModification.Reset());
            removedModification.Should().NotBe(LogSourceModification.PropertiesChanged());
        }
示例#6
0
        public void TestSplitInvalidateGreaterThanThreshold([Values(2, 99, 100)] int count)
        {
            int maxCount = count - 1;
            var section  = LogSourceModification.Removed(new LogLineIndex(42), count);

            section.Split(maxCount).Should().Equal(new[]
            {
                section
            }, "because invalidations are NEVER split up");
        }
        public void TestInvalidateThenAppendWithGap()
        {
            var changes = new MergedLogSourceChanges(10);

            changes.RemoveFrom(5);
            changes.Append(7, 10);

            changes.Sections.Should().Equal(new object[]
            {
                LogSourceModification.Removed(5, 5),
                LogSourceModification.Appended(5, 12)
            });
        }
        public void TestInvalidate()
        {
            var changes = new MergedLogSourceChanges(42);

            changes.RemoveFrom(10);

            changes.Sections.Should().Equal(new object[]
            {
                LogSourceModification.Removed(10, 32)
            });

            changes.TryGetFirstRemovedIndex(out var index).Should().BeTrue();
            index.Should().Be(new LogLineIndex(10));
        }
示例#9
0
        public void TestInvalidate1()
        {
            var notifier = new LogSourceListenerNotifier(_logFile.Object, _listener.Object, TimeSpan.Zero, 1);

            notifier.OnRead(1);
            notifier.Remove(0, 1);
            _modifications.Should().Equal(new[]
            {
                LogSourceModification.Reset(),
                LogSourceModification.Appended(0, 1),
                LogSourceModification.Removed(0, 1)
            });
            notifier.LastNumberOfLines.Should().Be(0);
        }
示例#10
0
        public void Remove(int firstIndex, int count)
        {
            int lastIndex       = Math.Min(firstIndex + count, _lastNumberOfLines);
            int invalidateCount = lastIndex - firstIndex;

            // When the start index of the invalidation is greater than the last reported index
            // then this means that our listeners haven't even gotten the change yet and thus
            // they don't need to be notified of the invalidation either.
            if (invalidateCount > 0)
            {
                var section = new LogSourceSection(firstIndex, invalidateCount);
                _listener.OnLogFileModified(_logSource, LogSourceModification.Removed(section));
                _lastNumberOfLines = firstIndex;
            }
        }
示例#11
0
        public void TestRemove()
        {
            var modification = LogSourceModification.Removed(9, 22);

            modification.IsRemoved(out var removedSection).Should().BeTrue();
            removedSection.Should().Equal(new LogSourceSection(9, 22));
            modification.ToString().Should().Be("Removed [#9, #22]");

            modification.IsAppended(out var appendedSection).Should().BeFalse();
            appendedSection.Should().Equal(new LogSourceSection());

            modification.IsReset().Should().BeFalse();

            modification.IsPropertiesChanged().Should().BeFalse();
        }
        public void TestInvalidateTwiceSuperfluous()
        {
            var changes = new MergedLogSourceChanges(13);

            changes.RemoveFrom(5);
            changes.RemoveFrom(7);

            changes.Sections.Should().Equal(new object[]
            {
                LogSourceModification.Removed(5, 8)
            });

            changes.TryGetFirstRemovedIndex(out var index).Should().BeTrue();
            index.Should().Be(new LogLineIndex(5));
        }
        public void TestInvalidateThenAppend()
        {
            var changes = new MergedLogSourceChanges(10);

            changes.RemoveFrom(5);
            changes.Append(5, 6);

            changes.Sections.Should().Equal(new object[]
            {
                LogSourceModification.Removed(5, 5),
                LogSourceModification.Appended(5, 6)
            });

            changes.TryGetFirstRemovedIndex(out var index).Should().BeTrue();
            index.Should().Be(new LogLineIndex(5));
        }
示例#14
0
        public void TestInvalidate2()
        {
            var notifier = new LogSourceListenerNotifier(_logFile.Object, _listener.Object, TimeSpan.FromSeconds(1), 10);

            notifier.OnRead(10);
            notifier.OnRead(12);
            notifier.Remove(0, 12);
            _modifications.Should().Equal(new[]
            {
                LogSourceModification.Reset(),
                LogSourceModification.Appended(0, 10),
                LogSourceModification.Removed(0, 10)
            },
                                          "Because the notifier should've reported only the first 10 changes and therefore Invalidate() only had to invalidate those 10 changes"
                                          );
            notifier.LastNumberOfLines.Should().Be(0);
        }
        public void RemoveFrom(LogLineIndex firstInvalidIndex)
        {
            if (firstInvalidIndex >= _count)
            {
                Log.WarnFormat("Ignoring invalidation from index '{0}' onwards because nothing has been appended there!", firstInvalidIndex);
                return;
            }

            if (firstInvalidIndex >= _initialCount)
            {
                // We do not need to add an invalidation, rather we can simply clamp an existing previous append
                if (firstInvalidIndex > 0)
                {
                    var previous = _modifications[_modifications.Count - 1];
                    if (previous.IsAppended(out var appendedSection))
                    {
                        var gap = appendedSection.Index + appendedSection.Count - firstInvalidIndex;
                        if (gap > 0)
                        {
                            appendedSection.Count -= gap;
                            _modifications[_modifications.Count - 1] = LogSourceModification.Appended(appendedSection);
                        }
                    }
                }
            }
            else
            {
                var invalidationCount = _count - firstInvalidIndex;
                if (_removeIndex != -1)
                {
                    var invalidation = _modifications[_removeIndex];
                    invalidation.IsRemoved(out var removedSection);
                    if (removedSection.Index <= firstInvalidIndex)
                    {
                        return;                         //< Nothing to do
                    }
                    _modifications[_removeIndex] = LogSourceModification.Removed(firstInvalidIndex, invalidationCount);
                }
                else
                {
                    _removeIndex = _modifications.Count;
                    _modifications.Add(LogSourceModification.Removed(firstInvalidIndex, invalidationCount));
                }
            }
        }
        public void TestTwoSourcesAppendBothInvalidateOne()
        {
            var source1 = new InMemoryLogSource();
            var source2 = new InMemoryLogSource();

            source2.AddEntry("a", LevelFlags.Warning, new DateTime(2021, 2, 28, 23, 00, 0));
            source2.AddEntry("b", LevelFlags.Warning, new DateTime(2021, 2, 28, 23, 01, 0));
            source1.AddEntry("c", LevelFlags.Info, new DateTime(2021, 2, 28, 23, 02, 0));
            source2.AddEntry("d", LevelFlags.Error, new DateTime(2021, 2, 28, 23, 03, 0));

            var index   = new MergedLogSourceIndex(source1, source2);
            var changes = index.Process(
                new MergedLogSourcePendingModification(source1, LogSourceModification.Appended(0, 1)),
                new MergedLogSourcePendingModification(source2, LogSourceModification.Appended(0, 3))
                );

            changes.Should().Equal(new object[]
            {
                LogSourceModification.Appended(0, 4)
            });
            index.Count.Should().Be(4);
            index[0].SourceId.Should().Be(1);
            index[0].MergedLogEntryIndex.Should().Be(0);
            index[1].SourceId.Should().Be(1);
            index[1].MergedLogEntryIndex.Should().Be(1);
            index[2].SourceId.Should().Be(0);
            index[2].MergedLogEntryIndex.Should().Be(2);
            index[3].SourceId.Should().Be(1);
            index[3].MergedLogEntryIndex.Should().Be(3);


            changes = index.Process(new MergedLogSourcePendingModification(source2, LogSourceModification.Removed(1, 2)));
            changes.Should().Equal(new object[]
            {
                LogSourceModification.Removed(1, 3),
                LogSourceModification.Appended(1, 1)
            });
            index.Count.Should().Be(2);
            index[0].SourceId.Should().Be(1);
            index[1].SourceId.Should().Be(0);
        }
示例#17
0
        public void TestListen3()
        {
            using (var proxy = new LogSourceProxy(_taskScheduler, TimeSpan.Zero, _logFile.Object))
            {
                proxy.AddListener(_listener.Object, TimeSpan.Zero, 1000);

                _listeners.OnRead(500);
                _listeners.Remove(400, 100);
                _listeners.OnRead(550);

                _taskScheduler.RunOnce();

                _modifications.Should().Equal(new[]
                {
                    LogSourceModification.Reset(),
                    LogSourceModification.Appended(0, 500),
                    LogSourceModification.Removed(400, 100),
                    LogSourceModification.Appended(400, 150)
                });
            }
        }
        public void TestAppendTwoSourcesWrongOrderSeparateChangesFullInvalidation()
        {
            var source1 = new InMemoryLogSource();

            source1.AddEntry("B", LevelFlags.Other, new DateTime(2019, 5, 27, 23, 10, 0));
            var source2 = new InMemoryLogSource();

            source2.AddEntry("A", LevelFlags.Other, new DateTime(2019, 5, 27, 23, 09, 0));

            var index   = new MergedLogSourceIndex(source1, source2);
            var changes = index.Process(new MergedLogSourcePendingModification(source1, LogSourceModification.Appended(0, 1)));

            changes.Should().Equal(new object[]
            {
                LogSourceModification.Appended(0, 1)
            });

            changes = index.Process(new MergedLogSourcePendingModification(source2, LogSourceModification.Appended(0, 1)));
            changes.Should().Equal(new object[]
            {
                LogSourceModification.Removed(0, 1),
                LogSourceModification.Appended(0, 2)
            });

            var indices = index.Get(new LogSourceSection(0, 2));

            indices.Count.Should().Be(2);
            indices[0].SourceId.Should().Be(1);
            indices[0].SourceLineIndex.Should().Be(0);
            indices[0].OriginalLogEntryIndex.Should().Be(0);
            indices[0].MergedLogEntryIndex.Should().Be(0);
            indices[0].Timestamp.Should().Be(new DateTime(2019, 5, 27, 23, 9, 0));

            indices[1].SourceId.Should().Be(0);
            indices[1].SourceLineIndex.Should().Be(0);
            indices[1].OriginalLogEntryIndex.Should().Be(0);
            indices[1].MergedLogEntryIndex.Should().Be(1);
            indices[1].Timestamp.Should().Be(new DateTime(2019, 5, 27, 23, 10, 0));
        }
        public void TestTail_WriteTwoLines_Changes1()
        {
            var encoding = Encoding.UTF8;
            var fileName = GetUniqueNonExistingFileName();

            var line1 = "The sky crawlers";
            var line2 = "is awesome!";

            using (var stream = new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.Read))
                using (var writer = new StreamWriter(stream, encoding))
                    using (var logFile = Create(fileName, encoding))
                    {
                        var changes = AddListener(logFile, 1000);
                        changes.Should().Equal(new object[] { LogSourceModification.Reset() });

                        writer.Write(line1 + "\r\n");
                        writer.Flush();
                        _taskScheduler.RunOnce();

                        changes.Should().Equal(new object[] { LogSourceModification.Reset(), LogSourceModification.Appended(0, 1), LogSourceModification.Appended(1, 1) },
                                               "because the file consists of two lines, one being totally empty");

                        writer.Write(line2 + "\r\n");
                        writer.Flush();
                        _taskScheduler.RunOnce();

                        changes.Should().Equal(new object[] { LogSourceModification.Reset(), LogSourceModification.Appended(0, 1), LogSourceModification.Appended(1, 1), LogSourceModification.Removed(1, 1), LogSourceModification.Appended(1, 2) });
                    }
        }
        public void TestTail_WriteOneLineTwoFlushes_Changes3()
        {
            var encoding = Encoding.UTF8;
            var fileName = GetUniqueNonExistingFileName();

            var linePart1 = "The sky";
            var linePart2 = " Crawlers";

            using (var stream = new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.Read))
                using (var writer = new StreamWriter(stream, encoding))
                    using (var logFile = Create(fileName, encoding))
                    {
                        var changes = AddListener(logFile, 1000);
                        changes.Should().Equal(new object[] { LogSourceModification.Reset() });

                        writer.Write(linePart1);
                        writer.Flush();
                        _taskScheduler.RunOnce();

                        changes.Should().Equal(new object[] { LogSourceModification.Reset(), LogSourceModification.Appended(0, 1) },
                                               "because the file consists of one line");

                        writer.Write(linePart2);
                        writer.Flush();
                        _taskScheduler.RunOnce();

                        changes.Should().Equal(new object[] { LogSourceModification.Reset(), LogSourceModification.Appended(0, 1), LogSourceModification.Removed(0, 1), LogSourceModification.Appended(0, 1) });
                    }
        }
示例#21
0
        public void TestMergeResetOneSource()
        {
            var source1 = new InMemoryLogSource();

            var source2 = new InMemoryLogSource();

            var merged  = new MergedLogSource(_taskScheduler, TimeSpan.Zero, source1, source2);
            var entries = Listen(merged);
            var changes = ListenToChanges(merged, 100);

            source2.AddEntry("a", LevelFlags.Warning, new DateTime(2021, 2, 28, 22, 15, 0));
            source1.AddEntry("b", LevelFlags.Info, new DateTime(2021, 2, 28, 22, 16, 0));
            source2.AddEntry("c", LevelFlags.Error, new DateTime(2021, 2, 28, 22, 17, 0));

            _taskScheduler.RunOnce();
            merged.GetProperty(Core.Properties.PercentageProcessed).Should().Be(Percentage.HundredPercent);
            entries.Count.Should().Be(3);
            changes.Should().Equal(new object[] { LogSourceModification.Reset(), LogSourceModification.Appended(0, 3) });


            source1.Clear();
            _taskScheduler.RunOnce();
            entries.Count.Should().Be(2, "because the one entry from source should have been removed from the merged source");
            changes.Should().Equal(new object[] { LogSourceModification.Reset(), LogSourceModification.Appended(0, 3), LogSourceModification.Removed(1, 2), LogSourceModification.Appended(1, 1) });
        }