OSDN Git Service

C#7.0で追加されたout変数宣言の構文を使用する
[opentween/open-tween.git] / OpenTween.Tests / Models / TabInformationTest.cs
1 // OpenTween - Client of Twitter
2 // Copyright (c) 2014 kim_upsilon (@kim_upsilon) <https://upsilo.net/~upsilon/>
3 // All rights reserved.
4 //
5 // This file is part of OpenTween.
6 //
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)
10 // any later version.
11 //
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
15 // for more details.
16 //
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.
21
22 using System;
23 using System.Collections.Generic;
24 using System.Linq;
25 using System.Reflection;
26 using System.Text;
27 using Xunit;
28 using Xunit.Extensions;
29
30 namespace OpenTween.Models
31 {
32     public class TabInformationTest
33     {
34         private TabInformations tabinfo;
35
36         public TabInformationTest()
37         {
38             this.tabinfo = Activator.CreateInstance(typeof(TabInformations), true) as TabInformations;
39
40             // TabInformation.GetInstance() で取得できるようにする
41             var field = typeof(TabInformations).GetField("_instance",
42                 BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.SetField);
43             field.SetValue(null, this.tabinfo);
44
45             // 標準のタブを追加
46             this.tabinfo.AddTab(new HomeTabModel("Recent"));
47             this.tabinfo.AddTab(new MentionsTabModel("Reply"));
48             this.tabinfo.AddTab(new DirectMessagesTabModel("DM"));
49             this.tabinfo.AddTab(new FavoritesTabModel("Favorites"));
50         }
51
52         [Fact]
53         public void AddTab_Test()
54         {
55             var tab = new FilterTabModel("MyTab");
56             var ret = this.tabinfo.AddTab(tab);
57
58             Assert.True(ret);
59             Assert.Same(tab, this.tabinfo.Tabs.Values.Last());
60         }
61
62         [Fact]
63         public void AddTab_DuplicateTest()
64         {
65             var ret = this.tabinfo.AddTab(new FilterTabModel("Recent"));
66
67             Assert.False(ret);
68         }
69
70         [Fact]
71         public void MakeTabName_Test()
72         {
73             var baseTabName = "NewTab";
74             Assert.Equal("NewTab", this.tabinfo.MakeTabName(baseTabName, 5));
75         }
76
77         [Fact]
78         public void MakeTabName_RetryTest()
79         {
80             this.tabinfo.AddTab(new FilterTabModel("NewTab"));
81             this.tabinfo.AddTab(new FilterTabModel("NewTab2"));
82
83             var baseTabName = "NewTab";
84             Assert.Equal("NewTab3", this.tabinfo.MakeTabName(baseTabName, 5));
85         }
86
87         [Fact]
88         public void MakeTabName_RetryErrorTest()
89         {
90             this.tabinfo.AddTab(new FilterTabModel("NewTab"));
91             this.tabinfo.AddTab(new FilterTabModel("NewTab2"));
92             this.tabinfo.AddTab(new FilterTabModel("NewTab3"));
93             this.tabinfo.AddTab(new FilterTabModel("NewTab4"));
94             this.tabinfo.AddTab(new FilterTabModel("NewTab5"));
95
96             var baseTabName = "NewTab";
97             Assert.Throws<TabException>(() => this.tabinfo.MakeTabName(baseTabName, 5));
98         }
99
100         [Fact]
101         public void IsMuted_Test()
102         {
103             this.tabinfo.MuteUserIds = new HashSet<long> { 12345L };
104
105             var post = new PostClass
106             {
107                 UserId = 12345L,
108                 Text = "hogehoge",
109             };
110             Assert.True(this.tabinfo.IsMuted(post, isHomeTimeline: true));
111         }
112
113         [Fact]
114         public void IsMuted_NotMutingTest()
115         {
116             this.tabinfo.MuteUserIds = new HashSet<long> { 12345L };
117
118             var post = new PostClass
119             {
120                 UserId = 11111L,
121                 Text = "hogehoge",
122             };
123             Assert.False(this.tabinfo.IsMuted(post, isHomeTimeline: true));
124         }
125
126         [Fact]
127         public void IsMuted_RetweetTest()
128         {
129             this.tabinfo.MuteUserIds = new HashSet<long> { 12345L };
130
131             var post = new PostClass
132             {
133                 UserId = 11111L,
134                 RetweetedByUserId = 12345L,
135                 Text = "hogehoge",
136             };
137             Assert.True(this.tabinfo.IsMuted(post, isHomeTimeline: true));
138         }
139
140         [Fact]
141         public void IsMuted_RetweetNotMutingTest()
142         {
143             this.tabinfo.MuteUserIds = new HashSet<long> { 12345L };
144
145             var post = new PostClass
146             {
147                 UserId = 11111L,
148                 RetweetedByUserId = 22222L,
149                 Text = "hogehoge",
150             };
151             Assert.False(this.tabinfo.IsMuted(post, isHomeTimeline: true));
152         }
153
154         [Fact]
155         public void IsMuted_ReplyTest()
156         {
157             this.tabinfo.MuteUserIds = new HashSet<long> { 12345L };
158
159             // ミュート対象のユーザーであってもリプライの場合は対象外とする
160             var post = new PostClass
161             {
162                 UserId = 12345L,
163                 Text = "@foo hogehoge",
164                 IsReply = true,
165             };
166             Assert.False(this.tabinfo.IsMuted(post, isHomeTimeline: true));
167         }
168
169         [Fact]
170         public void IsMuted_NotInHomeTimelineTest()
171         {
172             this.tabinfo.MuteUserIds = new HashSet<long> { 12345L };
173
174             // Recent以外のタブ(検索など)の場合は対象外とする
175             var post = new PostClass
176             {
177                 UserId = 12345L,
178                 Text = "hogehoge",
179             };
180             Assert.False(this.tabinfo.IsMuted(post, isHomeTimeline: false));
181         }
182
183         [Fact]
184         public void IsMuted_MuteTabRulesTest()
185         {
186             this.tabinfo.MuteUserIds = new HashSet<long> { };
187
188             var muteTab = new MuteTabModel();
189             muteTab.AddFilter(new PostFilterRule
190             {
191                 FilterName = "foo",
192                 MoveMatches = true,
193             });
194             this.tabinfo.AddTab(muteTab);
195
196             var post = new PostClass
197             {
198                 UserId = 12345L,
199                 ScreenName = "foo",
200                 Text = "hogehoge",
201             };
202             Assert.True(this.tabinfo.IsMuted(post, isHomeTimeline: true));
203         }
204
205         [Fact]
206         public void IsMuted_MuteTabRules_NotInHomeTimelineTest()
207         {
208             this.tabinfo.MuteUserIds = new HashSet<long> { };
209
210             var muteTab = new MuteTabModel();
211             muteTab.AddFilter(new PostFilterRule
212             {
213                 FilterName = "foo",
214                 MoveMatches = true,
215             });
216             this.tabinfo.AddTab(muteTab);
217
218             // ミュートタブによるミュートはリプライも対象とする
219             var post = new PostClass
220             {
221                 UserId = 12345L,
222                 ScreenName = "foo",
223                 Text = "@hoge hogehoge",
224                 IsReply = true,
225             };
226             Assert.True(this.tabinfo.IsMuted(post, isHomeTimeline: false));
227         }
228
229         [Fact]
230         public void SetReadAllTab_MarkAsReadTest()
231         {
232             var tab1 = new PublicSearchTabModel("search1");
233             var tab2 = new PublicSearchTabModel("search2");
234
235             this.tabinfo.AddTab(tab1);
236             this.tabinfo.AddTab(tab2);
237
238             // search1 に追加するツイート (StatusId: 100, 150, 200; すべて未読)
239             tab1.UnreadManage = true;
240             tab1.AddPostQueue(new PostClass { StatusId = 100L, IsRead = false });
241             tab1.AddPostQueue(new PostClass { StatusId = 150L, IsRead = false });
242             tab1.AddPostQueue(new PostClass { StatusId = 200L, IsRead = false });
243
244             // search2 に追加するツイート (StatusId: 150, 200, 250; すべて未読)
245             tab2.UnreadManage = true;
246             tab2.AddPostQueue(new PostClass { StatusId = 150L, IsRead = false });
247             tab2.AddPostQueue(new PostClass { StatusId = 200L, IsRead = false });
248             tab2.AddPostQueue(new PostClass { StatusId = 250L, IsRead = false });
249
250             this.tabinfo.DistributePosts();
251             this.tabinfo.SubmitUpdate();
252
253             // この時点での各タブの未読件数
254             Assert.Equal(3, tab1.UnreadCount);
255             Assert.Equal(3, tab2.UnreadCount);
256
257             // ... ここまで長い前置き
258
259             // StatusId: 200 を既読にする (search1, search2 両方に含まれる)
260             this.tabinfo.SetReadAllTab(200L, read: true);
261             Assert.Equal(2, tab1.UnreadCount);
262             Assert.Equal(2, tab2.UnreadCount);
263
264             // StatusId: 100 を既読にする (search1 のみに含まれる)
265             this.tabinfo.SetReadAllTab(100L, read: true);
266             Assert.Equal(1, tab1.UnreadCount);
267             Assert.Equal(2, tab2.UnreadCount);
268         }
269
270         [Fact]
271         public void SetReadAllTab_MarkAsUnreadTest()
272         {
273             var tab1 = new PublicSearchTabModel("search1");
274             var tab2 = new PublicSearchTabModel("search2");
275
276             this.tabinfo.AddTab(tab1);
277             this.tabinfo.AddTab(tab2);
278
279             // search1 に追加するツイート (StatusId: 100, 150, 200; すべて既読)
280             tab1.UnreadManage = true;
281             tab1.AddPostQueue(new PostClass { StatusId = 100L, IsRead = true });
282             tab1.AddPostQueue(new PostClass { StatusId = 150L, IsRead = true });
283             tab1.AddPostQueue(new PostClass { StatusId = 200L, IsRead = true });
284
285             // search2 に追加するツイート (StatusId: 150, 200, 250; すべて既読)
286             tab2.UnreadManage = true;
287             tab2.AddPostQueue(new PostClass { StatusId = 150L, IsRead = true });
288             tab2.AddPostQueue(new PostClass { StatusId = 200L, IsRead = true });
289             tab2.AddPostQueue(new PostClass { StatusId = 250L, IsRead = true });
290
291             this.tabinfo.DistributePosts();
292             this.tabinfo.SubmitUpdate();
293
294             // この時点での各タブの未読件数
295             Assert.Equal(0, tab1.UnreadCount);
296             Assert.Equal(0, tab2.UnreadCount);
297
298             // ... ここまで長い前置き
299
300             // StatusId: 200 を未読にする (search1, search2 両方に含まれる)
301             this.tabinfo.SetReadAllTab(200L, read: false);
302             Assert.Equal(1, tab1.UnreadCount);
303             Assert.Equal(1, tab2.UnreadCount);
304
305             // StatusId: 100 を未読にする (search1 のみに含まれる)
306             this.tabinfo.SetReadAllTab(100L, read: false);
307             Assert.Equal(2, tab1.UnreadCount);
308             Assert.Equal(1, tab2.UnreadCount);
309         }
310
311         [Fact]
312         public void SetReadHomeTab_Test()
313         {
314             var homeTab = this.tabinfo.Tabs["Recent"];
315
316             // Recent に追加するツイート (StatusId: 100, 150, 200; すべて未読)
317             homeTab.UnreadManage = true;
318             this.tabinfo.AddPost(new PostClass { StatusId = 100L, IsRead = false });
319             this.tabinfo.AddPost(new PostClass { StatusId = 150L, IsRead = false });
320             this.tabinfo.AddPost(new PostClass { StatusId = 200L, IsRead = false });
321
322             this.tabinfo.DistributePosts();
323             this.tabinfo.SubmitUpdate();
324
325             // この時点でのHomeタブの未読件数
326             Assert.Equal(3, homeTab.UnreadCount);
327
328             // Recent タブのツイートをすべて未読にする
329             this.tabinfo.SetReadHomeTab();
330             Assert.Equal(0, homeTab.UnreadCount);
331         }
332
333         [Fact]
334         public void SetReadHomeTab_ContainsReplyTest()
335         {
336             var homeTab = this.tabinfo.Tabs["Recent"];
337
338             // Recent に追加するツイート (StatusId: 100, 150, 200; すべて未読)
339             // StatusId: 150 は未読だがリプライ属性が付いている
340             homeTab.UnreadManage = true;
341             this.tabinfo.AddPost(new PostClass { StatusId = 100L, IsRead = false });
342             this.tabinfo.AddPost(new PostClass { StatusId = 150L, IsRead = false, IsReply = true });
343             this.tabinfo.AddPost(new PostClass { StatusId = 200L, IsRead = false });
344
345             this.tabinfo.DistributePosts();
346             this.tabinfo.SubmitUpdate();
347
348             // この時点でのHomeタブの未読件数
349             Assert.Equal(3, homeTab.UnreadCount);
350
351             // Recent タブのツイートをすべて未読にする
352             this.tabinfo.SetReadHomeTab();
353
354             // リプライである StatusId: 150 を除いてすべて未読になっている
355             Assert.Equal(1, homeTab.UnreadCount);
356             Assert.Equal(150L, homeTab.NextUnreadId);
357         }
358
359         [Fact]
360         public void SetReadHomeTab_ContainsFilterHitTest()
361         {
362             var homeTab = this.tabinfo.Tabs["Recent"];
363
364             // Recent に追加するツイート (StatusId: 100, 150, 200; すべて未読)
365             homeTab.UnreadManage = true;
366             this.tabinfo.AddPost(new PostClass { StatusId = 100L, IsRead = false });
367             this.tabinfo.AddPost(new PostClass { StatusId = 150L, IsRead = false });
368             this.tabinfo.AddPost(new PostClass { StatusId = 200L, IsRead = false });
369
370             // StatusId: 150 だけ FilterTab の振り分けルールにヒットする (PostClass.FilterHit が true になる)
371             var filterTab = new FilterTabModel("FilterTab");
372             filterTab.AddFilter(TestPostFilterRule.Create(x =>
373                 x.StatusId == 150L ? MyCommon.HITRESULT.Copy : MyCommon.HITRESULT.None));
374             this.tabinfo.AddTab(filterTab);
375
376             this.tabinfo.DistributePosts();
377             this.tabinfo.SubmitUpdate();
378
379             // この時点でのHomeタブの未読件数
380             Assert.Equal(3, homeTab.UnreadCount);
381
382             // Recent タブのツイートをすべて未読にする
383             this.tabinfo.SetReadHomeTab();
384
385             // FilterHit が true である StatusId: 150 を除いてすべて未読になっている
386             Assert.Equal(1, homeTab.UnreadCount);
387             Assert.Equal(150L, homeTab.NextUnreadId);
388         }
389
390         [Fact]
391         public void SubmitUpdate_RemoveSubmit_Test()
392         {
393             var homeTab = this.tabinfo.GetTabByType(MyCommon.TabUsageType.Home);
394
395             this.tabinfo.AddPost(new PostClass { StatusId = 100L });
396             this.tabinfo.DistributePosts();
397             this.tabinfo.SubmitUpdate();
398
399             Assert.Equal(1, homeTab.AllCount);
400
401             this.tabinfo.RemovePostFromAllTabs(100L, setIsDeleted: true);
402
403             // この時点ではまだ削除されない
404             Assert.Equal(1, homeTab.AllCount);
405
406             this.tabinfo.SubmitUpdate(out var soundFile, out var notifyPosts,
407                 out var newMentionOrDm, out var isDeletePost);
408
409             Assert.True(isDeletePost);
410             Assert.Equal(0, homeTab.AllCount);
411             Assert.False(this.tabinfo.Posts.ContainsKey(100L));
412         }
413
414         [Fact]
415         public void SubmitUpdate_RemoveSubmit_NotOrphaned_Test()
416         {
417             var homeTab = this.tabinfo.GetTabByType<HomeTabModel>();
418             var favTab = this.tabinfo.GetTabByType<FavoritesTabModel>();
419
420             this.tabinfo.AddPost(new PostClass { StatusId = 100L, IsFav = true });
421             this.tabinfo.DistributePosts();
422             this.tabinfo.SubmitUpdate();
423
424             Assert.Equal(1, homeTab.AllCount);
425             Assert.Equal(1, favTab.AllCount);
426
427             // favTab のみ発言を除去 (homeTab には残ったまま)
428             favTab.EnqueueRemovePost(100L, setIsDeleted: false);
429
430             // この時点ではまだ削除されない
431             Assert.Equal(1, homeTab.AllCount);
432             Assert.Equal(1, favTab.AllCount);
433
434             this.tabinfo.SubmitUpdate(out var soundFile, out var notifyPosts,
435                 out var newMentionOrDm, out var isDeletePost);
436
437             Assert.True(isDeletePost);
438             Assert.Equal(1, homeTab.AllCount);
439             Assert.Equal(0, favTab.AllCount);
440
441             // homeTab には発言が残っているので Posts からは削除されない
442             Assert.True(this.tabinfo.Posts.ContainsKey(100L));
443         }
444
445         [Fact]
446         public void SubmitUpdate_NotifyPriorityTest()
447         {
448             var homeTab = this.tabinfo.GetTabByType(MyCommon.TabUsageType.Home);
449             homeTab.UnreadManage = true;
450             homeTab.SoundFile = "home.wav";
451
452             var replyTab = this.tabinfo.GetTabByType(MyCommon.TabUsageType.Mentions);
453             replyTab.UnreadManage = true;
454             replyTab.SoundFile = "reply.wav";
455
456             var dmTab = this.tabinfo.GetTabByType(MyCommon.TabUsageType.DirectMessage);
457             dmTab.UnreadManage = true;
458             dmTab.SoundFile = "dm.wav";
459
460             // 通常ツイート
461             this.tabinfo.AddPost(new PostClass { StatusId = 100L, IsRead = false });
462
463             // リプライ
464             this.tabinfo.AddPost(new PostClass { StatusId = 200L, IsReply = true, IsRead = false });
465
466             // DM
467             dmTab.AddPostQueue(new PostClass { StatusId = 300L, IsDm = true, IsRead = false });
468
469             this.tabinfo.DistributePosts();
470
471             this.tabinfo.SubmitUpdate(out var soundFile, out var notifyPosts,
472                 out var newMentionOrDm, out var isDeletePost);
473
474             // DM が最も優先度が高いため DM の通知音が再生される
475             Assert.Equal("dm.wav", soundFile);
476
477             // 通知対象のツイートは 3 件
478             Assert.Equal(3, notifyPosts.Length);
479         }
480
481         [Fact]
482         public void SubmitUpdate_IgnoreEmptySoundPath_Test()
483         {
484             var homeTab = this.tabinfo.GetTabByType(MyCommon.TabUsageType.Home);
485             homeTab.UnreadManage = true;
486             homeTab.SoundFile = "home.wav";
487
488             var replyTab = this.tabinfo.GetTabByType(MyCommon.TabUsageType.Mentions);
489             replyTab.UnreadManage = true;
490             replyTab.SoundFile = "";
491
492             // 通常ツイート
493             this.tabinfo.AddPost(new PostClass { StatusId = 100L, IsRead = false });
494
495             // リプライ
496             this.tabinfo.AddPost(new PostClass { StatusId = 200L, IsReply = true, IsRead = false });
497
498             this.tabinfo.DistributePosts();
499
500             this.tabinfo.SubmitUpdate(out var soundFile, out var notifyPosts,
501                 out var newMentionOrDm, out var isDeletePost);
502
503             // リプライの方が通知音の優先度が高いが、replyTab.SoundFile が空文字列なので次点の Recent の通知音を鳴らす
504             Assert.Equal("home.wav", soundFile);
505
506             // 通知対象のツイートは 2 件
507             Assert.Equal(2, notifyPosts.Length);
508         }
509
510         [Fact]
511         public void FilterAll_CopyFilterTest()
512         {
513             var homeTab = this.tabinfo.GetTabByType(MyCommon.TabUsageType.Home);
514
515             var myTab1 = new FilterTabModel("MyTab1");
516             this.tabinfo.AddTab(myTab1);
517
518             var filter = new PostFilterRule
519             {
520                 FilterName = "aaa",
521
522                 // コピーのみ
523                 MoveMatches = false,
524                 MarkMatches = false,
525             };
526             myTab1.AddFilter(filter);
527             myTab1.FilterModified = false;
528
529             this.tabinfo.AddPost(new PostClass { StatusId = 100L, ScreenName = "aaa" });
530             this.tabinfo.AddPost(new PostClass { StatusId = 200L, ScreenName = "bbb" });
531             this.tabinfo.AddPost(new PostClass { StatusId = 300L, ScreenName = "ccc" });
532             this.tabinfo.DistributePosts();
533             this.tabinfo.SubmitUpdate();
534
535             // この時点での振り分け状態
536             Assert.Equal(new[] { 100L, 200L, 300L }, homeTab.StatusIds, AnyOrderComparer<long>.Instance);
537             Assert.Equal(new[] { 100L }, myTab1.StatusIds);
538
539             // フィルタを変更する
540             filter.FilterName = "bbb";
541
542             // フィルタの変更を反映
543             this.tabinfo.FilterAll();
544             this.tabinfo.DistributePosts();
545             this.tabinfo.SubmitUpdate();
546
547             // 期待する動作:
548             //   [statusId: 100] は MyTab1 から取り除かれる
549             //   [statusId: 200] は Recent から MyTab1 にコピーされる
550
551             // 変更後の振り分け状態
552             Assert.Equal(new[] { 100L, 200L, 300L }, homeTab.StatusIds, AnyOrderComparer<long>.Instance);
553             Assert.Equal(new[] { 200L }, myTab1.StatusIds);
554         }
555
556         [Fact]
557         public void FilterAll_CopyAndMarkFilterTest()
558         {
559             var homeTab = this.tabinfo.GetTabByType(MyCommon.TabUsageType.Home);
560
561             var myTab1 = new FilterTabModel("MyTab1");
562             this.tabinfo.AddTab(myTab1);
563
564             var filter = new PostFilterRule
565             {
566                 FilterName = "aaa",
567
568                 // コピー+マーク
569                 MoveMatches = false,
570                 MarkMatches = true,
571             };
572             myTab1.AddFilter(filter);
573             myTab1.FilterModified = false;
574
575             this.tabinfo.AddPost(new PostClass { StatusId = 100L, ScreenName = "aaa" });
576             this.tabinfo.AddPost(new PostClass { StatusId = 200L, ScreenName = "bbb" });
577             this.tabinfo.AddPost(new PostClass { StatusId = 300L, ScreenName = "ccc" });
578             this.tabinfo.DistributePosts();
579             this.tabinfo.SubmitUpdate();
580
581             // この時点での振り分け状態
582             Assert.Equal(new[] { 100L, 200L, 300L }, homeTab.StatusIds, AnyOrderComparer<long>.Instance);
583             Assert.Equal(new[] { 100L }, myTab1.StatusIds);
584
585             // フィルタを変更する
586             filter.FilterName = "bbb";
587
588             // フィルタの変更を反映
589             this.tabinfo.FilterAll();
590             this.tabinfo.DistributePosts();
591             this.tabinfo.SubmitUpdate();
592
593             // 期待する動作:
594             //   [statusId: 100] は MyTab1 から取り除かれる
595             //   [statusId: 200] は Recent から MyTab1 にコピーされ、マークが付与される
596
597             // 変更後の振り分け状態
598             Assert.Equal(new[] { 100L, 200L, 300L }, homeTab.StatusIds, AnyOrderComparer<long>.Instance);
599             Assert.Equal(new[] { 200L }, myTab1.StatusIds);
600
601             // [statusId: 200] は IsMark が true の状態になる
602             Assert.True(this.tabinfo[200L].IsMark);
603         }
604
605         [Fact]
606         public void FilterAll_MoveFilterTest()
607         {
608             var homeTab = this.tabinfo.GetTabByType(MyCommon.TabUsageType.Home);
609
610             var myTab1 = new FilterTabModel("MyTab1");
611             this.tabinfo.AddTab(myTab1);
612
613             var filter = new PostFilterRule
614             {
615                 FilterName = "aaa",
616
617                 // マッチしたら移動
618                 MoveMatches = true,
619             };
620             myTab1.AddFilter(filter);
621             myTab1.FilterModified = false;
622
623             this.tabinfo.AddPost(new PostClass { StatusId = 100L, ScreenName = "aaa" });
624             this.tabinfo.AddPost(new PostClass { StatusId = 200L, ScreenName = "bbb" });
625             this.tabinfo.AddPost(new PostClass { StatusId = 300L, ScreenName = "ccc" });
626             this.tabinfo.DistributePosts();
627             this.tabinfo.SubmitUpdate();
628
629             // この時点での振り分け状態
630             Assert.Equal(new[] { 200L, 300L }, homeTab.StatusIds, AnyOrderComparer<long>.Instance);
631             Assert.Equal(new[] { 100L }, myTab1.StatusIds);
632
633             // フィルタを変更する
634             filter.FilterName = "bbb";
635
636             // フィルタの変更を反映
637             this.tabinfo.FilterAll();
638             this.tabinfo.DistributePosts();
639             this.tabinfo.SubmitUpdate();
640
641             // 期待する動作:
642             //   [statusId: 100] は MyTab1 から取り除かれて Recent に戻される
643             //   [statusId: 200] は Recent から MyTab1 に移動される
644
645             // 変更後の振り分け状態
646             Assert.Equal(new[] { 100L, 300L }, homeTab.StatusIds, AnyOrderComparer<long>.Instance);
647             Assert.Equal(new[] { 200L }, myTab1.StatusIds);
648         }
649
650         [Fact]
651         public void FilterAll_MoveFilterTest2()
652         {
653             var homeTab = this.tabinfo.GetTabByType(MyCommon.TabUsageType.Home);
654
655             var myTab1 = new FilterTabModel("MyTab1");
656             var myTab2 = new FilterTabModel("MyTab2");
657             this.tabinfo.AddTab(myTab1);
658             this.tabinfo.AddTab(myTab2);
659
660             var filter1 = new PostFilterRule
661             {
662                 FilterName = "aaa",
663
664                 // マッチしたら移動
665                 MoveMatches = true,
666             };
667             myTab1.AddFilter(filter1);
668             myTab1.FilterModified = false;
669
670             var filter2 = new PostFilterRule
671             {
672                 FilterName = "bbb",
673
674                 // マッチしたら移動
675                 MoveMatches = true,
676             };
677             myTab2.AddFilter(filter2);
678             myTab2.FilterModified = false;
679
680             this.tabinfo.AddPost(new PostClass { StatusId = 100L, ScreenName = "aaa" });
681             this.tabinfo.AddPost(new PostClass { StatusId = 200L, ScreenName = "bbb" });
682             this.tabinfo.AddPost(new PostClass { StatusId = 300L, ScreenName = "ccc" });
683             this.tabinfo.DistributePosts();
684             this.tabinfo.SubmitUpdate();
685
686             // この時点での振り分け状態
687             Assert.Equal(new[] { 300L }, homeTab.StatusIds);
688             Assert.Equal(new[] { 100L }, myTab1.StatusIds);
689             Assert.Equal(new[] { 200L }, myTab2.StatusIds);
690
691             // MyTab1 のフィルタを変更する
692             filter1.FilterName = "bbb";
693
694             // MyTab2 のフィルタを変更する
695             filter2.FilterName = "ccc";
696
697             // フィルタの変更を反映
698             this.tabinfo.FilterAll();
699             this.tabinfo.DistributePosts();
700             this.tabinfo.SubmitUpdate();
701
702             // 期待する動作:
703             //   [statusId: 100] は MyTab1 から取り除かれて Recent に戻される
704             //   [statusId: 200] は MyTab1 に移動される
705             //   [statusId: 200] は MyTab2 から取り除かれるが MyTab1 に移動されているので Recent には戻さない
706             //   [statusId: 300] は Recent から MyTab2 に移動される
707
708             // 変更後の振り分け状態
709             Assert.Equal(new[] { 100L }, homeTab.StatusIds);
710             Assert.Equal(new[] { 200L }, myTab1.StatusIds);
711             Assert.Equal(new[] { 300L }, myTab2.StatusIds);
712         }
713
714         [Fact]
715         public void FilterAll_ExcludeReplyFilterTest()
716         {
717             var homeTab = this.tabinfo.GetTabByType<HomeTabModel>();
718             var replyTab = this.tabinfo.GetTabByType<MentionsTabModel>();
719
720             var filter = new PostFilterRule
721             {
722                 // @aaa からのリプライは Reply タブに振り分けない
723                 ExFilterName = "aaa",
724             };
725             replyTab.AddFilter(filter);
726             replyTab.FilterModified = false;
727
728             this.tabinfo.AddPost(new PostClass { StatusId = 100L, ScreenName = "aaa", IsReply = true });
729             this.tabinfo.AddPost(new PostClass { StatusId = 200L, ScreenName = "bbb", IsReply = true });
730             this.tabinfo.AddPost(new PostClass { StatusId = 300L, ScreenName = "ccc", IsReply = true });
731             this.tabinfo.DistributePosts();
732             this.tabinfo.SubmitUpdate();
733
734             // この時点での振り分け状態
735             Assert.Equal(new[] { 100L, 200L, 300L }, homeTab.StatusIds, AnyOrderComparer<long>.Instance);
736             Assert.Equal(new[] { 200L, 300L }, replyTab.StatusIds, AnyOrderComparer<long>.Instance);
737
738             // [statusId: 100] は IsExcludeReply が true の状態になっている
739             Assert.True(this.tabinfo[100L].IsExcludeReply);
740
741             // Reply のフィルタを変更する
742             filter.ExFilterName = "bbb";
743
744             // フィルタの変更を反映
745             this.tabinfo.FilterAll();
746             this.tabinfo.DistributePosts();
747             this.tabinfo.SubmitUpdate();
748
749             // 期待する動作:
750             //   [statusId: 100] は Reply にコピーされ、IsExcludeReply が false になる
751             //   [statusId: 200] は Reply から取り除かれ、IsExcludeReply が true になる
752
753             // 変更後の振り分け状態
754             Assert.Equal(new[] { 100L, 200L, 300L }, homeTab.StatusIds, AnyOrderComparer<long>.Instance);
755             Assert.Equal(new[] { 100L, 300L }, replyTab.StatusIds, AnyOrderComparer<long>.Instance);
756
757             // [statusId: 100] は IsExcludeReply が false の状態になる
758             Assert.False(this.tabinfo[100L].IsExcludeReply);
759
760             // [statusId: 200] は IsExcludeReply が true の状態になる
761             Assert.True(this.tabinfo[200L].IsExcludeReply);
762         }
763
764         class TestPostFilterRule : PostFilterRule
765         {
766             public static PostFilterRule Create(Func<PostClass, MyCommon.HITRESULT> filterDelegate)
767             {
768                 return new TestPostFilterRule
769                 {
770                     FilterDelegate = filterDelegate,
771                     IsDirty = false,
772                 };
773             }
774         }
775     }
776 }