1 // OpenTween - Client of Twitter
2 // Copyright (c) 2014 kim_upsilon (@kim_upsilon) <https://upsilo.net/~upsilon/>
3 // All rights reserved.
5 // This file is part of OpenTween.
7 // This program is free software; you can redistribute it and/or modify it
8 // under the terms of the GNU General Public License as published by the Free
9 // Software Foundation; either version 3 of the License, or (at your option)
12 // This program is distributed in the hope that it will be useful, but
13 // WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14 // or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
17 // You should have received a copy of the GNU General Public License along
18 // with this program. If not, see <http://www.gnu.org/licenses/>, or write to
19 // the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
20 // Boston, MA 02110-1301, USA.
23 using System.Collections.Generic;
25 using System.Reflection;
27 using System.Windows.Forms;
29 using Xunit.Extensions;
31 namespace OpenTween.Models
33 public class TabInformationTest
35 private readonly TabInformations tabinfo;
37 public TabInformationTest()
39 this.tabinfo = this.CreateInstance();
41 // TabInformation.GetInstance() で取得できるようにする
42 var field = typeof(TabInformations).GetField("Instance",
43 BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.SetField);
44 field.SetValue(null, this.tabinfo);
47 this.tabinfo.AddTab(new HomeTabModel("Recent"));
48 this.tabinfo.AddTab(new MentionsTabModel("Reply"));
49 this.tabinfo.AddTab(new DirectMessagesTabModel("DM"));
50 this.tabinfo.AddTab(new FavoritesTabModel("Favorites"));
53 private TabInformations CreateInstance()
54 => (TabInformations)Activator.CreateInstance(typeof(TabInformations), true);
57 public void AddTab_Test()
59 var tab = new FilterTabModel("MyTab");
60 var ret = this.tabinfo.AddTab(tab);
63 Assert.Same(tab, this.tabinfo.Tabs.Last());
67 public void AddTab_DuplicateTest()
69 var ret = this.tabinfo.AddTab(new FilterTabModel("Recent"));
75 public void RemoveTab_InnerStorageTabTest()
77 var tab = new PublicSearchTabModel("search");
78 tab.AddPostQueue(new PostClass { StatusId = 100L });
79 this.tabinfo.AddTab(tab);
80 this.tabinfo.SubmitUpdate();
82 Assert.True(this.tabinfo.ContainsTab("search"));
83 Assert.Empty(this.tabinfo.RemovedTab);
85 this.tabinfo.RemoveTab("search");
87 Assert.False(this.tabinfo.ContainsTab("search"));
88 Assert.Single(this.tabinfo.RemovedTab);
89 Assert.Contains(tab, this.tabinfo.RemovedTab);
93 public void RemoveTab_FilterTab_MovedPost_OrphanedTest()
95 var filterTab = new FilterTabModel("filter");
96 filterTab.AddFilter(new() { FilterName = "opentween", MoveMatches = true });
97 this.tabinfo.AddTab(filterTab);
99 this.tabinfo.AddPost(new PostClass { StatusId = 100L, ScreenName = "opentween" });
100 this.tabinfo.DistributePosts();
101 this.tabinfo.SubmitUpdate();
103 Assert.False(this.tabinfo.HomeTab.Contains(100L));
104 Assert.True(filterTab.Contains(100L));
106 this.tabinfo.RemoveTab("filter");
108 Assert.False(this.tabinfo.ContainsTab("filter"));
109 Assert.Single(this.tabinfo.RemovedTab);
110 Assert.Contains(filterTab, this.tabinfo.RemovedTab);
112 // 他に MoveMatches で移動している振り分けタブが存在しなければ Home タブに戻す
113 Assert.True(this.tabinfo.HomeTab.Contains(100L));
117 public void RemoveTab_FilterTab_MovedPost_NotOrphanedTest()
119 var filterTab1 = new FilterTabModel("filter1");
120 filterTab1.AddFilter(new() { FilterName = "opentween", MoveMatches = true });
121 this.tabinfo.AddTab(filterTab1);
123 var filterTab2 = new FilterTabModel("filter2");
124 filterTab2.AddFilter(new() { FilterName = "opentween", MoveMatches = true });
125 this.tabinfo.AddTab(filterTab2);
127 this.tabinfo.AddPost(new PostClass { StatusId = 100L, ScreenName = "opentween" });
128 this.tabinfo.DistributePosts();
129 this.tabinfo.SubmitUpdate();
131 Assert.False(this.tabinfo.HomeTab.Contains(100L));
132 Assert.True(filterTab1.Contains(100L));
133 Assert.True(filterTab2.Contains(100L));
135 this.tabinfo.RemoveTab("filter1");
137 Assert.False(this.tabinfo.ContainsTab("filter1"));
138 Assert.Single(this.tabinfo.RemovedTab);
139 Assert.Contains(filterTab1, this.tabinfo.RemovedTab);
141 // 他に MoveMatches で移動している振り分けタブが存在する場合は Home タブに戻さない
142 Assert.False(this.tabinfo.HomeTab.Contains(100L));
143 Assert.True(filterTab2.Contains(100L));
147 public void RemoveTab_FilterTab_CopiedPost_Test()
149 var filterTab = new FilterTabModel("filter");
150 filterTab.AddFilter(new() { FilterName = "opentween", MoveMatches = false });
151 this.tabinfo.AddTab(filterTab);
153 this.tabinfo.AddPost(new PostClass { StatusId = 100L, ScreenName = "opentween" });
154 this.tabinfo.DistributePosts();
155 this.tabinfo.SubmitUpdate();
157 Assert.True(this.tabinfo.HomeTab.Contains(100L));
158 Assert.True(filterTab.Contains(100L));
160 this.tabinfo.RemoveTab("filter");
162 Assert.False(this.tabinfo.ContainsTab("filter"));
163 Assert.Single(this.tabinfo.RemovedTab);
164 Assert.Contains(filterTab, this.tabinfo.RemovedTab);
166 // 振り分けタブにコピーされた発言は Home タブにも存在しているため何もしない
167 Assert.True(this.tabinfo.HomeTab.Contains(100L));
171 public void CanUndoRemovedTab_Test()
173 var tab = new PublicSearchTabModel("tab");
174 this.tabinfo.AddTab(tab);
175 Assert.False(this.tabinfo.CanUndoRemovedTab);
177 this.tabinfo.RemoveTab(tab.TabName);
178 Assert.True(this.tabinfo.CanUndoRemovedTab);
182 public void UndoRemovedTab_Test()
184 var tab = new PublicSearchTabModel("tab");
185 this.tabinfo.AddTab(tab);
186 Assert.True(this.tabinfo.ContainsTab("tab"));
187 Assert.Empty(this.tabinfo.RemovedTab);
189 this.tabinfo.RemoveTab("tab");
190 Assert.False(this.tabinfo.ContainsTab("tab"));
191 Assert.Single(this.tabinfo.RemovedTab);
193 var restoredTab = this.tabinfo.UndoRemovedTab();
194 Assert.True(this.tabinfo.ContainsTab("tab"));
195 Assert.Same(tab, restoredTab);
196 Assert.Empty(this.tabinfo.RemovedTab);
200 public void UndoRemovedTab_EmptyError_Test()
202 Assert.Empty(this.tabinfo.RemovedTab);
203 Assert.Throws<TabException>(
204 () => this.tabinfo.UndoRemovedTab()
209 public void UndoRemovedTab_DuplicatedName_Test()
211 var tab = new PublicSearchTabModel("tab");
212 this.tabinfo.AddTab(tab);
213 Assert.Empty(this.tabinfo.RemovedTab);
215 this.tabinfo.RemoveTab("tab");
216 Assert.Single(this.tabinfo.RemovedTab);
218 this.tabinfo.RenameTab("Recent", "tab");
219 Assert.Throws<TabException>(
220 () => this.tabinfo.UndoRemovedTab()
222 Assert.Single(this.tabinfo.RemovedTab);
226 public void RenameTab_PositionTest()
228 var replyTab = this.tabinfo.Tabs["Reply"];
229 Assert.Equal(1, this.tabinfo.Tabs.IndexOf(replyTab));
231 this.tabinfo.RenameTab("Reply", "Reply12345");
233 Assert.Equal("Reply12345", replyTab.TabName);
234 Assert.Equal(1, this.tabinfo.Tabs.IndexOf(replyTab));
238 public void RenameTab_SelectedTabTest()
240 var replyTab = this.tabinfo.Tabs["Reply"];
242 this.tabinfo.SelectTab("Reply");
243 this.tabinfo.RenameTab("Reply", "Reply12345");
245 Assert.Equal("Reply12345", replyTab.TabName);
246 Assert.Equal("Reply12345", this.tabinfo.SelectedTabName);
247 Assert.Equal(replyTab, this.tabinfo.SelectedTab);
251 public void MoveTab_MoveToStart_Test()
253 Assert.Equal(0, this.tabinfo.Tabs.IndexOf("Recent"));
254 Assert.Equal(1, this.tabinfo.Tabs.IndexOf("Reply"));
256 this.tabinfo.MoveTab(0, this.tabinfo.MentionTab);
258 Assert.Equal(0, this.tabinfo.Tabs.IndexOf("Reply"));
259 Assert.Equal(1, this.tabinfo.Tabs.IndexOf("Recent"));
263 public void MoveTab_MoveToEnd_Test()
265 Assert.Equal(4, this.tabinfo.Tabs.Count);
266 Assert.Equal(2, this.tabinfo.Tabs.IndexOf("DM"));
267 Assert.Equal(3, this.tabinfo.Tabs.IndexOf("Favorites"));
269 this.tabinfo.MoveTab(3, this.tabinfo.DirectMessageTab);
271 Assert.Equal(2, this.tabinfo.Tabs.IndexOf("Favorites"));
272 Assert.Equal(3, this.tabinfo.Tabs.IndexOf("DM"));
276 public void MoveTab_OutOfRangeError_Test()
278 Assert.Equal(4, this.tabinfo.Tabs.Count);
279 Assert.Throws<ArgumentOutOfRangeException>(
280 () => this.tabinfo.MoveTab(-1, this.tabinfo.HomeTab)
282 Assert.Throws<ArgumentOutOfRangeException>(
283 () => this.tabinfo.MoveTab(4, this.tabinfo.HomeTab)
288 [InlineData("Reply", true)]
289 [InlineData("UNKNOWN NAME", false)]
290 public void ContainsTab_TabName_Test(string tabName, bool expected)
291 => Assert.Equal(expected, this.tabinfo.ContainsTab(tabName));
294 public void ContainsTab_TabInstance_Test()
296 Assert.True(this.tabinfo.ContainsTab(this.tabinfo.HomeTab));
297 Assert.False(this.tabinfo.ContainsTab(new PublicSearchTabModel("tab")));
301 public void SelectTab_Test()
303 this.tabinfo.SelectTab("Reply");
305 Assert.Equal("Reply", this.tabinfo.SelectedTabName);
306 Assert.IsType<MentionsTabModel>(this.tabinfo.SelectedTab);
310 public void SelectTab_NotExistTest()
311 => Assert.Throws<ArgumentException>(() => this.tabinfo.SelectTab("INVALID"));
314 [InlineData(MyCommon.TabUsageType.Home, typeof(HomeTabModel))]
315 [InlineData(MyCommon.TabUsageType.Mentions, typeof(MentionsTabModel))]
316 [InlineData(MyCommon.TabUsageType.DirectMessage, typeof(DirectMessagesTabModel))]
317 [InlineData(MyCommon.TabUsageType.Favorites, typeof(FavoritesTabModel))]
318 [InlineData(MyCommon.TabUsageType.UserDefined, typeof(FilterTabModel))]
319 [InlineData(MyCommon.TabUsageType.UserTimeline, typeof(UserTimelineTabModel))]
320 [InlineData(MyCommon.TabUsageType.PublicSearch, typeof(PublicSearchTabModel))]
321 [InlineData(MyCommon.TabUsageType.Lists, typeof(ListTimelineTabModel))]
322 [InlineData(MyCommon.TabUsageType.Mute, typeof(MuteTabModel))]
323 public void CreateTabFromSettings_TabTypeTest(MyCommon.TabUsageType tabType, Type expected)
325 var tabSetting = new SettingTabs.SettingTabItem
330 var tabinfo = this.CreateInstance();
331 var tab = tabinfo.CreateTabFromSettings(tabSetting);
332 Assert.IsType(expected, tab);
336 public void CreateTabFromSettings_FilterTabTest()
338 var tabSetting = new SettingTabs.SettingTabItem
341 TabType = MyCommon.TabUsageType.UserDefined,
342 FilterArray = new PostFilterRule[]
344 new() { FilterName = "foo" },
347 var tabinfo = this.CreateInstance();
348 var tab = tabinfo.CreateTabFromSettings(tabSetting);
349 Assert.IsType<FilterTabModel>(tab);
351 var filterTab = (FilterTabModel)tab!;
352 Assert.Equal("foo", filterTab.FilterArray.First().FilterName);
356 public void CreateTabFromSettings_PublicSearchTabTest()
358 var tabSetting = new SettingTabs.SettingTabItem
361 TabType = MyCommon.TabUsageType.PublicSearch,
365 var tabinfo = this.CreateInstance();
366 var tab = tabinfo.CreateTabFromSettings(tabSetting);
367 Assert.IsType<PublicSearchTabModel>(tab);
369 var searchTab = (PublicSearchTabModel)tab!;
370 Assert.Equal("foo", searchTab.SearchWords);
371 Assert.Equal("ja", searchTab.SearchLang);
375 public void AddDefaultTabs_Test()
377 var tabinfo = this.CreateInstance();
378 Assert.Equal(0, tabinfo.Tabs.Count);
380 tabinfo.AddDefaultTabs();
382 Assert.Equal(4, tabinfo.Tabs.Count);
383 Assert.IsType<HomeTabModel>(tabinfo.Tabs[0]);
384 Assert.IsType<MentionsTabModel>(tabinfo.Tabs[1]);
385 Assert.IsType<DirectMessagesTabModel>(tabinfo.Tabs[2]);
386 Assert.IsType<FavoritesTabModel>(tabinfo.Tabs[3]);
388 var homeTab = tabinfo.HomeTab;
389 Assert.False(homeTab.Notify);
391 var mentionsTab = tabinfo.MentionTab;
392 Assert.True(mentionsTab.Notify);
394 var dmTab = tabinfo.DirectMessageTab;
395 Assert.True(dmTab.Notify);
397 var favsTab = tabinfo.FavoriteTab;
398 Assert.False(favsTab.Notify);
402 public void MakeTabName_Test()
404 var baseTabName = "NewTab";
405 Assert.Equal("NewTab", this.tabinfo.MakeTabName(baseTabName, 5));
409 public void MakeTabName_RetryTest()
411 this.tabinfo.AddTab(new FilterTabModel("NewTab"));
412 this.tabinfo.AddTab(new FilterTabModel("NewTab2"));
414 var baseTabName = "NewTab";
415 Assert.Equal("NewTab3", this.tabinfo.MakeTabName(baseTabName, 5));
419 public void MakeTabName_RetryErrorTest()
421 this.tabinfo.AddTab(new FilterTabModel("NewTab"));
422 this.tabinfo.AddTab(new FilterTabModel("NewTab2"));
423 this.tabinfo.AddTab(new FilterTabModel("NewTab3"));
424 this.tabinfo.AddTab(new FilterTabModel("NewTab4"));
425 this.tabinfo.AddTab(new FilterTabModel("NewTab5"));
427 var baseTabName = "NewTab";
428 Assert.Throws<TabException>(() => this.tabinfo.MakeTabName(baseTabName, 5));
432 public void SetSortMode_Test()
434 this.tabinfo.SetSortMode(ComparerMode.Id, SortOrder.Descending);
435 Assert.Equal(ComparerMode.Id, this.tabinfo.SortMode);
436 Assert.Equal(SortOrder.Descending, this.tabinfo.SortOrder);
437 Assert.Equal(ComparerMode.Id, this.tabinfo.HomeTab.SortMode);
438 Assert.Equal(SortOrder.Descending, this.tabinfo.HomeTab.SortOrder);
440 this.tabinfo.SetSortMode(ComparerMode.Source, SortOrder.Ascending);
441 Assert.Equal(ComparerMode.Source, this.tabinfo.SortMode);
442 Assert.Equal(SortOrder.Ascending, this.tabinfo.SortOrder);
443 Assert.Equal(ComparerMode.Source, this.tabinfo.HomeTab.SortMode);
444 Assert.Equal(SortOrder.Ascending, this.tabinfo.HomeTab.SortOrder);
448 public void ToggleSortOrder_SameMode_Test()
450 this.tabinfo.SetSortMode(ComparerMode.Id, SortOrder.Descending);
451 Assert.Equal(ComparerMode.Id, this.tabinfo.SortMode);
452 Assert.Equal(SortOrder.Descending, this.tabinfo.SortOrder);
454 this.tabinfo.ToggleSortOrder(ComparerMode.Id);
455 Assert.Equal(ComparerMode.Id, this.tabinfo.SortMode);
456 Assert.Equal(SortOrder.Ascending, this.tabinfo.SortOrder);
458 this.tabinfo.ToggleSortOrder(ComparerMode.Id);
459 Assert.Equal(ComparerMode.Id, this.tabinfo.SortMode);
460 Assert.Equal(SortOrder.Descending, this.tabinfo.SortOrder);
464 public void ToggleSortOrder_OtherMode_Test()
466 this.tabinfo.SetSortMode(ComparerMode.Id, SortOrder.Descending);
467 Assert.Equal(ComparerMode.Id, this.tabinfo.SortMode);
468 Assert.Equal(SortOrder.Descending, this.tabinfo.SortOrder);
470 this.tabinfo.ToggleSortOrder(ComparerMode.Source);
471 Assert.Equal(ComparerMode.Source, this.tabinfo.SortMode);
472 Assert.Equal(SortOrder.Ascending, this.tabinfo.SortOrder);
476 public void IsMuted_Test()
478 this.tabinfo.MuteUserIds = new HashSet<long> { 12345L };
480 var post = new PostClass
485 Assert.True(this.tabinfo.IsMuted(post, isHomeTimeline: true));
489 public void IsMuted_NotMutingTest()
491 this.tabinfo.MuteUserIds = new HashSet<long> { 12345L };
493 var post = new PostClass
498 Assert.False(this.tabinfo.IsMuted(post, isHomeTimeline: true));
502 public void IsMuted_RetweetTest()
504 this.tabinfo.MuteUserIds = new HashSet<long> { 12345L };
506 var post = new PostClass
509 RetweetedByUserId = 12345L,
512 Assert.True(this.tabinfo.IsMuted(post, isHomeTimeline: true));
516 public void IsMuted_RetweetNotMutingTest()
518 this.tabinfo.MuteUserIds = new HashSet<long> { 12345L };
520 var post = new PostClass
523 RetweetedByUserId = 22222L,
526 Assert.False(this.tabinfo.IsMuted(post, isHomeTimeline: true));
530 public void IsMuted_ReplyTest()
532 this.tabinfo.MuteUserIds = new HashSet<long> { 12345L };
534 // ミュート対象のユーザーであってもリプライの場合は対象外とする
535 var post = new PostClass
538 Text = "@foo hogehoge",
541 Assert.False(this.tabinfo.IsMuted(post, isHomeTimeline: true));
545 public void IsMuted_NotInHomeTimelineTest()
547 this.tabinfo.MuteUserIds = new HashSet<long> { 12345L };
549 // Recent以外のタブ(検索など)の場合は対象外とする
550 var post = new PostClass
555 Assert.False(this.tabinfo.IsMuted(post, isHomeTimeline: false));
559 public void IsMuted_MuteTabRulesTest()
561 this.tabinfo.MuteUserIds = new HashSet<long> { };
563 var muteTab = new MuteTabModel();
564 muteTab.AddFilter(new PostFilterRule
569 this.tabinfo.AddTab(muteTab);
571 var post = new PostClass
577 Assert.True(this.tabinfo.IsMuted(post, isHomeTimeline: true));
581 public void IsMuted_MuteTabRules_NotInHomeTimelineTest()
583 this.tabinfo.MuteUserIds = new HashSet<long> { };
585 var muteTab = new MuteTabModel();
586 muteTab.AddFilter(new PostFilterRule
591 this.tabinfo.AddTab(muteTab);
593 // ミュートタブによるミュートはリプライも対象とする
594 var post = new PostClass
598 Text = "@hoge hogehoge",
601 Assert.True(this.tabinfo.IsMuted(post, isHomeTimeline: false));
605 public void SetReadAllTab_MarkAsReadTest()
607 var tab1 = new PublicSearchTabModel("search1");
608 var tab2 = new PublicSearchTabModel("search2");
610 this.tabinfo.AddTab(tab1);
611 this.tabinfo.AddTab(tab2);
613 // search1 に追加するツイート (StatusId: 100, 150, 200; すべて未読)
614 tab1.UnreadManage = true;
615 tab1.AddPostQueue(new PostClass { StatusId = 100L, IsRead = false });
616 tab1.AddPostQueue(new PostClass { StatusId = 150L, IsRead = false });
617 tab1.AddPostQueue(new PostClass { StatusId = 200L, IsRead = false });
619 // search2 に追加するツイート (StatusId: 150, 200, 250; すべて未読)
620 tab2.UnreadManage = true;
621 tab2.AddPostQueue(new PostClass { StatusId = 150L, IsRead = false });
622 tab2.AddPostQueue(new PostClass { StatusId = 200L, IsRead = false });
623 tab2.AddPostQueue(new PostClass { StatusId = 250L, IsRead = false });
625 this.tabinfo.DistributePosts();
626 this.tabinfo.SubmitUpdate();
629 Assert.Equal(3, tab1.UnreadCount);
630 Assert.Equal(3, tab2.UnreadCount);
634 // StatusId: 200 を既読にする (search1, search2 両方に含まれる)
635 this.tabinfo.SetReadAllTab(200L, read: true);
636 Assert.Equal(2, tab1.UnreadCount);
637 Assert.Equal(2, tab2.UnreadCount);
639 // StatusId: 100 を既読にする (search1 のみに含まれる)
640 this.tabinfo.SetReadAllTab(100L, read: true);
641 Assert.Equal(1, tab1.UnreadCount);
642 Assert.Equal(2, tab2.UnreadCount);
646 public void SetReadAllTab_MarkAsUnreadTest()
648 var tab1 = new PublicSearchTabModel("search1");
649 var tab2 = new PublicSearchTabModel("search2");
651 this.tabinfo.AddTab(tab1);
652 this.tabinfo.AddTab(tab2);
654 // search1 に追加するツイート (StatusId: 100, 150, 200; すべて既読)
655 tab1.UnreadManage = true;
656 tab1.AddPostQueue(new PostClass { StatusId = 100L, IsRead = true });
657 tab1.AddPostQueue(new PostClass { StatusId = 150L, IsRead = true });
658 tab1.AddPostQueue(new PostClass { StatusId = 200L, IsRead = true });
660 // search2 に追加するツイート (StatusId: 150, 200, 250; すべて既読)
661 tab2.UnreadManage = true;
662 tab2.AddPostQueue(new PostClass { StatusId = 150L, IsRead = true });
663 tab2.AddPostQueue(new PostClass { StatusId = 200L, IsRead = true });
664 tab2.AddPostQueue(new PostClass { StatusId = 250L, IsRead = true });
666 this.tabinfo.DistributePosts();
667 this.tabinfo.SubmitUpdate();
670 Assert.Equal(0, tab1.UnreadCount);
671 Assert.Equal(0, tab2.UnreadCount);
675 // StatusId: 200 を未読にする (search1, search2 両方に含まれる)
676 this.tabinfo.SetReadAllTab(200L, read: false);
677 Assert.Equal(1, tab1.UnreadCount);
678 Assert.Equal(1, tab2.UnreadCount);
680 // StatusId: 100 を未読にする (search1 のみに含まれる)
681 this.tabinfo.SetReadAllTab(100L, read: false);
682 Assert.Equal(2, tab1.UnreadCount);
683 Assert.Equal(1, tab2.UnreadCount);
687 public void SetReadHomeTab_Test()
689 var homeTab = this.tabinfo.Tabs["Recent"];
691 // Recent に追加するツイート (StatusId: 100, 150, 200; すべて未読)
692 homeTab.UnreadManage = true;
693 this.tabinfo.AddPost(new PostClass { StatusId = 100L, IsRead = false });
694 this.tabinfo.AddPost(new PostClass { StatusId = 150L, IsRead = false });
695 this.tabinfo.AddPost(new PostClass { StatusId = 200L, IsRead = false });
697 this.tabinfo.DistributePosts();
698 this.tabinfo.SubmitUpdate();
701 Assert.Equal(3, homeTab.UnreadCount);
703 // Recent タブのツイートをすべて未読にする
704 this.tabinfo.SetReadHomeTab();
705 Assert.Equal(0, homeTab.UnreadCount);
709 public void SetReadHomeTab_ContainsReplyTest()
711 var homeTab = this.tabinfo.Tabs["Recent"];
713 // Recent に追加するツイート (StatusId: 100, 150, 200; すべて未読)
714 // StatusId: 150 は未読だがリプライ属性が付いている
715 homeTab.UnreadManage = true;
716 this.tabinfo.AddPost(new PostClass { StatusId = 100L, IsRead = false });
717 this.tabinfo.AddPost(new PostClass { StatusId = 150L, IsRead = false, IsReply = true });
718 this.tabinfo.AddPost(new PostClass { StatusId = 200L, IsRead = false });
720 this.tabinfo.DistributePosts();
721 this.tabinfo.SubmitUpdate();
724 Assert.Equal(3, homeTab.UnreadCount);
726 // Recent タブのツイートをすべて未読にする
727 this.tabinfo.SetReadHomeTab();
729 // リプライである StatusId: 150 を除いてすべて未読になっている
730 Assert.Equal(1, homeTab.UnreadCount);
731 Assert.Equal(150L, homeTab.NextUnreadId);
735 public void SetReadHomeTab_ContainsFilterHitTest()
737 var homeTab = this.tabinfo.Tabs["Recent"];
739 // Recent に追加するツイート (StatusId: 100, 150, 200; すべて未読)
740 homeTab.UnreadManage = true;
741 this.tabinfo.AddPost(new PostClass { StatusId = 100L, IsRead = false });
742 this.tabinfo.AddPost(new PostClass { StatusId = 150L, IsRead = false });
743 this.tabinfo.AddPost(new PostClass { StatusId = 200L, IsRead = false });
745 // StatusId: 150 だけ FilterTab の振り分けルールにヒットする (PostClass.FilterHit が true になる)
746 var filterTab = new FilterTabModel("FilterTab");
747 filterTab.AddFilter(TestPostFilterRule.Create(x =>
748 x.StatusId == 150L ? MyCommon.HITRESULT.Copy : MyCommon.HITRESULT.None));
749 this.tabinfo.AddTab(filterTab);
751 this.tabinfo.DistributePosts();
752 this.tabinfo.SubmitUpdate();
755 Assert.Equal(3, homeTab.UnreadCount);
757 // Recent タブのツイートをすべて未読にする
758 this.tabinfo.SetReadHomeTab();
760 // FilterHit が true である StatusId: 150 を除いてすべて未読になっている
761 Assert.Equal(1, homeTab.UnreadCount);
762 Assert.Equal(150L, homeTab.NextUnreadId);
766 public void SubmitUpdate_RemoveSubmit_Test()
768 var homeTab = this.tabinfo.HomeTab;
770 this.tabinfo.AddPost(new PostClass { StatusId = 100L });
771 this.tabinfo.DistributePosts();
772 this.tabinfo.SubmitUpdate();
774 Assert.Equal(1, homeTab.AllCount);
776 this.tabinfo.RemovePostFromAllTabs(100L, setIsDeleted: true);
779 Assert.Equal(1, homeTab.AllCount);
781 this.tabinfo.SubmitUpdate(out _, out _, out _, out var isDeletePost);
783 Assert.True(isDeletePost);
784 Assert.Equal(0, homeTab.AllCount);
785 Assert.False(this.tabinfo.Posts.ContainsKey(100L));
789 public void SubmitUpdate_RemoveSubmit_NotOrphaned_Test()
791 var homeTab = this.tabinfo.HomeTab;
792 var favTab = this.tabinfo.FavoriteTab;
794 this.tabinfo.AddPost(new PostClass { StatusId = 100L, IsFav = true });
795 this.tabinfo.DistributePosts();
796 this.tabinfo.SubmitUpdate();
798 Assert.Equal(1, homeTab.AllCount);
799 Assert.Equal(1, favTab.AllCount);
801 // favTab のみ発言を除去 (homeTab には残ったまま)
802 favTab.EnqueueRemovePost(100L, setIsDeleted: false);
805 Assert.Equal(1, homeTab.AllCount);
806 Assert.Equal(1, favTab.AllCount);
808 this.tabinfo.SubmitUpdate(out _, out _, out _, out var isDeletePost);
810 Assert.True(isDeletePost);
811 Assert.Equal(1, homeTab.AllCount);
812 Assert.Equal(0, favTab.AllCount);
814 // homeTab には発言が残っているので Posts からは削除されない
815 Assert.True(this.tabinfo.Posts.ContainsKey(100L));
819 public void SubmitUpdate_NotifyPriorityTest()
821 var homeTab = this.tabinfo.HomeTab;
822 homeTab.UnreadManage = true;
823 homeTab.Notify = true;
824 homeTab.SoundFile = "home.wav";
826 var replyTab = this.tabinfo.MentionTab;
827 replyTab.UnreadManage = true;
828 replyTab.Notify = true;
829 replyTab.SoundFile = "reply.wav";
831 var dmTab = this.tabinfo.DirectMessageTab;
832 dmTab.UnreadManage = true;
834 dmTab.SoundFile = "dm.wav";
837 this.tabinfo.AddPost(new PostClass { StatusId = 100L, IsRead = false });
840 this.tabinfo.AddPost(new PostClass { StatusId = 200L, IsReply = true, IsRead = false });
843 dmTab.AddPostQueue(new PostClass { StatusId = 300L, IsDm = true, IsRead = false });
845 this.tabinfo.DistributePosts();
847 this.tabinfo.SubmitUpdate(out var soundFile, out var notifyPosts, out _, out _);
849 // DM が最も優先度が高いため DM の通知音が再生される
850 Assert.Equal("dm.wav", soundFile);
853 Assert.Equal(3, notifyPosts.Length);
857 public void SubmitUpdate_IgnoreEmptySoundPath_Test()
859 var homeTab = this.tabinfo.HomeTab;
860 homeTab.UnreadManage = true;
861 homeTab.Notify = true;
862 homeTab.SoundFile = "home.wav";
864 var replyTab = this.tabinfo.MentionTab;
865 replyTab.UnreadManage = true;
866 replyTab.Notify = true;
867 replyTab.SoundFile = "";
870 this.tabinfo.AddPost(new PostClass { StatusId = 100L, IsRead = false });
873 this.tabinfo.AddPost(new PostClass { StatusId = 200L, IsReply = true, IsRead = false });
875 this.tabinfo.DistributePosts();
877 this.tabinfo.SubmitUpdate(out var soundFile, out var notifyPosts, out _, out _);
879 // リプライの方が通知音の優先度が高いが、replyTab.SoundFile が空文字列なので次点の Recent の通知音を鳴らす
880 Assert.Equal("home.wav", soundFile);
883 Assert.Equal(2, notifyPosts.Length);
887 public void FilterAll_CopyFilterTest()
889 var homeTab = this.tabinfo.HomeTab;
891 var myTab1 = new FilterTabModel("MyTab1");
892 this.tabinfo.AddTab(myTab1);
894 var filter = new PostFilterRule
902 myTab1.AddFilter(filter);
903 myTab1.FilterModified = false;
905 this.tabinfo.AddPost(new PostClass { StatusId = 100L, ScreenName = "aaa" });
906 this.tabinfo.AddPost(new PostClass { StatusId = 200L, ScreenName = "bbb" });
907 this.tabinfo.AddPost(new PostClass { StatusId = 300L, ScreenName = "ccc" });
908 this.tabinfo.DistributePosts();
909 this.tabinfo.SubmitUpdate();
912 Assert.Equal(new[] { 100L, 200L, 300L }, homeTab.StatusIds, AnyOrderComparer<long>.Instance);
913 Assert.Equal(new[] { 100L }, myTab1.StatusIds);
916 filter.FilterName = "bbb";
919 this.tabinfo.FilterAll();
920 this.tabinfo.DistributePosts();
921 this.tabinfo.SubmitUpdate();
924 // [statusId: 100] は MyTab1 から取り除かれる
925 // [statusId: 200] は Recent から MyTab1 にコピーされる
928 Assert.Equal(new[] { 100L, 200L, 300L }, homeTab.StatusIds, AnyOrderComparer<long>.Instance);
929 Assert.Equal(new[] { 200L }, myTab1.StatusIds);
933 public void FilterAll_CopyAndMarkFilterTest()
935 var homeTab = this.tabinfo.HomeTab;
937 var myTab1 = new FilterTabModel("MyTab1");
938 this.tabinfo.AddTab(myTab1);
940 var filter = new PostFilterRule
948 myTab1.AddFilter(filter);
949 myTab1.FilterModified = false;
951 this.tabinfo.AddPost(new PostClass { StatusId = 100L, ScreenName = "aaa" });
952 this.tabinfo.AddPost(new PostClass { StatusId = 200L, ScreenName = "bbb" });
953 this.tabinfo.AddPost(new PostClass { StatusId = 300L, ScreenName = "ccc" });
954 this.tabinfo.DistributePosts();
955 this.tabinfo.SubmitUpdate();
958 Assert.Equal(new[] { 100L, 200L, 300L }, homeTab.StatusIds, AnyOrderComparer<long>.Instance);
959 Assert.Equal(new[] { 100L }, myTab1.StatusIds);
962 filter.FilterName = "bbb";
965 this.tabinfo.FilterAll();
966 this.tabinfo.DistributePosts();
967 this.tabinfo.SubmitUpdate();
970 // [statusId: 100] は MyTab1 から取り除かれる
971 // [statusId: 200] は Recent から MyTab1 にコピーされ、マークが付与される
974 Assert.Equal(new[] { 100L, 200L, 300L }, homeTab.StatusIds, AnyOrderComparer<long>.Instance);
975 Assert.Equal(new[] { 200L }, myTab1.StatusIds);
977 // [statusId: 200] は IsMark が true の状態になる
978 Assert.True(this.tabinfo[200L]!.IsMark);
982 public void FilterAll_MoveFilterTest()
984 var homeTab = this.tabinfo.HomeTab;
986 var myTab1 = new FilterTabModel("MyTab1");
987 this.tabinfo.AddTab(myTab1);
989 var filter = new PostFilterRule
996 myTab1.AddFilter(filter);
997 myTab1.FilterModified = false;
999 this.tabinfo.AddPost(new PostClass { StatusId = 100L, ScreenName = "aaa" });
1000 this.tabinfo.AddPost(new PostClass { StatusId = 200L, ScreenName = "bbb" });
1001 this.tabinfo.AddPost(new PostClass { StatusId = 300L, ScreenName = "ccc" });
1002 this.tabinfo.DistributePosts();
1003 this.tabinfo.SubmitUpdate();
1006 Assert.Equal(new[] { 200L, 300L }, homeTab.StatusIds, AnyOrderComparer<long>.Instance);
1007 Assert.Equal(new[] { 100L }, myTab1.StatusIds);
1010 filter.FilterName = "bbb";
1013 this.tabinfo.FilterAll();
1014 this.tabinfo.DistributePosts();
1015 this.tabinfo.SubmitUpdate();
1018 // [statusId: 100] は MyTab1 から取り除かれて Recent に戻される
1019 // [statusId: 200] は Recent から MyTab1 に移動される
1022 Assert.Equal(new[] { 100L, 300L }, homeTab.StatusIds, AnyOrderComparer<long>.Instance);
1023 Assert.Equal(new[] { 200L }, myTab1.StatusIds);
1027 public void FilterAll_MoveFilterTest2()
1029 var homeTab = this.tabinfo.HomeTab;
1031 var myTab1 = new FilterTabModel("MyTab1");
1032 var myTab2 = new FilterTabModel("MyTab2");
1033 this.tabinfo.AddTab(myTab1);
1034 this.tabinfo.AddTab(myTab2);
1036 var filter1 = new PostFilterRule
1043 myTab1.AddFilter(filter1);
1044 myTab1.FilterModified = false;
1046 var filter2 = new PostFilterRule
1053 myTab2.AddFilter(filter2);
1054 myTab2.FilterModified = false;
1056 this.tabinfo.AddPost(new PostClass { StatusId = 100L, ScreenName = "aaa" });
1057 this.tabinfo.AddPost(new PostClass { StatusId = 200L, ScreenName = "bbb" });
1058 this.tabinfo.AddPost(new PostClass { StatusId = 300L, ScreenName = "ccc" });
1059 this.tabinfo.DistributePosts();
1060 this.tabinfo.SubmitUpdate();
1063 Assert.Equal(new[] { 300L }, homeTab.StatusIds);
1064 Assert.Equal(new[] { 100L }, myTab1.StatusIds);
1065 Assert.Equal(new[] { 200L }, myTab2.StatusIds);
1067 // MyTab1 のフィルタを変更する
1068 filter1.FilterName = "bbb";
1070 // MyTab2 のフィルタを変更する
1071 filter2.FilterName = "ccc";
1074 this.tabinfo.FilterAll();
1075 this.tabinfo.DistributePosts();
1076 this.tabinfo.SubmitUpdate();
1079 // [statusId: 100] は MyTab1 から取り除かれて Recent に戻される
1080 // [statusId: 200] は MyTab1 に移動される
1081 // [statusId: 200] は MyTab2 から取り除かれるが MyTab1 に移動されているので Recent には戻さない
1082 // [statusId: 300] は Recent から MyTab2 に移動される
1085 Assert.Equal(new[] { 100L }, homeTab.StatusIds);
1086 Assert.Equal(new[] { 200L }, myTab1.StatusIds);
1087 Assert.Equal(new[] { 300L }, myTab2.StatusIds);
1091 public void FilterAll_ExcludeReplyFilterTest()
1093 var homeTab = this.tabinfo.HomeTab;
1094 var replyTab = this.tabinfo.MentionTab;
1096 var filter = new PostFilterRule
1098 // @aaa からのリプライは Reply タブに振り分けない
1099 ExFilterName = "aaa",
1101 replyTab.AddFilter(filter);
1102 replyTab.FilterModified = false;
1104 this.tabinfo.AddPost(new PostClass { StatusId = 100L, ScreenName = "aaa", IsReply = true });
1105 this.tabinfo.AddPost(new PostClass { StatusId = 200L, ScreenName = "bbb", IsReply = true });
1106 this.tabinfo.AddPost(new PostClass { StatusId = 300L, ScreenName = "ccc", IsReply = true });
1107 this.tabinfo.DistributePosts();
1108 this.tabinfo.SubmitUpdate();
1111 Assert.Equal(new[] { 100L, 200L, 300L }, homeTab.StatusIds, AnyOrderComparer<long>.Instance);
1112 Assert.Equal(new[] { 200L, 300L }, replyTab.StatusIds, AnyOrderComparer<long>.Instance);
1114 // [statusId: 100] は IsExcludeReply が true の状態になっている
1115 Assert.True(this.tabinfo[100L]!.IsExcludeReply);
1118 filter.ExFilterName = "bbb";
1121 this.tabinfo.FilterAll();
1122 this.tabinfo.DistributePosts();
1123 this.tabinfo.SubmitUpdate();
1126 // [statusId: 100] は Reply にコピーされ、IsExcludeReply が false になる
1127 // [statusId: 200] は Reply から取り除かれ、IsExcludeReply が true になる
1130 Assert.Equal(new[] { 100L, 200L, 300L }, homeTab.StatusIds, AnyOrderComparer<long>.Instance);
1131 Assert.Equal(new[] { 100L, 300L }, replyTab.StatusIds, AnyOrderComparer<long>.Instance);
1133 // [statusId: 100] は IsExcludeReply が false の状態になる
1134 Assert.False(this.tabinfo[100L]!.IsExcludeReply);
1136 // [statusId: 200] は IsExcludeReply が true の状態になる
1137 Assert.True(this.tabinfo[200L]!.IsExcludeReply);
1141 public void RefreshOwl_HomeTabTest()
1143 var post = new PostClass
1150 this.tabinfo.AddPost(post);
1151 this.tabinfo.DistributePosts();
1152 this.tabinfo.SubmitUpdate();
1154 var followerIds = new HashSet<long> { 123L };
1155 this.tabinfo.RefreshOwl(followerIds);
1157 Assert.False(post.IsOwl);
1161 public void RefreshOwl_InnerStoregeTabTest()
1163 var tab = new PublicSearchTabModel("search");
1164 this.tabinfo.AddTab(tab);
1166 var post = new PostClass
1173 tab.AddPostQueue(post);
1174 this.tabinfo.DistributePosts();
1175 this.tabinfo.SubmitUpdate();
1177 var followerIds = new HashSet<long> { 123L };
1178 this.tabinfo.RefreshOwl(followerIds);
1180 Assert.False(post.IsOwl);
1184 public void RefreshOwl_UnfollowedTest()
1186 var post = new PostClass
1193 this.tabinfo.AddPost(post);
1194 this.tabinfo.DistributePosts();
1195 this.tabinfo.SubmitUpdate();
1197 var followerIds = new HashSet<long> { 456L };
1198 this.tabinfo.RefreshOwl(followerIds);
1200 Assert.True(post.IsOwl);
1203 private class TestPostFilterRule : PostFilterRule
1205 public static PostFilterRule Create(Func<PostClass, MyCommon.HITRESULT> filterDelegate)
1207 return new TestPostFilterRule
1209 filterDelegate = filterDelegate,