OSDN Git Service

不要なExtractTimelineTweetsメソッドの呼び出しを削除
[opentween/open-tween.git] / OpenTween.Tests / TwitterTest.cs
1 // OpenTween - Client of Twitter
2 // Copyright (c) 2013 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.Text;
26 using System.Text.RegularExpressions;
27 using OpenTween.Api;
28 using OpenTween.Api.DataModel;
29 using OpenTween.Models;
30 using OpenTween.Setting;
31 using Xunit;
32 using Xunit.Extensions;
33
34 namespace OpenTween
35 {
36     public class TwitterTest
37     {
38         [Theory]
39         [InlineData("https://twitter.com/twitterapi/status/22634515958",
40             new[] { "22634515958" })]
41         [InlineData("""<a target="_self" href="https://t.co/aaaaaaaa" title="https://twitter.com/twitterapi/status/22634515958">twitter.com/twitterapi/stat…</a>""",
42             new[] { "22634515958" })]
43         [InlineData("""<a target="_self" href="https://t.co/bU3oR95KIy" title="https://twitter.com/haru067/status/224782458816692224">https://t.co/bU3oR95KIy</a>""" +
44             """<a target="_self" href="https://t.co/bbbbbbbb" title="https://twitter.com/karno/status/311081657790771200">https://t.co/bbbbbbbb</a>""",
45             new[] { "224782458816692224", "311081657790771200" })]
46         [InlineData("https://mobile.twitter.com/muji_net/status/21984934471",
47             new[] { "21984934471" })]
48         [InlineData("https://twitter.com/imgazyobuzi/status/293333871171354624/photo/1",
49             new[] { "293333871171354624" })]
50         public void StatusUrlRegexTest(string url, string[] expected)
51         {
52             var results = Twitter.StatusUrlRegex.Matches(url).Cast<Match>()
53                 .Select(x => x.Groups["StatusId"].Value).ToArray();
54
55             Assert.Equal(expected, results);
56         }
57
58         [Theory]
59         [InlineData("https://twitter.com/twitterapi/status/22634515958", true)]
60         [InlineData("http://twitter.com/twitterapi/status/22634515958", true)]
61         [InlineData("https://mobile.twitter.com/twitterapi/status/22634515958", true)]
62         [InlineData("http://mobile.twitter.com/twitterapi/status/22634515958", true)]
63         [InlineData("https://twitter.com/i/web/status/22634515958", false)]
64         [InlineData("https://twitter.com/imgazyobuzi/status/293333871171354624/photo/1", false)]
65         [InlineData("https://pic.twitter.com/gbxdb2Oj", false)]
66         [InlineData("https://twitter.com/messages/compose?recipient_id=514241801", true)]
67         [InlineData("http://twitter.com/messages/compose?recipient_id=514241801", true)]
68         [InlineData("https://twitter.com/messages/compose?recipient_id=514241801&text=%E3%81%BB%E3%81%92", true)]
69         public void AttachmentUrlRegexTest(string url, bool isMatch)
70             => Assert.Equal(isMatch, Twitter.AttachmentUrlRegex.IsMatch(url));
71
72         [Theory]
73         [InlineData("http://favstar.fm/users/twitterapi/status/22634515958", new[] { "22634515958" })]
74         [InlineData("http://ja.favstar.fm/users/twitterapi/status/22634515958", new[] { "22634515958" })]
75         [InlineData("http://favstar.fm/t/22634515958", new[] { "22634515958" })]
76         [InlineData("http://aclog.koba789.com/i/312485321239564288", new[] { "312485321239564288" })]
77         [InlineData("http://frtrt.net/solo_status.php?status=263483634307198977", new[] { "263483634307198977" })]
78         public void ThirdPartyStatusUrlRegexTest(string url, string[] expected)
79         {
80             var results = Twitter.ThirdPartyStatusUrlRegex.Matches(url).Cast<Match>()
81                 .Select(x => x.Groups["StatusId"].Value).ToArray();
82
83             Assert.Equal(expected, results);
84         }
85
86         [Fact]
87         public void FindTopOfReplyChainTest()
88         {
89             var posts = new Dictionary<PostId, PostClass>
90             {
91                 [new TwitterStatusId("950")] = new PostClass { StatusId = new TwitterStatusId("950"), InReplyToStatusId = null }, // このツイートが末端
92                 [new TwitterStatusId("987")] = new PostClass { StatusId = new TwitterStatusId("987"), InReplyToStatusId = new TwitterStatusId("950") },
93                 [new TwitterStatusId("999")] = new PostClass { StatusId = new TwitterStatusId("999"), InReplyToStatusId = new TwitterStatusId("987") },
94                 [new TwitterStatusId("1000")] = new PostClass { StatusId = new TwitterStatusId("1000"), InReplyToStatusId = new TwitterStatusId("999") },
95             };
96             Assert.Equal(new TwitterStatusId("950"), Twitter.FindTopOfReplyChain(posts, new TwitterStatusId("1000")).StatusId);
97             Assert.Equal(new TwitterStatusId("950"), Twitter.FindTopOfReplyChain(posts, new TwitterStatusId("950")).StatusId);
98             Assert.Throws<ArgumentException>(() => Twitter.FindTopOfReplyChain(posts, new TwitterStatusId("500")));
99
100             posts = new Dictionary<PostId, PostClass>
101             {
102                 // new TwitterStatusId("1200") は posts の中に存在しない
103                 [new TwitterStatusId("1210")] = new PostClass { StatusId = new TwitterStatusId("1210"), InReplyToStatusId = new TwitterStatusId("1200") },
104                 [new TwitterStatusId("1220")] = new PostClass { StatusId = new TwitterStatusId("1220"), InReplyToStatusId = new TwitterStatusId("1210") },
105                 [new TwitterStatusId("1230")] = new PostClass { StatusId = new TwitterStatusId("1230"), InReplyToStatusId = new TwitterStatusId("1220") },
106             };
107             Assert.Equal(new TwitterStatusId("1210"), Twitter.FindTopOfReplyChain(posts, new TwitterStatusId("1230")).StatusId);
108             Assert.Equal(new TwitterStatusId("1210"), Twitter.FindTopOfReplyChain(posts, new TwitterStatusId("1210")).StatusId);
109         }
110
111         [Fact]
112         public void GetApiResultCount_DefaultTest()
113         {
114             var oldInstance = SettingManagerTest.Common;
115             SettingManagerTest.Common = new SettingCommon();
116
117             var timeline = SettingManager.Instance.Common.CountApi;
118             var reply = SettingManager.Instance.Common.CountApiReply;
119             var more = SettingManager.Instance.Common.MoreCountApi;
120             var startup = SettingManager.Instance.Common.FirstCountApi;
121             var favorite = SettingManager.Instance.Common.FavoritesCountApi;
122             var list = SettingManager.Instance.Common.ListCountApi;
123             var search = SettingManager.Instance.Common.SearchCountApi;
124             var usertl = SettingManager.Instance.Common.UserTimelineCountApi;
125
126             // デフォルト値チェック
127             Assert.False(SettingManager.Instance.Common.UseAdditionalCount);
128             Assert.Equal(60, timeline);
129             Assert.Equal(40, reply);
130             Assert.Equal(200, more);
131             Assert.Equal(100, startup);
132             Assert.Equal(40, favorite);
133             Assert.Equal(100, list);
134             Assert.Equal(100, search);
135             Assert.Equal(20, usertl);
136
137             // Timeline,Reply
138             Assert.Equal(timeline, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.Timeline, false, false));
139             Assert.Equal(reply, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.Reply, false, false));
140
141             // その他はTimelineと同値になる
142             Assert.Equal(timeline, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.Favorites, false, false));
143             Assert.Equal(timeline, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.List, false, false));
144             Assert.Equal(timeline, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.PublicSearch, false, false));
145             Assert.Equal(timeline, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.UserTimeline, false, false));
146
147             SettingManagerTest.Common = oldInstance;
148         }
149
150         [Fact]
151         public void GetApiResultCount_AdditionalCountTest()
152         {
153             var oldInstance = SettingManagerTest.Common;
154             SettingManagerTest.Common = new SettingCommon();
155
156             var timeline = SettingManager.Instance.Common.CountApi;
157             var reply = SettingManager.Instance.Common.CountApiReply;
158             var more = SettingManager.Instance.Common.MoreCountApi;
159             var startup = SettingManager.Instance.Common.FirstCountApi;
160             var favorite = SettingManager.Instance.Common.FavoritesCountApi;
161             var list = SettingManager.Instance.Common.ListCountApi;
162             var search = SettingManager.Instance.Common.SearchCountApi;
163             var usertl = SettingManager.Instance.Common.UserTimelineCountApi;
164
165             SettingManager.Instance.Common.UseAdditionalCount = true;
166
167             // Timeline
168             Assert.Equal(timeline, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.Timeline, false, false));
169             Assert.Equal(100, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.Timeline, true, false)); // 100 件が上限
170             Assert.Equal(startup, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.Timeline, false, true));
171
172             // Reply
173             Assert.Equal(reply, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.Reply, false, false));
174             Assert.Equal(more, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.Reply, true, false));
175             Assert.Equal(reply, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.Reply, false, true));  // Replyの値が使われる
176
177             // Favorites
178             Assert.Equal(favorite, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.Favorites, false, false));
179             Assert.Equal(favorite, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.Favorites, true, false));
180             Assert.Equal(favorite, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.Favorites, false, true));
181
182             SettingManager.Instance.Common.FavoritesCountApi = 0;
183
184             Assert.Equal(timeline, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.Favorites, false, false));
185             Assert.Equal(more, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.Favorites, true, false));
186             Assert.Equal(startup, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.Favorites, false, true));
187
188             // List
189             Assert.Equal(list, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.List, false, false));
190             Assert.Equal(list, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.List, true, false));
191             Assert.Equal(list, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.List, false, true));
192
193             SettingManager.Instance.Common.ListCountApi = 0;
194
195             Assert.Equal(timeline, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.List, false, false));
196             Assert.Equal(more, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.List, true, false));
197             Assert.Equal(startup, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.List, false, true));
198
199             // PublicSearch
200             Assert.Equal(search, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.PublicSearch, false, false));
201             Assert.Equal(search, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.PublicSearch, true, false));
202             Assert.Equal(search, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.PublicSearch, false, true));
203
204             SettingManager.Instance.Common.SearchCountApi = 0;
205
206             Assert.Equal(timeline, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.PublicSearch, false, false));
207             Assert.Equal(search, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.PublicSearch, true, false));  // MoreCountApiの値がPublicSearchの最大値に制限される
208             Assert.Equal(startup, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.PublicSearch, false, true));
209
210             // UserTimeline
211             Assert.Equal(usertl, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.UserTimeline, false, false));
212             Assert.Equal(usertl, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.UserTimeline, true, false));
213             Assert.Equal(usertl, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.UserTimeline, false, true));
214
215             SettingManager.Instance.Common.UserTimelineCountApi = 0;
216
217             Assert.Equal(timeline, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.UserTimeline, false, false));
218             Assert.Equal(more, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.UserTimeline, true, false));
219             Assert.Equal(startup, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.UserTimeline, false, true));
220
221             SettingManagerTest.Common = oldInstance;
222         }
223
224         [Fact]
225         public void GetTextLengthRemain_Test()
226         {
227             using var twitterApi = new TwitterApi();
228             using var twitter = new Twitter(twitterApi);
229
230             Assert.Equal(280, twitter.GetTextLengthRemain(""));
231             Assert.Equal(272, twitter.GetTextLengthRemain("hogehoge"));
232         }
233
234         [Fact]
235         public void GetTextLengthRemain_DirectMessageTest()
236         {
237             using var twitterApi = new TwitterApi();
238             using var twitter = new Twitter(twitterApi);
239
240             // 2015年8月から DM の文字数上限が 10,000 文字に変更された
241             // https://twittercommunity.com/t/41348
242             twitter.Configuration.DmTextCharacterLimit = 10000;
243
244             Assert.Equal(10000, twitter.GetTextLengthRemain("D twitter "));
245             Assert.Equal(9992, twitter.GetTextLengthRemain("D twitter hogehoge"));
246
247             // t.co に短縮される分の文字数を考慮
248             twitter.Configuration.ShortUrlLength = 20;
249             Assert.Equal(9971, twitter.GetTextLengthRemain("D twitter hogehoge http://example.com/"));
250
251             twitter.Configuration.ShortUrlLengthHttps = 21;
252             Assert.Equal(9970, twitter.GetTextLengthRemain("D twitter hogehoge https://example.com/"));
253         }
254
255         [Fact]
256         public void GetTextLengthRemain_UrlTest()
257         {
258             using var twitterApi = new TwitterApi();
259             using var twitter = new Twitter(twitterApi);
260
261             // t.co に短縮される分の文字数を考慮
262             twitter.TextConfiguration.TransformedURLLength = 20;
263             Assert.Equal(260, twitter.GetTextLengthRemain("http://example.com/"));
264             Assert.Equal(260, twitter.GetTextLengthRemain("http://example.com/hogehoge"));
265             Assert.Equal(251, twitter.GetTextLengthRemain("hogehoge http://example.com/"));
266
267             Assert.Equal(260, twitter.GetTextLengthRemain("https://example.com/"));
268             Assert.Equal(260, twitter.GetTextLengthRemain("https://example.com/hogehoge"));
269             Assert.Equal(251, twitter.GetTextLengthRemain("hogehoge https://example.com/"));
270         }
271
272         [Fact]
273         public void GetTextLengthRemain_UrlWithoutSchemeTest()
274         {
275             using var twitterApi = new TwitterApi();
276             using var twitter = new Twitter(twitterApi);
277
278             // t.co に短縮される分の文字数を考慮
279             twitter.TextConfiguration.TransformedURLLength = 20;
280             Assert.Equal(260, twitter.GetTextLengthRemain("example.com"));
281             Assert.Equal(260, twitter.GetTextLengthRemain("example.com/hogehoge"));
282             Assert.Equal(251, twitter.GetTextLengthRemain("hogehoge example.com"));
283
284             // スキーム (http://) を省略かつ末尾が ccTLD の場合は t.co に短縮されない
285             Assert.Equal(270, twitter.GetTextLengthRemain("example.jp"));
286             // ただし、末尾にパスが続く場合は t.co に短縮される
287             Assert.Equal(260, twitter.GetTextLengthRemain("example.jp/hogehoge"));
288         }
289
290         [Fact]
291         public void GetTextLengthRemain_SurrogatePairTest()
292         {
293             using var twitterApi = new TwitterApi();
294             using var twitter = new Twitter(twitterApi);
295
296             Assert.Equal(278, twitter.GetTextLengthRemain("🍣"));
297             Assert.Equal(267, twitter.GetTextLengthRemain("🔥🐔🔥 焼き鳥"));
298         }
299
300         [Fact]
301         public void GetTextLengthRemain_EmojiTest()
302         {
303             using var twitterApi = new TwitterApi();
304             using var twitter = new Twitter(twitterApi);
305
306             // 絵文字の文字数カウントの仕様変更に対するテストケース
307             // https://twittercommunity.com/t/114607
308
309             Assert.Equal(279, twitter.GetTextLengthRemain("©")); // 基本多言語面の絵文字
310             Assert.Equal(277, twitter.GetTextLengthRemain("©\uFE0E")); // 異字体セレクタ付き (text style)
311             Assert.Equal(279, twitter.GetTextLengthRemain("©\uFE0F")); // 異字体セレクタ付き (emoji style)
312             Assert.Equal(278, twitter.GetTextLengthRemain("🍣")); // 拡張面の絵文字
313             Assert.Equal(279, twitter.GetTextLengthRemain("#⃣")); // 合字で表現される絵文字
314             Assert.Equal(278, twitter.GetTextLengthRemain("👦\U0001F3FF")); // Emoji modifier 付きの絵文字
315             Assert.Equal(278, twitter.GetTextLengthRemain("\U0001F3FF")); // Emoji modifier 単体
316             Assert.Equal(278, twitter.GetTextLengthRemain("👨\u200D🎨")); // ZWJ で結合された絵文字
317             Assert.Equal(278, twitter.GetTextLengthRemain("🏃\u200D♀\uFE0F")); // ZWJ と異字体セレクタを含む絵文字
318         }
319
320         [Fact]
321         public void GetTextLengthRemain_BrokenSurrogateTest()
322         {
323             using var twitterApi = new TwitterApi();
324             using var twitter = new Twitter(twitterApi);
325
326             // 投稿欄に IME から絵文字を入力すると HighSurrogate のみ入力された状態で TextChanged イベントが呼ばれることがある
327             Assert.Equal(278, twitter.GetTextLengthRemain("\ud83d"));
328             Assert.Equal(9999, twitter.GetTextLengthRemain("D twitter \ud83d"));
329         }
330
331         [Theory]
332         [InlineData("https://pbs.twimg.com/profile_images/00000/foo_normal.jpg", "normal", "https://pbs.twimg.com/profile_images/00000/foo_normal.jpg")]
333         [InlineData("https://pbs.twimg.com/profile_images/00000/foo_normal.jpg", "bigger", "https://pbs.twimg.com/profile_images/00000/foo_bigger.jpg")]
334         [InlineData("https://pbs.twimg.com/profile_images/00000/foo_normal.jpg", "mini", "https://pbs.twimg.com/profile_images/00000/foo_mini.jpg")]
335         [InlineData("https://pbs.twimg.com/profile_images/00000/foo_normal.jpg", "original", "https://pbs.twimg.com/profile_images/00000/foo.jpg")]
336         [InlineData("https://pbs.twimg.com/profile_images/00000/foo_normal_bar_normal.jpg", "original", "https://pbs.twimg.com/profile_images/00000/foo_normal_bar.jpg")]
337         public void CreateProfileImageUrl_Test(string normalUrl, string size, string expected)
338             => Assert.Equal(expected, Twitter.CreateProfileImageUrl(normalUrl, size));
339
340         [Fact]
341         public void CreateProfileImageUrl_InvalidSizeTest()
342             => Assert.Throws<ArgumentException>(() => Twitter.CreateProfileImageUrl("https://pbs.twimg.com/profile_images/00000/foo_normal.jpg", "INVALID"));
343
344         [Theory]
345         [InlineData(24, "mini")]
346         [InlineData(25, "normal")]
347         [InlineData(48, "normal")]
348         [InlineData(49, "bigger")]
349         [InlineData(73, "bigger")]
350         [InlineData(74, "original")]
351         public void DecideProfileImageSize_Test(int sizePx, string expected)
352             => Assert.Equal(expected, Twitter.DecideProfileImageSize(sizePx));
353     }
354 }