OSDN Git Service

完了状態のTaskを生成するために Task.CompletedTask を使用する
[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.DataModel;
28 using OpenTween.Models;
29 using OpenTween.Setting;
30 using Xunit;
31 using Xunit.Extensions;
32
33 namespace OpenTween
34 {
35     public class TwitterTest
36     {
37         [Theory]
38         [InlineData("https://twitter.com/twitterapi/status/22634515958",
39             new[] { "22634515958" })]
40         [InlineData("<a target=\"_self\" href=\"https://t.co/aaaaaaaa\" title=\"https://twitter.com/twitterapi/status/22634515958\">twitter.com/twitterapi/stat…</a>",
41             new[] { "22634515958" })]
42         [InlineData("<a target=\"_self\" href=\"https://t.co/bU3oR95KIy\" title=\"https://twitter.com/haru067/status/224782458816692224\">https://t.co/bU3oR95KIy</a>" +
43             "<a target=\"_self\" href=\"https://t.co/bbbbbbbb\" title=\"https://twitter.com/karno/status/311081657790771200\">https://t.co/bbbbbbbb</a>",
44             new[] { "224782458816692224", "311081657790771200" })]
45         [InlineData("https://mobile.twitter.com/muji_net/status/21984934471",
46             new[] { "21984934471" })]
47         [InlineData("https://twitter.com/imgazyobuzi/status/293333871171354624/photo/1",
48             new[] { "293333871171354624" })]
49         public void StatusUrlRegexTest(string url, string[] expected)
50         {
51             var results = Twitter.StatusUrlRegex.Matches(url).Cast<Match>()
52                 .Select(x => x.Groups["StatusId"].Value).ToArray();
53
54             Assert.Equal(expected, results);
55         }
56
57         [Theory]
58         [InlineData("https://twitter.com/twitterapi/status/22634515958", true)]
59         [InlineData("http://twitter.com/twitterapi/status/22634515958", true)]
60         [InlineData("https://mobile.twitter.com/twitterapi/status/22634515958", true)]
61         [InlineData("http://mobile.twitter.com/twitterapi/status/22634515958", true)]
62         [InlineData("https://twitter.com/i/web/status/22634515958", false)]
63         [InlineData("https://twitter.com/imgazyobuzi/status/293333871171354624/photo/1", false)]
64         [InlineData("https://pic.twitter.com/gbxdb2Oj", false)]
65         [InlineData("https://twitter.com/messages/compose?recipient_id=514241801", true)]
66         [InlineData("http://twitter.com/messages/compose?recipient_id=514241801", true)]
67         [InlineData("https://twitter.com/messages/compose?recipient_id=514241801&text=%E3%81%BB%E3%81%92", true)]
68         public void AttachmentUrlRegexTest(string url, bool isMatch)
69         {
70             Assert.Equal(isMatch, Twitter.AttachmentUrlRegex.IsMatch(url));
71         }
72
73         [Theory]
74         [InlineData("http://favstar.fm/users/twitterapi/status/22634515958", new[] { "22634515958" })]
75         [InlineData("http://ja.favstar.fm/users/twitterapi/status/22634515958", new[] { "22634515958" })]
76         [InlineData("http://favstar.fm/t/22634515958", new[] { "22634515958" })]
77         [InlineData("http://aclog.koba789.com/i/312485321239564288", new[] { "312485321239564288" })]
78         [InlineData("http://frtrt.net/solo_status.php?status=263483634307198977", new[] { "263483634307198977" })]
79         public void ThirdPartyStatusUrlRegexTest(string url, string[] expected)
80         {
81             var results = Twitter.ThirdPartyStatusUrlRegex.Matches(url).Cast<Match>()
82                 .Select(x => x.Groups["StatusId"].Value).ToArray();
83
84             Assert.Equal(expected, results);
85         }
86
87         [Fact]
88         public void FindTopOfReplyChainTest()
89         {
90             var posts = new Dictionary<long, PostClass>
91             {
92                 [950L] = new PostClass { StatusId = 950L, InReplyToStatusId = null }, // このツイートが末端
93                 [987L] = new PostClass { StatusId = 987L, InReplyToStatusId = 950L },
94                 [999L] = new PostClass { StatusId = 999L, InReplyToStatusId = 987L },
95                 [1000L] = new PostClass { StatusId = 1000L, InReplyToStatusId = 999L },
96             };
97             Assert.Equal(950L, Twitter.FindTopOfReplyChain(posts, 1000L).StatusId);
98             Assert.Equal(950L, Twitter.FindTopOfReplyChain(posts, 950L).StatusId);
99             Assert.Throws<ArgumentException>(() => Twitter.FindTopOfReplyChain(posts, 500L));
100
101             posts = new Dictionary<long, PostClass>
102             {
103                 // 1200L は posts の中に存在しない
104                 [1210L] = new PostClass { StatusId = 1210L, InReplyToStatusId = 1200L },
105                 [1220L] = new PostClass { StatusId = 1220L, InReplyToStatusId = 1210L },
106                 [1230L] = new PostClass { StatusId = 1230L, InReplyToStatusId = 1220L },
107             };
108             Assert.Equal(1210L, Twitter.FindTopOfReplyChain(posts, 1230L).StatusId);
109             Assert.Equal(1210L, Twitter.FindTopOfReplyChain(posts, 1210L).StatusId);
110         }
111
112         [Fact]
113         public void CreateAccessibleText_MediaAltTest()
114         {
115             var text = "https://t.co/hoge";
116             var entities = new TwitterEntities
117             {
118                 Media = new[]
119                 {
120                     new TwitterEntityMedia
121                     {
122                         Indices = new[] { 0, 17 },
123                         Url = "https://t.co/hoge",
124                         DisplayUrl = "pic.twitter.com/hoge",
125                         ExpandedUrl = "https://twitter.com/hoge/status/1234567890/photo/1",
126                         AltText = "代替テキスト",
127                     },
128                 },
129             };
130
131             var expectedText = string.Format(Properties.Resources.ImageAltText, "代替テキスト");
132
133             Assert.Equal(expectedText, Twitter.CreateAccessibleText(text, entities, quotedStatus: null, quotedStatusLink: null));
134         }
135
136         [Fact]
137         public void CreateAccessibleText_MediaNoAltTest()
138         {
139             var text = "https://t.co/hoge";
140             var entities = new TwitterEntities
141             {
142                 Media = new[]
143                 {
144                     new TwitterEntityMedia
145                     {
146                         Indices = new[] { 0, 17 },
147                         Url = "https://t.co/hoge",
148                         DisplayUrl = "pic.twitter.com/hoge",
149                         ExpandedUrl = "https://twitter.com/hoge/status/1234567890/photo/1",
150                         AltText = null,
151                     },
152                 },
153             };
154
155             var expectedText = "pic.twitter.com/hoge";
156
157             Assert.Equal(expectedText, Twitter.CreateAccessibleText(text, entities, quotedStatus: null, quotedStatusLink: null));
158         }
159
160         [Fact]
161         public void CreateAccessibleText_QuotedUrlTest()
162         {
163             var text = "https://t.co/hoge";
164             var entities = new TwitterEntities
165             {
166                 Urls = new[]
167                 {
168                     new TwitterEntityUrl
169                     {
170                         Indices = new[] { 0, 17 },
171                         Url = "https://t.co/hoge",
172                         DisplayUrl = "twitter.com/hoge/status/1…",
173                         ExpandedUrl = "https://twitter.com/hoge/status/1234567890",
174                     },
175                 },
176             };
177             var quotedStatus = new TwitterStatus
178             {
179                 Id = 1234567890L,
180                 IdStr = "1234567890",
181                 User = new TwitterUser
182                 {
183                     Id = 1111,
184                     IdStr = "1111",
185                     ScreenName = "foo",
186                 },
187                 FullText = "test",
188             };
189
190             var expectedText = string.Format(Properties.Resources.QuoteStatus_AccessibleText, "foo", "test");
191
192             Assert.Equal(expectedText, Twitter.CreateAccessibleText(text, entities, quotedStatus, quotedStatusLink: null));
193         }
194
195         [Fact]
196         public void CreateAccessibleText_QuotedUrlWithPermelinkTest()
197         {
198             var text = "hoge";
199             var entities = new TwitterEntities();
200             var quotedStatus = new TwitterStatus
201             {
202                 Id = 1234567890L,
203                 IdStr = "1234567890",
204                 User = new TwitterUser
205                 {
206                     Id = 1111,
207                     IdStr = "1111",
208                     ScreenName = "foo",
209                 },
210                 FullText = "test",
211             };
212             var quotedStatusLink = new TwitterQuotedStatusPermalink
213             {
214                 Url = "https://t.co/hoge",
215                 Display = "twitter.com/hoge/status/1…",
216                 Expanded = "https://twitter.com/hoge/status/1234567890",
217             };
218
219             var expectedText = "hoge " + string.Format(Properties.Resources.QuoteStatus_AccessibleText, "foo", "test");
220
221             Assert.Equal(expectedText, Twitter.CreateAccessibleText(text, entities, quotedStatus, quotedStatusLink));
222         }
223
224         [Fact]
225         public void CreateAccessibleText_QuotedUrlNoReferenceTest()
226         {
227             var text = "https://t.co/hoge";
228             var entities = new TwitterEntities
229             {
230                 Urls = new[]
231                 {
232                     new TwitterEntityUrl
233                     {
234                         Indices = new[] { 0, 17 },
235                         Url = "https://t.co/hoge",
236                         DisplayUrl = "twitter.com/hoge/status/1…",
237                         ExpandedUrl = "https://twitter.com/hoge/status/1234567890",
238                     },
239                 },
240             };
241             var quotedStatus = (TwitterStatus)null;
242
243             var expectedText = "twitter.com/hoge/status/1…";
244
245             Assert.Equal(expectedText, Twitter.CreateAccessibleText(text, entities, quotedStatus, quotedStatusLink: null));
246         }
247
248         [Fact]
249         public void CreateHtmlAnchor_Test()
250         {
251             var text = "@twitterapi #BreakingMyTwitter https://t.co/mIJcSoVSK3";
252             var entities = new TwitterEntities
253             {
254                 UserMentions = new[]
255                 {
256                     new TwitterEntityMention { Indices = new[] { 0, 11 }, ScreenName = "twitterapi" },
257                 },
258                 Hashtags = new[]
259                 {
260                     new TwitterEntityHashtag { Indices = new[] { 12, 30 }, Text = "BreakingMyTwitter" },
261                 },
262                 Urls = new[]
263                 {
264                     new TwitterEntityUrl
265                     {
266                         Indices = new[] { 31, 54 },
267                         Url ="https://t.co/mIJcSoVSK3",
268                         DisplayUrl = "apps-of-a-feather.com",
269                         ExpandedUrl = "http://apps-of-a-feather.com/",
270                     },
271                 },
272             };
273
274             var expectedHtml = @"<a class=""mention"" href=""https://twitter.com/twitterapi"">@twitterapi</a>"
275                 + @" <a class=""hashtag"" href=""https://twitter.com/search?q=%23BreakingMyTwitter"">#BreakingMyTwitter</a>"
276                 + @" <a href=""https://t.co/mIJcSoVSK3"" title=""https://t.co/mIJcSoVSK3"">apps-of-a-feather.com</a>";
277
278             Assert.Equal(expectedHtml, Twitter.CreateHtmlAnchor(text, entities, quotedStatusLink: null));
279         }
280
281         [Fact]
282         public void CreateHtmlAnchor_NicovideoTest()
283         {
284             var text = "sm9";
285             var entities = new TwitterEntities();
286
287             var expectedHtml = @"<a href=""http://www.nicovideo.jp/watch/sm9"">sm9</a>";
288
289             Assert.Equal(expectedHtml, Twitter.CreateHtmlAnchor(text, entities, quotedStatusLink: null));
290         }
291
292         [Fact]
293         public void CreateHtmlAnchor_QuotedUrlWithPermelinkTest()
294         {
295             var text = "hoge";
296             var entities = new TwitterEntities();
297             var quotedStatus = new TwitterStatus
298             {
299                 Id = 1234567890L,
300                 IdStr = "1234567890",
301                 User = new TwitterUser
302                 {
303                     Id = 1111,
304                     IdStr = "1111",
305                     ScreenName = "foo",
306                 },
307                 FullText = "test",
308             };
309             var quotedStatusLink = new TwitterQuotedStatusPermalink
310             {
311                 Url = "https://t.co/hoge",
312                 Display = "twitter.com/hoge/status/1…",
313                 Expanded = "https://twitter.com/hoge/status/1234567890",
314             };
315
316             var expectedHtml = @"hoge"
317                 + @" <a href=""https://t.co/hoge"" title=""https://t.co/hoge"">twitter.com/hoge/status/1…</a>";
318
319             Assert.Equal(expectedHtml, Twitter.CreateHtmlAnchor(text, entities, quotedStatusLink));
320         }
321
322         [Fact]
323         public void ParseSource_Test()
324         {
325             var sourceHtml = "<a href=\"http://twitter.com\" rel=\"nofollow\">Twitter Web Client</a>";
326
327             var expected = ("Twitter Web Client", new Uri("http://twitter.com/"));
328             Assert.Equal(expected, Twitter.ParseSource(sourceHtml));
329         }
330
331         [Fact]
332         public void ParseSource_PlainTextTest()
333         {
334             var sourceHtml = "web";
335
336             var expected = ("web", (Uri)null);
337             Assert.Equal(expected, Twitter.ParseSource(sourceHtml));
338         }
339
340         [Fact]
341         public void ParseSource_RelativeUriTest()
342         {
343             // 参照: https://twitter.com/kim_upsilon/status/477796052049752064
344             var sourceHtml = "<a href=\"erased_45416\" rel=\"nofollow\">erased_45416</a>";
345
346             var expected = ("erased_45416", new Uri("https://twitter.com/erased_45416"));
347             Assert.Equal(expected, Twitter.ParseSource(sourceHtml));
348         }
349
350         [Fact]
351         public void ParseSource_EmptyTest()
352         {
353             // 参照: https://twitter.com/kim_upsilon/status/595156014032244738
354             var sourceHtml = "";
355
356             var expected = ("", (Uri)null);
357             Assert.Equal(expected, Twitter.ParseSource(sourceHtml));
358         }
359
360         [Fact]
361         public void ParseSource_NullTest()
362         {
363             string sourceHtml = null;
364
365             var expected = ("", (Uri)null);
366             Assert.Equal(expected, Twitter.ParseSource(sourceHtml));
367         }
368
369         [Fact]
370         public void ParseSource_UnescapeTest()
371         {
372             string sourceHtml = "<a href=\"http://example.com/?aaa=123&amp;bbb=456\" rel=\"nofollow\">&lt;&lt;hogehoge&gt;&gt;</a>";
373
374             var expected = ("<<hogehoge>>", new Uri("http://example.com/?aaa=123&bbb=456"));
375             Assert.Equal(expected, Twitter.ParseSource(sourceHtml));
376         }
377
378         [Fact]
379         public void ParseSource_UnescapeNoUriTest()
380         {
381             string sourceHtml = "&lt;&lt;hogehoge&gt;&gt;";
382
383             var expected = ("<<hogehoge>>", (Uri)null);
384             Assert.Equal(expected, Twitter.ParseSource(sourceHtml));
385         }
386
387         [Fact]
388         public void GetQuoteTweetStatusIds_EntityTest()
389         {
390             var entities = new[]
391             {
392                 new TwitterEntityUrl
393                 {
394                     Url = "https://t.co/3HXq0LrbJb",
395                     ExpandedUrl = "https://twitter.com/kim_upsilon/status/599261132361072640",
396                 },
397             };
398
399             var statusIds = Twitter.GetQuoteTweetStatusIds(entities, quotedStatusLink: null);
400             Assert.Equal(new[] { 599261132361072640L }, statusIds);
401         }
402
403         [Fact]
404         public void GetQuoteTweetStatusIds_QuotedStatusLinkTest()
405         {
406             var entities = new TwitterEntities();
407             var quotedStatusLink = new TwitterQuotedStatusPermalink
408             {
409                 Url = "https://t.co/3HXq0LrbJb",
410                 Expanded = "https://twitter.com/kim_upsilon/status/599261132361072640",
411             };
412
413             var statusIds = Twitter.GetQuoteTweetStatusIds(entities, quotedStatusLink);
414             Assert.Equal(new[] { 599261132361072640L }, statusIds);
415         }
416
417         [Fact]
418         public void GetQuoteTweetStatusIds_UrlStringTest()
419         {
420             var urls = new[]
421             {
422                 "https://twitter.com/kim_upsilon/status/599261132361072640",
423             };
424
425             var statusIds = Twitter.GetQuoteTweetStatusIds(urls);
426             Assert.Equal(new[] { 599261132361072640L }, statusIds);
427         }
428
429         [Fact]
430         public void GetQuoteTweetStatusIds_OverflowTest()
431         {
432             var urls = new[]
433             {
434                 // 符号付き 64 ビット整数の範囲を超える値
435                 "https://twitter.com/kim_upsilon/status/9999999999999999999",
436             };
437
438             var statusIds = Twitter.GetQuoteTweetStatusIds(urls);
439             Assert.Empty(statusIds);
440         }
441
442         [Fact]
443         public void GetApiResultCount_DefaultTest()
444         {
445             var oldInstance = SettingManagerTest.Common;
446             SettingManagerTest.Common = new SettingCommon();
447
448             var timeline = SettingManager.Common.CountApi;
449             var reply = SettingManager.Common.CountApiReply;
450             var more = SettingManager.Common.MoreCountApi;
451             var startup = SettingManager.Common.FirstCountApi;
452             var favorite = SettingManager.Common.FavoritesCountApi;
453             var list = SettingManager.Common.ListCountApi;
454             var search = SettingManager.Common.SearchCountApi;
455             var usertl = SettingManager.Common.UserTimelineCountApi;
456
457             // デフォルト値チェック
458             Assert.False(SettingManager.Common.UseAdditionalCount);
459             Assert.Equal(60, timeline);
460             Assert.Equal(40, reply);
461             Assert.Equal(200, more);
462             Assert.Equal(100, startup);
463             Assert.Equal(40, favorite);
464             Assert.Equal(100, list);
465             Assert.Equal(100, search);
466             Assert.Equal(20, usertl);
467
468             // Timeline,Reply
469             Assert.Equal(timeline, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.Timeline, false, false));
470             Assert.Equal(reply, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.Reply, false, false));
471
472             // その他はTimelineと同値になる
473             Assert.Equal(timeline, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.Favorites, false, false));
474             Assert.Equal(timeline, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.List, false, false));
475             Assert.Equal(timeline, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.PublicSearch, false, false));
476             Assert.Equal(timeline, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.UserTimeline, false, false));
477
478             SettingManagerTest.Common = oldInstance;
479         }
480
481         [Fact]
482         public void GetApiResultCount_AdditionalCountTest()
483         {
484             var oldInstance = SettingManagerTest.Common;
485             SettingManagerTest.Common = new SettingCommon();
486
487             var timeline = SettingManager.Common.CountApi;
488             var reply = SettingManager.Common.CountApiReply;
489             var more = SettingManager.Common.MoreCountApi;
490             var startup = SettingManager.Common.FirstCountApi;
491             var favorite = SettingManager.Common.FavoritesCountApi;
492             var list = SettingManager.Common.ListCountApi;
493             var search = SettingManager.Common.SearchCountApi;
494             var usertl = SettingManager.Common.UserTimelineCountApi;
495
496             SettingManager.Common.UseAdditionalCount = true;
497
498             // Timeline
499             Assert.Equal(timeline, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.Timeline, false, false));
500             Assert.Equal(more, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.Timeline, true, false));
501             Assert.Equal(startup, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.Timeline, false, true));
502
503             // Reply
504             Assert.Equal(reply, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.Reply, false, false));
505             Assert.Equal(more, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.Reply, true, false));
506             Assert.Equal(reply, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.Reply, false, true));  //Replyの値が使われる
507
508             // Favorites
509             Assert.Equal(favorite, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.Favorites, false, false));
510             Assert.Equal(favorite, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.Favorites, true, false));
511             Assert.Equal(favorite, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.Favorites, false, true));
512
513             SettingManager.Common.FavoritesCountApi = 0;
514
515             Assert.Equal(timeline, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.Favorites, false, false));
516             Assert.Equal(more, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.Favorites, true, false));
517             Assert.Equal(startup, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.Favorites, false, true));
518
519             // List
520             Assert.Equal(list, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.List, false, false));
521             Assert.Equal(list, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.List, true, false));
522             Assert.Equal(list, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.List, false, true));
523
524             SettingManager.Common.ListCountApi = 0;
525
526             Assert.Equal(timeline, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.List, false, false));
527             Assert.Equal(more, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.List, true, false));
528             Assert.Equal(startup, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.List, false, true));
529
530             // PublicSearch
531             Assert.Equal(search, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.PublicSearch, false, false));
532             Assert.Equal(search, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.PublicSearch, true, false));
533             Assert.Equal(search, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.PublicSearch, false, true));
534
535             SettingManager.Common.SearchCountApi = 0;
536
537             Assert.Equal(timeline, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.PublicSearch, false, false));
538             Assert.Equal(search, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.PublicSearch, true, false));  //MoreCountApiの値がPublicSearchの最大値に制限される
539             Assert.Equal(startup, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.PublicSearch, false, true));
540
541             // UserTimeline
542             Assert.Equal(usertl, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.UserTimeline, false, false));
543             Assert.Equal(usertl, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.UserTimeline, true, false));
544             Assert.Equal(usertl, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.UserTimeline, false, true));
545
546             SettingManager.Common.UserTimelineCountApi = 0;
547
548             Assert.Equal(timeline, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.UserTimeline, false, false));
549             Assert.Equal(more, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.UserTimeline, true, false));
550             Assert.Equal(startup, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.UserTimeline, false, true));
551
552             SettingManagerTest.Common = oldInstance;
553         }
554
555         [Fact]
556         public void GetTextLengthRemain_Test()
557         {
558             using (var twitter = new Twitter())
559             {
560                 Assert.Equal(280, twitter.GetTextLengthRemain(""));
561                 Assert.Equal(272, twitter.GetTextLengthRemain("hogehoge"));
562             }
563         }
564
565         [Fact]
566         public void GetTextLengthRemain_DirectMessageTest()
567         {
568             using (var twitter = new Twitter())
569             {
570                 // 2015年8月から DM の文字数上限が 10,000 文字に変更された
571                 // https://twittercommunity.com/t/41348
572                 twitter.Configuration.DmTextCharacterLimit = 10000;
573
574                 Assert.Equal(10000, twitter.GetTextLengthRemain("D twitter "));
575                 Assert.Equal(9992, twitter.GetTextLengthRemain("D twitter hogehoge"));
576
577                 // t.co に短縮される分の文字数を考慮
578                 twitter.Configuration.ShortUrlLength = 20;
579                 Assert.Equal(9971, twitter.GetTextLengthRemain("D twitter hogehoge http://example.com/"));
580
581                 twitter.Configuration.ShortUrlLengthHttps = 21;
582                 Assert.Equal(9970, twitter.GetTextLengthRemain("D twitter hogehoge https://example.com/"));
583             }
584         }
585
586         [Fact]
587         public void GetTextLengthRemain_UrlTest()
588         {
589             using (var twitter = new Twitter())
590             {
591                 // t.co に短縮される分の文字数を考慮
592                 twitter.TextConfiguration.TransformedURLLength = 20;
593                 Assert.Equal(260, twitter.GetTextLengthRemain("http://example.com/"));
594                 Assert.Equal(260, twitter.GetTextLengthRemain("http://example.com/hogehoge"));
595                 Assert.Equal(251, twitter.GetTextLengthRemain("hogehoge http://example.com/"));
596
597                 Assert.Equal(260, twitter.GetTextLengthRemain("https://example.com/"));
598                 Assert.Equal(260, twitter.GetTextLengthRemain("https://example.com/hogehoge"));
599                 Assert.Equal(251, twitter.GetTextLengthRemain("hogehoge https://example.com/"));
600             }
601         }
602
603         [Fact]
604         public void GetTextLengthRemain_UrlWithoutSchemeTest()
605         {
606             using (var twitter = new Twitter())
607             {
608                 // t.co に短縮される分の文字数を考慮
609                 twitter.TextConfiguration.TransformedURLLength = 20;
610                 Assert.Equal(260, twitter.GetTextLengthRemain("example.com"));
611                 Assert.Equal(260, twitter.GetTextLengthRemain("example.com/hogehoge"));
612                 Assert.Equal(251, twitter.GetTextLengthRemain("hogehoge example.com"));
613
614                 // スキーム (http://) を省略かつ末尾が ccTLD の場合は t.co に短縮されない
615                 Assert.Equal(270, twitter.GetTextLengthRemain("example.jp"));
616                 // ただし、末尾にパスが続く場合は t.co に短縮される
617                 Assert.Equal(260, twitter.GetTextLengthRemain("example.jp/hogehoge"));
618             }
619         }
620
621         [Fact]
622         public void GetTextLengthRemain_SurrogatePairTest()
623         {
624             using (var twitter = new Twitter())
625             {
626                 Assert.Equal(278, twitter.GetTextLengthRemain("🍣"));
627                 Assert.Equal(267, twitter.GetTextLengthRemain("🔥🐔🔥 焼き鳥"));
628             }
629         }
630
631         [Fact]
632         public void GetTextLengthRemain_EmojiTest()
633         {
634             using (var twitter = new Twitter())
635             {
636                 // 絵文字の文字数カウントの仕様変更に対するテストケース
637                 // https://twittercommunity.com/t/114607
638
639                 Assert.Equal(279, twitter.GetTextLengthRemain("©")); // 基本多言語面の絵文字
640                 Assert.Equal(277, twitter.GetTextLengthRemain("©\uFE0E")); // 異字体セレクタ付き (text style)
641                 Assert.Equal(279, twitter.GetTextLengthRemain("©\uFE0F")); // 異字体セレクタ付き (emoji style)
642                 Assert.Equal(278, twitter.GetTextLengthRemain("🍣")); // 拡張面の絵文字
643                 Assert.Equal(279, twitter.GetTextLengthRemain("#⃣")); // 合字で表現される絵文字
644                 Assert.Equal(278, twitter.GetTextLengthRemain("👦\U0001F3FF")); // Emoji modifier 付きの絵文字
645                 Assert.Equal(278, twitter.GetTextLengthRemain("\U0001F3FF")); // Emoji modifier 単体
646                 Assert.Equal(278, twitter.GetTextLengthRemain("👨\u200D🎨")); // ZWJ で結合された絵文字
647                 Assert.Equal(278, twitter.GetTextLengthRemain("🏃\u200D♀\uFE0F")); // ZWJ と異字体セレクタを含む絵文字
648             }
649         }
650
651         [Fact]
652         public void GetTextLengthRemain_BrokenSurrogateTest()
653         {
654             using (var twitter = new Twitter())
655             {
656                 // 投稿欄に IME から絵文字を入力すると HighSurrogate のみ入力された状態で TextChanged イベントが呼ばれることがある
657                 Assert.Equal(278, twitter.GetTextLengthRemain("\ud83d"));
658                 Assert.Equal(9999, twitter.GetTextLengthRemain("D twitter \ud83d"));
659             }
660         }
661     }
662 }