public FileTailerViewModel(ILogger logger,ISchedulerProvider schedulerProvider, FileInfo fileInfo) { if (logger == null) throw new ArgumentNullException(nameof(logger)); if (schedulerProvider == null) throw new ArgumentNullException(nameof(schedulerProvider)); if (fileInfo == null) throw new ArgumentNullException(nameof(fileInfo)); var filterRequest = this.WhenValueChanged(vm => vm.SearchText).Throttle(TimeSpan.FromMilliseconds(125)); var autoChanged = this.WhenValueChanged(vm => vm.AutoTail); var scroller = _userScrollRequested .CombineLatest(autoChanged, (user, auto) => { var mode = AutoTail ? ScrollingMode.Tail : ScrollingMode.User; return new ScrollRequest(mode, user.PageSize, user.FirstIndex); }) .Sample(TimeSpan.FromMilliseconds(150)) .DistinctUntilChanged(); var tailer = new FileTailer(fileInfo, filterRequest, scroller); //create user display for count line count var lineCounter = tailer.TotalLines.CombineLatest(tailer.MatchedLines,(total,matched)=> { return total == matched ? $"File has {total.ToString("#,###")} lines" : $"Showing {matched.ToString("#,###")} of {total.ToString("#,###")} lines"; }) .Subscribe(text => LineCountText=text); //load lines into observable collection var loader = tailer.Lines.Connect() .Transform(line => new LineProxy(line)) .Sort(SortExpressionComparer<LineProxy>.Ascending(proxy => proxy.Number)) .ObserveOn(schedulerProvider.MainThread) .Bind(out _data) .Subscribe(changes => logger.Info($"Rows changed {changes.Adds} adds, {changes.Removes} removed"), ex => logger.Error(ex, "There is a problem with bind data")); //monitor matching lines and start index, var matchedLinesMonitor = tailer.MatchedLines .Subscribe(matched => MatchedLineCount = matched); //track first visible index var firstIndexMonitor = tailer.Lines.Connect() .QueryWhenChanged(lines =>lines.Count == 0 ? 0 : lines.Select(l => l.Index).Min()) .Subscribe(first=> FirstIndex= first); _cleanUp = new CompositeDisposable(tailer, lineCounter, loader, firstIndexMonitor, matchedLinesMonitor, Disposable.Create(() => { _userScrollRequested.OnCompleted(); })); }
public void AutoTailWithFilter() { var file = Path.GetTempFileName(); var info = new FileInfo(file); var scheduler = new TestScheduler(); var textMatch = Observable.Return("odd"); var autoTailer = Observable.Return(new ScrollRequest(10)); File.AppendAllLines(file, Enumerable.Range(1, 100) .Select(i => i%2 == 1 ? $"{i} is an odd number" : $"{i} is an even number").ToArray()); using (var tailer = new FileTailer(info, textMatch, autoTailer, scheduler)) { //lines which contain "1" var expectedLines = Enumerable.Range(1, 100) .Select(i => i % 2 == 1 ? $"{i} is an odd number" : $"{i} is an even number") .Where(s => s.Contains("odd")) .Reverse() .Take(10) .Reverse() .ToArray(); scheduler.AdvanceBySeconds(1); tailer.Lines.Items.Select(l => l.Text).ShouldAllBeEquivalentTo(expectedLines); File.AppendAllLines(file, Enumerable.Range(101, 10) .Select(i => i % 2 == 1 ? $"{i} is an odd number" : $"{i} is an even number").ToArray()); scheduler.AdvanceBySeconds(1); expectedLines = Enumerable.Range(1, 110) .Select(i => i % 2 == 1 ? $"{i} is an odd number" : $"{i} is an even number") .Where(s => s.Contains("odd")) .Reverse() .Take(10) .Reverse() .ToArray(); scheduler.AdvanceBySeconds(1); File.Delete(file); tailer.Lines.Items.Select(l => l.Text).ShouldAllBeEquivalentTo(expectedLines); } }
public void AutoTailWithFilter() { var scheduler = new TestScheduler(); var autoTailer = Observable.Return(new ScrollRequest(10)); Func<string, bool> predicate = s => s.Contains("odd"); using (var file = new TestFile()) { file.Append(Enumerable.Range(1, 100).Select(i => i%2 == 1 ? $"{i} is an odd number" : $"{i} is an even number").ToArray()); var search = file.Info.Search(predicate, scheduler); using (var tailer = new FileTailer(file.Info, search, autoTailer, new NullLogger(), scheduler)) { //lines which contain "1" var expectedLines = Enumerable.Range(1, 100) .Select(i => i%2 == 1 ? $"{i} is an odd number" : $"{i} is an even number") .Where(s => s.Contains("odd")) .Reverse() .Take(10) .Reverse() .ToArray(); scheduler.AdvanceBySeconds(1); tailer.Lines.Items.Select(l => l.Text).ShouldAllBeEquivalentTo(expectedLines); file.Append( Enumerable.Range(101, 10).Select(i => i%2 == 1 ? $"{i} is an odd number" : $"{i} is an even number").ToArray()); scheduler.AdvanceBySeconds(1); expectedLines = Enumerable.Range(1, 110) .Select(i => i%2 == 1 ? $"{i} is an odd number" : $"{i} is an even number") .Where(s => s.Contains("odd")) .Reverse() .Take(10) .Reverse() .ToArray(); scheduler.AdvanceBySeconds(1); tailer.Lines.Items.Select(l => l.Text).ShouldAllBeEquivalentTo(expectedLines); } } }
public void AutoTail() { var file = Path.GetTempFileName(); var info = new FileInfo(file); var scheduler = new TestScheduler(); var textMatch = Observable.Return((string)null); var autoTailer = Observable.Return(new ScrollRequest(10)); File.AppendAllLines(file, Enumerable.Range(1, 100).Select(i =>i.ToString()).ToArray()); using (var tailer = new FileTailer(info, textMatch, autoTailer,scheduler)) { tailer.Lines.Items.Select(l => l.Number).ShouldAllBeEquivalentTo(Enumerable.Range(91, 10)); File.AppendAllLines(file, Enumerable.Range(101, 10).Select(i => i.ToString())); scheduler.AdvanceByMilliSeconds(250); File.Delete(file); tailer.Lines.Items.Select(l => l.Number).ShouldAllBeEquivalentTo(Enumerable.Range(101, 10)); } }
public void AutoTail() { var scheduler = new TestScheduler(); var textMatch = Observable.Return(FileSearchResult.None); var autoTailer = Observable.Return(new ScrollRequest(10)); using (var file = new TestFile()) { file.Append( Enumerable.Range(1, 100).Select(i => i.ToString()).ToArray()); using (var tailer = new FileTailer(file.Info, textMatch, autoTailer, new NullLogger(), scheduler)) { scheduler.AdvanceByMilliSeconds(250); tailer.Lines.Items.Select(l => l.Number).ShouldAllBeEquivalentTo(Enumerable.Range(91, 10)); file.Append( Enumerable.Range(101, 10).Select(i => i.ToString())); scheduler.AdvanceByMilliSeconds(250); tailer.Lines.Items.Select(l => l.Number).ShouldAllBeEquivalentTo(Enumerable.Range(101, 10)); } } }
public void WillNotApplyFilterWhenTextIsLessThan3Character() { var file = Path.GetTempFileName(); var info = new FileInfo(file); var scheduler = new TestScheduler(); var textMatch = Observable.Return((string)"1"); var autoTailer = Observable.Return(new ScrollRequest(10)); File.AppendAllLines(file, Enumerable.Range(1, 100).Select(i => i.ToString()).ToArray()); using (var tailer = new FileTailer(info, textMatch, autoTailer, scheduler)) { //lines which contain "1" int[] expectedLines = Enumerable.Range(91, 10) .Select(i => i.ToString()) .Select(int.Parse) .ToArray(); scheduler.AdvanceBySeconds(1); tailer.Lines.Items.Select(l => l.Number).ShouldAllBeEquivalentTo(expectedLines); File.Delete(file); tailer.Lines.Items.Select(l => l.Number).ShouldAllBeEquivalentTo(expectedLines); } }