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 RenameTab_PositionTest()
173 var replyTab = this.tabinfo.Tabs["Reply"];
174 Assert.Equal(1, this.tabinfo.Tabs.IndexOf(replyTab));
176 this.tabinfo.RenameTab("Reply", "Reply12345");
178 Assert.Equal("Reply12345", replyTab.TabName);
179 Assert.Equal(1, this.tabinfo.Tabs.IndexOf(replyTab));
183 public void RenameTab_SelectedTabTest()
185 var replyTab = this.tabinfo.Tabs["Reply"];
187 this.tabinfo.SelectTab("Reply");
188 this.tabinfo.RenameTab("Reply", "Reply12345");
190 Assert.Equal("Reply12345", replyTab.TabName);
191 Assert.Equal("Reply12345", this.tabinfo.SelectedTabName);
192 Assert.Equal(replyTab, this.tabinfo.SelectedTab);
196 public void MoveTab_MoveToStart_Test()
198 Assert.Equal(0, this.tabinfo.Tabs.IndexOf("Recent"));
199 Assert.Equal(1, this.tabinfo.Tabs.IndexOf("Reply"));
201 this.tabinfo.MoveTab(0, this.tabinfo.MentionTab);
203 Assert.Equal(0, this.tabinfo.Tabs.IndexOf("Reply"));
204 Assert.Equal(1, this.tabinfo.Tabs.IndexOf("Recent"));
208 public void MoveTab_MoveToEnd_Test()
210 Assert.Equal(4, this.tabinfo.Tabs.Count);
211 Assert.Equal(2, this.tabinfo.Tabs.IndexOf("DM"));
212 Assert.Equal(3, this.tabinfo.Tabs.IndexOf("Favorites"));
214 this.tabinfo.MoveTab(3, this.tabinfo.DirectMessageTab);
216 Assert.Equal(2, this.tabinfo.Tabs.IndexOf("Favorites"));
217 Assert.Equal(3, this.tabinfo.Tabs.IndexOf("DM"));
221 public void MoveTab_OutOfRangeError_Test()
223 Assert.Equal(4, this.tabinfo.Tabs.Count);
224 Assert.Throws<ArgumentOutOfRangeException>(
225 () => this.tabinfo.MoveTab(-1, this.tabinfo.HomeTab)
227 Assert.Throws<ArgumentOutOfRangeException>(
228 () => this.tabinfo.MoveTab(4, this.tabinfo.HomeTab)
233 [InlineData("Reply", true)]
234 [InlineData("UNKNOWN NAME", false)]
235 public void ContainsTab_TabName_Test(string tabName, bool expected)
236 => Assert.Equal(expected, this.tabinfo.ContainsTab(tabName));
239 public void ContainsTab_TabInstance_Test()
241 Assert.True(this.tabinfo.ContainsTab(this.tabinfo.HomeTab));
242 Assert.False(this.tabinfo.ContainsTab(new PublicSearchTabModel("tab")));
246 public void SelectTab_Test()
248 this.tabinfo.SelectTab("Reply");
250 Assert.Equal("Reply", this.tabinfo.SelectedTabName);
251 Assert.IsType<MentionsTabModel>(this.tabinfo.SelectedTab);
255 public void SelectTab_NotExistTest()
256 => Assert.Throws<ArgumentException>(() => this.tabinfo.SelectTab("INVALID"));
259 [InlineData(MyCommon.TabUsageType.Home, typeof(HomeTabModel))]
260 [InlineData(MyCommon.TabUsageType.Mentions, typeof(MentionsTabModel))]
261 [InlineData(MyCommon.TabUsageType.DirectMessage, typeof(DirectMessagesTabModel))]
262 [InlineData(MyCommon.TabUsageType.Favorites, typeof(FavoritesTabModel))]
263 [InlineData(MyCommon.TabUsageType.UserDefined, typeof(FilterTabModel))]
264 [InlineData(MyCommon.TabUsageType.UserTimeline, typeof(UserTimelineTabModel))]
265 [InlineData(MyCommon.TabUsageType.PublicSearch, typeof(PublicSearchTabModel))]
266 [InlineData(MyCommon.TabUsageType.Lists, typeof(ListTimelineTabModel))]
267 [InlineData(MyCommon.TabUsageType.Mute, typeof(MuteTabModel))]
268 public void CreateTabFromSettings_TabTypeTest(MyCommon.TabUsageType tabType, Type expected)
270 var tabSetting = new SettingTabs.SettingTabItem
275 var tabinfo = this.CreateInstance();
276 var tab = tabinfo.CreateTabFromSettings(tabSetting);
277 Assert.IsType(expected, tab);
281 public void CreateTabFromSettings_FilterTabTest()
283 var tabSetting = new SettingTabs.SettingTabItem
286 TabType = MyCommon.TabUsageType.UserDefined,
287 FilterArray = new PostFilterRule[]
289 new() { FilterName = "foo" },
292 var tabinfo = this.CreateInstance();
293 var tab = tabinfo.CreateTabFromSettings(tabSetting);
294 Assert.IsType<FilterTabModel>(tab);
296 var filterTab = (FilterTabModel)tab!;
297 Assert.Equal("foo", filterTab.FilterArray.First().FilterName);
301 public void CreateTabFromSettings_PublicSearchTabTest()
303 var tabSetting = new SettingTabs.SettingTabItem
306 TabType = MyCommon.TabUsageType.PublicSearch,
310 var tabinfo = this.CreateInstance();
311 var tab = tabinfo.CreateTabFromSettings(tabSetting);
312 Assert.IsType<PublicSearchTabModel>(tab);
314 var searchTab = (PublicSearchTabModel)tab!;
315 Assert.Equal("foo", searchTab.SearchWords);
316 Assert.Equal("ja", searchTab.SearchLang);
320 public void AddDefaultTabs_Test()
322 var tabinfo = this.CreateInstance();
323 Assert.Equal(0, tabinfo.Tabs.Count);
325 tabinfo.AddDefaultTabs();
327 Assert.Equal(4, tabinfo.Tabs.Count);
328 Assert.IsType<HomeTabModel>(tabinfo.Tabs[0]);
329 Assert.IsType<MentionsTabModel>(tabinfo.Tabs[1]);
330 Assert.IsType<DirectMessagesTabModel>(tabinfo.Tabs[2]);
331 Assert.IsType<FavoritesTabModel>(tabinfo.Tabs[3]);
333 var homeTab = tabinfo.HomeTab;
334 Assert.False(homeTab.Notify);
336 var mentionsTab = tabinfo.MentionTab;
337 Assert.True(mentionsTab.Notify);
339 var dmTab = tabinfo.DirectMessageTab;
340 Assert.True(dmTab.Notify);
342 var favsTab = tabinfo.FavoriteTab;
343 Assert.False(favsTab.Notify);
347 public void MakeTabName_Test()
349 var baseTabName = "NewTab";
350 Assert.Equal("NewTab", this.tabinfo.MakeTabName(baseTabName, 5));
354 public void MakeTabName_RetryTest()
356 this.tabinfo.AddTab(new FilterTabModel("NewTab"));
357 this.tabinfo.AddTab(new FilterTabModel("NewTab2"));
359 var baseTabName = "NewTab";
360 Assert.Equal("NewTab3", this.tabinfo.MakeTabName(baseTabName, 5));
364 public void MakeTabName_RetryErrorTest()
366 this.tabinfo.AddTab(new FilterTabModel("NewTab"));
367 this.tabinfo.AddTab(new FilterTabModel("NewTab2"));
368 this.tabinfo.AddTab(new FilterTabModel("NewTab3"));
369 this.tabinfo.AddTab(new FilterTabModel("NewTab4"));
370 this.tabinfo.AddTab(new FilterTabModel("NewTab5"));
372 var baseTabName = "NewTab";
373 Assert.Throws<TabException>(() => this.tabinfo.MakeTabName(baseTabName, 5));
377 public void SetSortMode_Test()
379 this.tabinfo.SetSortMode(ComparerMode.Id, SortOrder.Descending);
380 Assert.Equal(ComparerMode.Id, this.tabinfo.SortMode);
381 Assert.Equal(SortOrder.Descending, this.tabinfo.SortOrder);
382 Assert.Equal(ComparerMode.Id, this.tabinfo.HomeTab.SortMode);
383 Assert.Equal(SortOrder.Descending, this.tabinfo.HomeTab.SortOrder);
385 this.tabinfo.SetSortMode(ComparerMode.Source, SortOrder.Ascending);
386 Assert.Equal(ComparerMode.Source, this.tabinfo.SortMode);
387 Assert.Equal(SortOrder.Ascending, this.tabinfo.SortOrder);
388 Assert.Equal(ComparerMode.Source, this.tabinfo.HomeTab.SortMode);
389 Assert.Equal(SortOrder.Ascending, this.tabinfo.HomeTab.SortOrder);
393 public void ToggleSortOrder_SameMode_Test()
395 this.tabinfo.SetSortMode(ComparerMode.Id, SortOrder.Descending);
396 Assert.Equal(ComparerMode.Id, this.tabinfo.SortMode);
397 Assert.Equal(SortOrder.Descending, this.tabinfo.SortOrder);
399 this.tabinfo.ToggleSortOrder(ComparerMode.Id);
400 Assert.Equal(ComparerMode.Id, this.tabinfo.SortMode);
401 Assert.Equal(SortOrder.Ascending, this.tabinfo.SortOrder);
403 this.tabinfo.ToggleSortOrder(ComparerMode.Id);
404 Assert.Equal(ComparerMode.Id, this.tabinfo.SortMode);
405 Assert.Equal(SortOrder.Descending, this.tabinfo.SortOrder);
409 public void ToggleSortOrder_OtherMode_Test()
411 this.tabinfo.SetSortMode(ComparerMode.Id, SortOrder.Descending);
412 Assert.Equal(ComparerMode.Id, this.tabinfo.SortMode);
413 Assert.Equal(SortOrder.Descending, this.tabinfo.SortOrder);
415 this.tabinfo.ToggleSortOrder(ComparerMode.Source);
416 Assert.Equal(ComparerMode.Source, this.tabinfo.SortMode);
417 Assert.Equal(SortOrder.Ascending, this.tabinfo.SortOrder);
421 public void IsMuted_Test()
423 this.tabinfo.MuteUserIds = new HashSet<long> { 12345L };
425 var post = new PostClass
430 Assert.True(this.tabinfo.IsMuted(post, isHomeTimeline: true));
434 public void IsMuted_NotMutingTest()
436 this.tabinfo.MuteUserIds = new HashSet<long> { 12345L };
438 var post = new PostClass
443 Assert.False(this.tabinfo.IsMuted(post, isHomeTimeline: true));
447 public void IsMuted_RetweetTest()
449 this.tabinfo.MuteUserIds = new HashSet<long> { 12345L };
451 var post = new PostClass
454 RetweetedByUserId = 12345L,
457 Assert.True(this.tabinfo.IsMuted(post, isHomeTimeline: true));
461 public void IsMuted_RetweetNotMutingTest()
463 this.tabinfo.MuteUserIds = new HashSet<long> { 12345L };
465 var post = new PostClass
468 RetweetedByUserId = 22222L,
471 Assert.False(this.tabinfo.IsMuted(post, isHomeTimeline: true));
475 public void IsMuted_ReplyTest()
477 this.tabinfo.MuteUserIds = new HashSet<long> { 12345L };
479 // ミュート対象のユーザーであってもリプライの場合は対象外とする
480 var post = new PostClass
483 Text = "@foo hogehoge",
486 Assert.False(this.tabinfo.IsMuted(post, isHomeTimeline: true));
490 public void IsMuted_NotInHomeTimelineTest()
492 this.tabinfo.MuteUserIds = new HashSet<long> { 12345L };
494 // Recent以外のタブ(検索など)の場合は対象外とする
495 var post = new PostClass
500 Assert.False(this.tabinfo.IsMuted(post, isHomeTimeline: false));
504 public void IsMuted_MuteTabRulesTest()
506 this.tabinfo.MuteUserIds = new HashSet<long> { };
508 var muteTab = new MuteTabModel();
509 muteTab.AddFilter(new PostFilterRule
514 this.tabinfo.AddTab(muteTab);
516 var post = new PostClass
522 Assert.True(this.tabinfo.IsMuted(post, isHomeTimeline: true));
526 public void IsMuted_MuteTabRules_NotInHomeTimelineTest()
528 this.tabinfo.MuteUserIds = new HashSet<long> { };
530 var muteTab = new MuteTabModel();
531 muteTab.AddFilter(new PostFilterRule
536 this.tabinfo.AddTab(muteTab);
538 // ミュートタブによるミュートはリプライも対象とする
539 var post = new PostClass
543 Text = "@hoge hogehoge",
546 Assert.True(this.tabinfo.IsMuted(post, isHomeTimeline: false));
550 public void SetReadAllTab_MarkAsReadTest()
552 var tab1 = new PublicSearchTabModel("search1");
553 var tab2 = new PublicSearchTabModel("search2");
555 this.tabinfo.AddTab(tab1);
556 this.tabinfo.AddTab(tab2);
558 // search1 に追加するツイート (StatusId: 100, 150, 200; すべて未読)
559 tab1.UnreadManage = true;
560 tab1.AddPostQueue(new PostClass { StatusId = 100L, IsRead = false });
561 tab1.AddPostQueue(new PostClass { StatusId = 150L, IsRead = false });
562 tab1.AddPostQueue(new PostClass { StatusId = 200L, IsRead = false });
564 // search2 に追加するツイート (StatusId: 150, 200, 250; すべて未読)
565 tab2.UnreadManage = true;
566 tab2.AddPostQueue(new PostClass { StatusId = 150L, IsRead = false });
567 tab2.AddPostQueue(new PostClass { StatusId = 200L, IsRead = false });
568 tab2.AddPostQueue(new PostClass { StatusId = 250L, IsRead = false });
570 this.tabinfo.DistributePosts();
571 this.tabinfo.SubmitUpdate();
574 Assert.Equal(3, tab1.UnreadCount);
575 Assert.Equal(3, tab2.UnreadCount);
579 // StatusId: 200 を既読にする (search1, search2 両方に含まれる)
580 this.tabinfo.SetReadAllTab(200L, read: true);
581 Assert.Equal(2, tab1.UnreadCount);
582 Assert.Equal(2, tab2.UnreadCount);
584 // StatusId: 100 を既読にする (search1 のみに含まれる)
585 this.tabinfo.SetReadAllTab(100L, read: true);
586 Assert.Equal(1, tab1.UnreadCount);
587 Assert.Equal(2, tab2.UnreadCount);
591 public void SetReadAllTab_MarkAsUnreadTest()
593 var tab1 = new PublicSearchTabModel("search1");
594 var tab2 = new PublicSearchTabModel("search2");
596 this.tabinfo.AddTab(tab1);
597 this.tabinfo.AddTab(tab2);
599 // search1 に追加するツイート (StatusId: 100, 150, 200; すべて既読)
600 tab1.UnreadManage = true;
601 tab1.AddPostQueue(new PostClass { StatusId = 100L, IsRead = true });
602 tab1.AddPostQueue(new PostClass { StatusId = 150L, IsRead = true });
603 tab1.AddPostQueue(new PostClass { StatusId = 200L, IsRead = true });
605 // search2 に追加するツイート (StatusId: 150, 200, 250; すべて既読)
606 tab2.UnreadManage = true;
607 tab2.AddPostQueue(new PostClass { StatusId = 150L, IsRead = true });
608 tab2.AddPostQueue(new PostClass { StatusId = 200L, IsRead = true });
609 tab2.AddPostQueue(new PostClass { StatusId = 250L, IsRead = true });
611 this.tabinfo.DistributePosts();
612 this.tabinfo.SubmitUpdate();
615 Assert.Equal(0, tab1.UnreadCount);
616 Assert.Equal(0, tab2.UnreadCount);
620 // StatusId: 200 を未読にする (search1, search2 両方に含まれる)
621 this.tabinfo.SetReadAllTab(200L, read: false);
622 Assert.Equal(1, tab1.UnreadCount);
623 Assert.Equal(1, tab2.UnreadCount);
625 // StatusId: 100 を未読にする (search1 のみに含まれる)
626 this.tabinfo.SetReadAllTab(100L, read: false);
627 Assert.Equal(2, tab1.UnreadCount);
628 Assert.Equal(1, tab2.UnreadCount);
632 public void SetReadHomeTab_Test()
634 var homeTab = this.tabinfo.Tabs["Recent"];
636 // Recent に追加するツイート (StatusId: 100, 150, 200; すべて未読)
637 homeTab.UnreadManage = true;
638 this.tabinfo.AddPost(new PostClass { StatusId = 100L, IsRead = false });
639 this.tabinfo.AddPost(new PostClass { StatusId = 150L, IsRead = false });
640 this.tabinfo.AddPost(new PostClass { StatusId = 200L, IsRead = false });
642 this.tabinfo.DistributePosts();
643 this.tabinfo.SubmitUpdate();
646 Assert.Equal(3, homeTab.UnreadCount);
648 // Recent タブのツイートをすべて未読にする
649 this.tabinfo.SetReadHomeTab();
650 Assert.Equal(0, homeTab.UnreadCount);
654 public void SetReadHomeTab_ContainsReplyTest()
656 var homeTab = this.tabinfo.Tabs["Recent"];
658 // Recent に追加するツイート (StatusId: 100, 150, 200; すべて未読)
659 // StatusId: 150 は未読だがリプライ属性が付いている
660 homeTab.UnreadManage = true;
661 this.tabinfo.AddPost(new PostClass { StatusId = 100L, IsRead = false });
662 this.tabinfo.AddPost(new PostClass { StatusId = 150L, IsRead = false, IsReply = true });
663 this.tabinfo.AddPost(new PostClass { StatusId = 200L, IsRead = false });
665 this.tabinfo.DistributePosts();
666 this.tabinfo.SubmitUpdate();
669 Assert.Equal(3, homeTab.UnreadCount);
671 // Recent タブのツイートをすべて未読にする
672 this.tabinfo.SetReadHomeTab();
674 // リプライである StatusId: 150 を除いてすべて未読になっている
675 Assert.Equal(1, homeTab.UnreadCount);
676 Assert.Equal(150L, homeTab.NextUnreadId);
680 public void SetReadHomeTab_ContainsFilterHitTest()
682 var homeTab = this.tabinfo.Tabs["Recent"];
684 // Recent に追加するツイート (StatusId: 100, 150, 200; すべて未読)
685 homeTab.UnreadManage = true;
686 this.tabinfo.AddPost(new PostClass { StatusId = 100L, IsRead = false });
687 this.tabinfo.AddPost(new PostClass { StatusId = 150L, IsRead = false });
688 this.tabinfo.AddPost(new PostClass { StatusId = 200L, IsRead = false });
690 // StatusId: 150 だけ FilterTab の振り分けルールにヒットする (PostClass.FilterHit が true になる)
691 var filterTab = new FilterTabModel("FilterTab");
692 filterTab.AddFilter(TestPostFilterRule.Create(x =>
693 x.StatusId == 150L ? MyCommon.HITRESULT.Copy : MyCommon.HITRESULT.None));
694 this.tabinfo.AddTab(filterTab);
696 this.tabinfo.DistributePosts();
697 this.tabinfo.SubmitUpdate();
700 Assert.Equal(3, homeTab.UnreadCount);
702 // Recent タブのツイートをすべて未読にする
703 this.tabinfo.SetReadHomeTab();
705 // FilterHit が true である StatusId: 150 を除いてすべて未読になっている
706 Assert.Equal(1, homeTab.UnreadCount);
707 Assert.Equal(150L, homeTab.NextUnreadId);
711 public void SubmitUpdate_RemoveSubmit_Test()
713 var homeTab = this.tabinfo.HomeTab;
715 this.tabinfo.AddPost(new PostClass { StatusId = 100L });
716 this.tabinfo.DistributePosts();
717 this.tabinfo.SubmitUpdate();
719 Assert.Equal(1, homeTab.AllCount);
721 this.tabinfo.RemovePostFromAllTabs(100L, setIsDeleted: true);
724 Assert.Equal(1, homeTab.AllCount);
726 this.tabinfo.SubmitUpdate(out _, out _, out _, out var isDeletePost);
728 Assert.True(isDeletePost);
729 Assert.Equal(0, homeTab.AllCount);
730 Assert.False(this.tabinfo.Posts.ContainsKey(100L));
734 public void SubmitUpdate_RemoveSubmit_NotOrphaned_Test()
736 var homeTab = this.tabinfo.HomeTab;
737 var favTab = this.tabinfo.FavoriteTab;
739 this.tabinfo.AddPost(new PostClass { StatusId = 100L, IsFav = true });
740 this.tabinfo.DistributePosts();
741 this.tabinfo.SubmitUpdate();
743 Assert.Equal(1, homeTab.AllCount);
744 Assert.Equal(1, favTab.AllCount);
746 // favTab のみ発言を除去 (homeTab には残ったまま)
747 favTab.EnqueueRemovePost(100L, setIsDeleted: false);
750 Assert.Equal(1, homeTab.AllCount);
751 Assert.Equal(1, favTab.AllCount);
753 this.tabinfo.SubmitUpdate(out _, out _, out _, out var isDeletePost);
755 Assert.True(isDeletePost);
756 Assert.Equal(1, homeTab.AllCount);
757 Assert.Equal(0, favTab.AllCount);
759 // homeTab には発言が残っているので Posts からは削除されない
760 Assert.True(this.tabinfo.Posts.ContainsKey(100L));
764 public void SubmitUpdate_NotifyPriorityTest()
766 var homeTab = this.tabinfo.HomeTab;
767 homeTab.UnreadManage = true;
768 homeTab.Notify = true;
769 homeTab.SoundFile = "home.wav";
771 var replyTab = this.tabinfo.MentionTab;
772 replyTab.UnreadManage = true;
773 replyTab.Notify = true;
774 replyTab.SoundFile = "reply.wav";
776 var dmTab = this.tabinfo.DirectMessageTab;
777 dmTab.UnreadManage = true;
779 dmTab.SoundFile = "dm.wav";
782 this.tabinfo.AddPost(new PostClass { StatusId = 100L, IsRead = false });
785 this.tabinfo.AddPost(new PostClass { StatusId = 200L, IsReply = true, IsRead = false });
788 dmTab.AddPostQueue(new PostClass { StatusId = 300L, IsDm = true, IsRead = false });
790 this.tabinfo.DistributePosts();
792 this.tabinfo.SubmitUpdate(out var soundFile, out var notifyPosts, out _, out _);
794 // DM が最も優先度が高いため DM の通知音が再生される
795 Assert.Equal("dm.wav", soundFile);
798 Assert.Equal(3, notifyPosts.Length);
802 public void SubmitUpdate_IgnoreEmptySoundPath_Test()
804 var homeTab = this.tabinfo.HomeTab;
805 homeTab.UnreadManage = true;
806 homeTab.Notify = true;
807 homeTab.SoundFile = "home.wav";
809 var replyTab = this.tabinfo.MentionTab;
810 replyTab.UnreadManage = true;
811 replyTab.Notify = true;
812 replyTab.SoundFile = "";
815 this.tabinfo.AddPost(new PostClass { StatusId = 100L, IsRead = false });
818 this.tabinfo.AddPost(new PostClass { StatusId = 200L, IsReply = true, IsRead = false });
820 this.tabinfo.DistributePosts();
822 this.tabinfo.SubmitUpdate(out var soundFile, out var notifyPosts, out _, out _);
824 // リプライの方が通知音の優先度が高いが、replyTab.SoundFile が空文字列なので次点の Recent の通知音を鳴らす
825 Assert.Equal("home.wav", soundFile);
828 Assert.Equal(2, notifyPosts.Length);
832 public void FilterAll_CopyFilterTest()
834 var homeTab = this.tabinfo.HomeTab;
836 var myTab1 = new FilterTabModel("MyTab1");
837 this.tabinfo.AddTab(myTab1);
839 var filter = new PostFilterRule
847 myTab1.AddFilter(filter);
848 myTab1.FilterModified = false;
850 this.tabinfo.AddPost(new PostClass { StatusId = 100L, ScreenName = "aaa" });
851 this.tabinfo.AddPost(new PostClass { StatusId = 200L, ScreenName = "bbb" });
852 this.tabinfo.AddPost(new PostClass { StatusId = 300L, ScreenName = "ccc" });
853 this.tabinfo.DistributePosts();
854 this.tabinfo.SubmitUpdate();
857 Assert.Equal(new[] { 100L, 200L, 300L }, homeTab.StatusIds, AnyOrderComparer<long>.Instance);
858 Assert.Equal(new[] { 100L }, myTab1.StatusIds);
861 filter.FilterName = "bbb";
864 this.tabinfo.FilterAll();
865 this.tabinfo.DistributePosts();
866 this.tabinfo.SubmitUpdate();
869 // [statusId: 100] は MyTab1 から取り除かれる
870 // [statusId: 200] は Recent から MyTab1 にコピーされる
873 Assert.Equal(new[] { 100L, 200L, 300L }, homeTab.StatusIds, AnyOrderComparer<long>.Instance);
874 Assert.Equal(new[] { 200L }, myTab1.StatusIds);
878 public void FilterAll_CopyAndMarkFilterTest()
880 var homeTab = this.tabinfo.HomeTab;
882 var myTab1 = new FilterTabModel("MyTab1");
883 this.tabinfo.AddTab(myTab1);
885 var filter = new PostFilterRule
893 myTab1.AddFilter(filter);
894 myTab1.FilterModified = false;
896 this.tabinfo.AddPost(new PostClass { StatusId = 100L, ScreenName = "aaa" });
897 this.tabinfo.AddPost(new PostClass { StatusId = 200L, ScreenName = "bbb" });
898 this.tabinfo.AddPost(new PostClass { StatusId = 300L, ScreenName = "ccc" });
899 this.tabinfo.DistributePosts();
900 this.tabinfo.SubmitUpdate();
903 Assert.Equal(new[] { 100L, 200L, 300L }, homeTab.StatusIds, AnyOrderComparer<long>.Instance);
904 Assert.Equal(new[] { 100L }, myTab1.StatusIds);
907 filter.FilterName = "bbb";
910 this.tabinfo.FilterAll();
911 this.tabinfo.DistributePosts();
912 this.tabinfo.SubmitUpdate();
915 // [statusId: 100] は MyTab1 から取り除かれる
916 // [statusId: 200] は Recent から MyTab1 にコピーされ、マークが付与される
919 Assert.Equal(new[] { 100L, 200L, 300L }, homeTab.StatusIds, AnyOrderComparer<long>.Instance);
920 Assert.Equal(new[] { 200L }, myTab1.StatusIds);
922 // [statusId: 200] は IsMark が true の状態になる
923 Assert.True(this.tabinfo[200L]!.IsMark);
927 public void FilterAll_MoveFilterTest()
929 var homeTab = this.tabinfo.HomeTab;
931 var myTab1 = new FilterTabModel("MyTab1");
932 this.tabinfo.AddTab(myTab1);
934 var filter = new PostFilterRule
941 myTab1.AddFilter(filter);
942 myTab1.FilterModified = false;
944 this.tabinfo.AddPost(new PostClass { StatusId = 100L, ScreenName = "aaa" });
945 this.tabinfo.AddPost(new PostClass { StatusId = 200L, ScreenName = "bbb" });
946 this.tabinfo.AddPost(new PostClass { StatusId = 300L, ScreenName = "ccc" });
947 this.tabinfo.DistributePosts();
948 this.tabinfo.SubmitUpdate();
951 Assert.Equal(new[] { 200L, 300L }, homeTab.StatusIds, AnyOrderComparer<long>.Instance);
952 Assert.Equal(new[] { 100L }, myTab1.StatusIds);
955 filter.FilterName = "bbb";
958 this.tabinfo.FilterAll();
959 this.tabinfo.DistributePosts();
960 this.tabinfo.SubmitUpdate();
963 // [statusId: 100] は MyTab1 から取り除かれて Recent に戻される
964 // [statusId: 200] は Recent から MyTab1 に移動される
967 Assert.Equal(new[] { 100L, 300L }, homeTab.StatusIds, AnyOrderComparer<long>.Instance);
968 Assert.Equal(new[] { 200L }, myTab1.StatusIds);
972 public void FilterAll_MoveFilterTest2()
974 var homeTab = this.tabinfo.HomeTab;
976 var myTab1 = new FilterTabModel("MyTab1");
977 var myTab2 = new FilterTabModel("MyTab2");
978 this.tabinfo.AddTab(myTab1);
979 this.tabinfo.AddTab(myTab2);
981 var filter1 = new PostFilterRule
988 myTab1.AddFilter(filter1);
989 myTab1.FilterModified = false;
991 var filter2 = new PostFilterRule
998 myTab2.AddFilter(filter2);
999 myTab2.FilterModified = false;
1001 this.tabinfo.AddPost(new PostClass { StatusId = 100L, ScreenName = "aaa" });
1002 this.tabinfo.AddPost(new PostClass { StatusId = 200L, ScreenName = "bbb" });
1003 this.tabinfo.AddPost(new PostClass { StatusId = 300L, ScreenName = "ccc" });
1004 this.tabinfo.DistributePosts();
1005 this.tabinfo.SubmitUpdate();
1008 Assert.Equal(new[] { 300L }, homeTab.StatusIds);
1009 Assert.Equal(new[] { 100L }, myTab1.StatusIds);
1010 Assert.Equal(new[] { 200L }, myTab2.StatusIds);
1012 // MyTab1 のフィルタを変更する
1013 filter1.FilterName = "bbb";
1015 // MyTab2 のフィルタを変更する
1016 filter2.FilterName = "ccc";
1019 this.tabinfo.FilterAll();
1020 this.tabinfo.DistributePosts();
1021 this.tabinfo.SubmitUpdate();
1024 // [statusId: 100] は MyTab1 から取り除かれて Recent に戻される
1025 // [statusId: 200] は MyTab1 に移動される
1026 // [statusId: 200] は MyTab2 から取り除かれるが MyTab1 に移動されているので Recent には戻さない
1027 // [statusId: 300] は Recent から MyTab2 に移動される
1030 Assert.Equal(new[] { 100L }, homeTab.StatusIds);
1031 Assert.Equal(new[] { 200L }, myTab1.StatusIds);
1032 Assert.Equal(new[] { 300L }, myTab2.StatusIds);
1036 public void FilterAll_ExcludeReplyFilterTest()
1038 var homeTab = this.tabinfo.HomeTab;
1039 var replyTab = this.tabinfo.MentionTab;
1041 var filter = new PostFilterRule
1043 // @aaa からのリプライは Reply タブに振り分けない
1044 ExFilterName = "aaa",
1046 replyTab.AddFilter(filter);
1047 replyTab.FilterModified = false;
1049 this.tabinfo.AddPost(new PostClass { StatusId = 100L, ScreenName = "aaa", IsReply = true });
1050 this.tabinfo.AddPost(new PostClass { StatusId = 200L, ScreenName = "bbb", IsReply = true });
1051 this.tabinfo.AddPost(new PostClass { StatusId = 300L, ScreenName = "ccc", IsReply = true });
1052 this.tabinfo.DistributePosts();
1053 this.tabinfo.SubmitUpdate();
1056 Assert.Equal(new[] { 100L, 200L, 300L }, homeTab.StatusIds, AnyOrderComparer<long>.Instance);
1057 Assert.Equal(new[] { 200L, 300L }, replyTab.StatusIds, AnyOrderComparer<long>.Instance);
1059 // [statusId: 100] は IsExcludeReply が true の状態になっている
1060 Assert.True(this.tabinfo[100L]!.IsExcludeReply);
1063 filter.ExFilterName = "bbb";
1066 this.tabinfo.FilterAll();
1067 this.tabinfo.DistributePosts();
1068 this.tabinfo.SubmitUpdate();
1071 // [statusId: 100] は Reply にコピーされ、IsExcludeReply が false になる
1072 // [statusId: 200] は Reply から取り除かれ、IsExcludeReply が true になる
1075 Assert.Equal(new[] { 100L, 200L, 300L }, homeTab.StatusIds, AnyOrderComparer<long>.Instance);
1076 Assert.Equal(new[] { 100L, 300L }, replyTab.StatusIds, AnyOrderComparer<long>.Instance);
1078 // [statusId: 100] は IsExcludeReply が false の状態になる
1079 Assert.False(this.tabinfo[100L]!.IsExcludeReply);
1081 // [statusId: 200] は IsExcludeReply が true の状態になる
1082 Assert.True(this.tabinfo[200L]!.IsExcludeReply);
1086 public void RefreshOwl_HomeTabTest()
1088 var post = new PostClass
1095 this.tabinfo.AddPost(post);
1096 this.tabinfo.DistributePosts();
1097 this.tabinfo.SubmitUpdate();
1099 var followerIds = new HashSet<long> { 123L };
1100 this.tabinfo.RefreshOwl(followerIds);
1102 Assert.False(post.IsOwl);
1106 public void RefreshOwl_InnerStoregeTabTest()
1108 var tab = new PublicSearchTabModel("search");
1109 this.tabinfo.AddTab(tab);
1111 var post = new PostClass
1118 tab.AddPostQueue(post);
1119 this.tabinfo.DistributePosts();
1120 this.tabinfo.SubmitUpdate();
1122 var followerIds = new HashSet<long> { 123L };
1123 this.tabinfo.RefreshOwl(followerIds);
1125 Assert.False(post.IsOwl);
1129 public void RefreshOwl_UnfollowedTest()
1131 var post = new PostClass
1138 this.tabinfo.AddPost(post);
1139 this.tabinfo.DistributePosts();
1140 this.tabinfo.SubmitUpdate();
1142 var followerIds = new HashSet<long> { 456L };
1143 this.tabinfo.RefreshOwl(followerIds);
1145 Assert.True(post.IsOwl);
1148 private class TestPostFilterRule : PostFilterRule
1150 public static PostFilterRule Create(Func<PostClass, MyCommon.HITRESULT> filterDelegate)
1152 return new TestPostFilterRule
1154 filterDelegate = filterDelegate,