1 // OpenTween - Client of Twitter
2 // Copyright (c) 2013 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;
26 using System.Text.RegularExpressions;
27 using OpenTween.Api.DataModel;
28 using OpenTween.Models;
30 using Xunit.Extensions;
34 public class TwitterTest
37 [InlineData("https://twitter.com/twitterapi/status/22634515958",
38 new[] { "22634515958" })]
39 [InlineData("<a target=\"_self\" href=\"https://t.co/aaaaaaaa\" title=\"https://twitter.com/twitterapi/status/22634515958\">twitter.com/twitterapi/stat…</a>",
40 new[] { "22634515958" })]
41 [InlineData("<a target=\"_self\" href=\"https://t.co/bU3oR95KIy\" title=\"https://twitter.com/haru067/status/224782458816692224\">https://t.co/bU3oR95KIy</a>" +
42 "<a target=\"_self\" href=\"https://t.co/bbbbbbbb\" title=\"https://twitter.com/karno/status/311081657790771200\">https://t.co/bbbbbbbb</a>",
43 new[] { "224782458816692224", "311081657790771200" })]
44 [InlineData("https://mobile.twitter.com/muji_net/status/21984934471",
45 new[] { "21984934471" })]
46 [InlineData("https://twitter.com/imgazyobuzi/status/293333871171354624/photo/1",
47 new[] { "293333871171354624" })]
48 public void StatusUrlRegexTest(string url, string[] expected)
50 var results = Twitter.StatusUrlRegex.Matches(url).Cast<Match>()
51 .Select(x => x.Groups["StatusId"].Value).ToArray();
53 Assert.Equal(expected, results);
57 [InlineData("http://favstar.fm/users/twitterapi/status/22634515958", new[] { "22634515958" })]
58 [InlineData("http://ja.favstar.fm/users/twitterapi/status/22634515958", new[] { "22634515958" })]
59 [InlineData("http://favstar.fm/t/22634515958", new[] { "22634515958" })]
60 [InlineData("http://aclog.koba789.com/i/312485321239564288", new[] { "312485321239564288" })]
61 [InlineData("http://frtrt.net/solo_status.php?status=263483634307198977", new[] { "263483634307198977" })]
62 public void ThirdPartyStatusUrlRegexTest(string url, string[] expected)
64 var results = Twitter.ThirdPartyStatusUrlRegex.Matches(url).Cast<Match>()
65 .Select(x => x.Groups["StatusId"].Value).ToArray();
67 Assert.Equal(expected, results);
71 public void FindTopOfReplyChainTest()
73 var posts = new Dictionary<long, PostClass>
75 [950L] = new PostClass { StatusId = 950L, InReplyToStatusId = null }, // このツイートが末端
76 [987L] = new PostClass { StatusId = 987L, InReplyToStatusId = 950L },
77 [999L] = new PostClass { StatusId = 999L, InReplyToStatusId = 987L },
78 [1000L] = new PostClass { StatusId = 1000L, InReplyToStatusId = 999L },
80 Assert.Equal(950L, Twitter.FindTopOfReplyChain(posts, 1000L).StatusId);
81 Assert.Equal(950L, Twitter.FindTopOfReplyChain(posts, 950L).StatusId);
82 Assert.Throws<ArgumentException>(() => Twitter.FindTopOfReplyChain(posts, 500L));
84 posts = new Dictionary<long, PostClass>
86 // 1200L は posts の中に存在しない
87 [1210L] = new PostClass { StatusId = 1210L, InReplyToStatusId = 1200L },
88 [1220L] = new PostClass { StatusId = 1220L, InReplyToStatusId = 1210L },
89 [1230L] = new PostClass { StatusId = 1230L, InReplyToStatusId = 1220L },
91 Assert.Equal(1210L, Twitter.FindTopOfReplyChain(posts, 1230L).StatusId);
92 Assert.Equal(1210L, Twitter.FindTopOfReplyChain(posts, 1210L).StatusId);
96 public void ParseSource_Test()
98 var sourceHtml = "<a href=\"http://twitter.com\" rel=\"nofollow\">Twitter Web Client</a>";
100 var result = Twitter.ParseSource(sourceHtml);
101 Assert.Equal("Twitter Web Client", result.Item1);
102 Assert.Equal(new Uri("http://twitter.com/"), result.Item2);
106 public void ParseSource_PlainTextTest()
108 var sourceHtml = "web";
110 var result = Twitter.ParseSource(sourceHtml);
111 Assert.Equal("web", result.Item1);
112 Assert.Equal(null, result.Item2);
116 public void ParseSource_RelativeUriTest()
118 // 参照: https://twitter.com/kim_upsilon/status/477796052049752064
119 var sourceHtml = "<a href=\"erased_45416\" rel=\"nofollow\">erased_45416</a>";
121 var result = Twitter.ParseSource(sourceHtml);
122 Assert.Equal("erased_45416", result.Item1);
123 Assert.Equal(new Uri("https://twitter.com/erased_45416"), result.Item2);
127 public void ParseSource_EmptyTest()
129 // 参照: https://twitter.com/kim_upsilon/status/595156014032244738
132 var result = Twitter.ParseSource(sourceHtml);
133 Assert.Equal("", result.Item1);
134 Assert.Equal(null, result.Item2);
138 public void ParseSource_NullTest()
140 string sourceHtml = null;
142 var result = Twitter.ParseSource(sourceHtml);
143 Assert.Equal("", result.Item1);
144 Assert.Equal(null, result.Item2);
148 public void ParseSource_UnescapeTest()
150 string sourceHtml = "<a href=\"http://example.com/?aaa=123&bbb=456\" rel=\"nofollow\"><<hogehoge>></a>";
152 var result = Twitter.ParseSource(sourceHtml);
153 Assert.Equal("<<hogehoge>>", result.Item1);
154 Assert.Equal(new Uri("http://example.com/?aaa=123&bbb=456"), result.Item2);
158 public void ParseSource_UnescapeNoUriTest()
160 string sourceHtml = "<<hogehoge>>";
162 var result = Twitter.ParseSource(sourceHtml);
163 Assert.Equal("<<hogehoge>>", result.Item1);
164 Assert.Equal(null, result.Item2);
168 public void GetQuoteTweetStatusIds_EntityTest()
174 Url = "https://t.co/3HXq0LrbJb",
175 ExpandedUrl = "https://twitter.com/kim_upsilon/status/599261132361072640",
179 var statusIds = Twitter.GetQuoteTweetStatusIds(entities);
180 Assert.Equal(new[] { 599261132361072640L }, statusIds);
184 public void GetQuoteTweetStatusIds_UrlStringTest()
188 "https://twitter.com/kim_upsilon/status/599261132361072640",
191 var statusIds = Twitter.GetQuoteTweetStatusIds(urls);
192 Assert.Equal(new[] { 599261132361072640L }, statusIds);
196 public void GetQuoteTweetStatusIds_OverflowTest()
200 // 符号付き 64 ビット整数の範囲を超える値
201 "https://twitter.com/kim_upsilon/status/9999999999999999999",
204 var statusIds = Twitter.GetQuoteTweetStatusIds(urls);
205 Assert.Empty(statusIds);
209 public void GetApiResultCount_DefaultTest()
211 var oldInstance = SettingCommon.Instance;
212 SettingCommon.Instance = new SettingCommon();
214 var timeline = SettingCommon.Instance.CountApi;
215 var reply = SettingCommon.Instance.CountApiReply;
216 var dm = 20; // DMは固定値
217 var more = SettingCommon.Instance.MoreCountApi;
218 var startup = SettingCommon.Instance.FirstCountApi;
219 var favorite = SettingCommon.Instance.FavoritesCountApi;
220 var list = SettingCommon.Instance.ListCountApi;
221 var search = SettingCommon.Instance.SearchCountApi;
222 var usertl = SettingCommon.Instance.UserTimelineCountApi;
225 Assert.Equal(false, SettingCommon.Instance.UseAdditionalCount);
226 Assert.Equal(60, timeline);
227 Assert.Equal(40, reply);
228 Assert.Equal(200, more);
229 Assert.Equal(100, startup);
230 Assert.Equal(40, favorite);
231 Assert.Equal(100, list);
232 Assert.Equal(100, search);
233 Assert.Equal(20, usertl);
236 Assert.Equal(timeline, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.Timeline, false, false));
237 Assert.Equal(reply, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.Reply, false, false));
240 Assert.Equal(dm, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.DirectMessegeRcv, false, false));
241 Assert.Equal(dm, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.DirectMessegeSnt, false, false));
243 // その他はTimelineと同値になる
244 Assert.Equal(timeline, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.Favorites, false, false));
245 Assert.Equal(timeline, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.List, false, false));
246 Assert.Equal(timeline, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.PublicSearch, false, false));
247 Assert.Equal(timeline, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.UserTimeline, false, false));
249 SettingCommon.Instance = oldInstance;
253 public void GetApiResultCount_AdditionalCountTest()
255 var oldInstance = SettingCommon.Instance;
256 SettingCommon.Instance = new SettingCommon();
258 var timeline = SettingCommon.Instance.CountApi;
259 var reply = SettingCommon.Instance.CountApiReply;
260 var dm = 20; // DMは固定値
261 var more = SettingCommon.Instance.MoreCountApi;
262 var startup = SettingCommon.Instance.FirstCountApi;
263 var favorite = SettingCommon.Instance.FavoritesCountApi;
264 var list = SettingCommon.Instance.ListCountApi;
265 var search = SettingCommon.Instance.SearchCountApi;
266 var usertl = SettingCommon.Instance.UserTimelineCountApi;
268 SettingCommon.Instance.UseAdditionalCount = true;
271 Assert.Equal(timeline, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.Timeline, false, false));
272 Assert.Equal(more, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.Timeline, true, false));
273 Assert.Equal(startup, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.Timeline, false, true));
276 Assert.Equal(reply, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.Reply, false, false));
277 Assert.Equal(more, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.Reply, true, false));
278 Assert.Equal(reply, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.Reply, false, true)); //Replyの値が使われる
281 Assert.Equal(dm, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.DirectMessegeRcv, false, false));
282 Assert.Equal(dm, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.DirectMessegeSnt, false, false));
285 Assert.Equal(favorite, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.Favorites, false, false));
286 Assert.Equal(favorite, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.Favorites, true, false));
287 Assert.Equal(favorite, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.Favorites, false, true));
289 SettingCommon.Instance.FavoritesCountApi = 0;
291 Assert.Equal(timeline, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.Favorites, false, false));
292 Assert.Equal(more, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.Favorites, true, false));
293 Assert.Equal(startup, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.Favorites, false, true));
296 Assert.Equal(list, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.List, false, false));
297 Assert.Equal(list, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.List, true, false));
298 Assert.Equal(list, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.List, false, true));
300 SettingCommon.Instance.ListCountApi = 0;
302 Assert.Equal(timeline, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.List, false, false));
303 Assert.Equal(more, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.List, true, false));
304 Assert.Equal(startup, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.List, false, true));
307 Assert.Equal(search, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.PublicSearch, false, false));
308 Assert.Equal(search, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.PublicSearch, true, false));
309 Assert.Equal(search, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.PublicSearch, false, true));
311 SettingCommon.Instance.SearchCountApi = 0;
313 Assert.Equal(timeline, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.PublicSearch, false, false));
314 Assert.Equal(search, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.PublicSearch, true, false)); //MoreCountApiの値がPublicSearchの最大値に制限される
315 Assert.Equal(startup, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.PublicSearch, false, true));
318 Assert.Equal(usertl, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.UserTimeline, false, false));
319 Assert.Equal(usertl, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.UserTimeline, true, false));
320 Assert.Equal(usertl, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.UserTimeline, false, true));
322 SettingCommon.Instance.UserTimelineCountApi = 0;
324 Assert.Equal(timeline, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.UserTimeline, false, false));
325 Assert.Equal(more, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.UserTimeline, true, false));
326 Assert.Equal(startup, Twitter.GetApiResultCount(MyCommon.WORKERTYPE.UserTimeline, false, true));
328 SettingCommon.Instance = oldInstance;
332 public void GetTextLengthRemain_Test()
334 using (var twitter = new Twitter())
336 Assert.Equal(140, twitter.GetTextLengthRemain(""));
337 Assert.Equal(132, twitter.GetTextLengthRemain("hogehoge"));
342 public void GetTextLengthRemain_DirectMessageTest()
344 using (var twitter = new Twitter())
346 // 2015年8月から DM の文字数上限が 10,000 文字に変更された
347 // https://twittercommunity.com/t/41348
348 twitter.Configuration.DmTextCharacterLimit = 10000;
350 Assert.Equal(10000, twitter.GetTextLengthRemain("D twitter "));
351 Assert.Equal(9992, twitter.GetTextLengthRemain("D twitter hogehoge"));
356 public void GetTextLengthRemain_UrlTest()
358 using (var twitter = new Twitter())
360 // t.co に短縮される分の文字数を考慮
361 twitter.Configuration.ShortUrlLength = 20;
362 Assert.Equal(120, twitter.GetTextLengthRemain("http://example.com/"));
363 Assert.Equal(120, twitter.GetTextLengthRemain("http://example.com/hogehoge"));
364 Assert.Equal(111, twitter.GetTextLengthRemain("hogehoge http://example.com/"));
366 twitter.Configuration.ShortUrlLengthHttps = 21;
367 Assert.Equal(119, twitter.GetTextLengthRemain("https://example.com/"));
368 Assert.Equal(119, twitter.GetTextLengthRemain("https://example.com/hogehoge"));
369 Assert.Equal(110, twitter.GetTextLengthRemain("hogehoge https://example.com/"));
374 public void GetTextLengthRemain_UrlWithoutSchemeTest()
376 using (var twitter = new Twitter())
378 // t.co に短縮される分の文字数を考慮
379 twitter.Configuration.ShortUrlLength = 20;
380 Assert.Equal(120, twitter.GetTextLengthRemain("example.com"));
381 Assert.Equal(120, twitter.GetTextLengthRemain("example.com/hogehoge"));
382 Assert.Equal(111, twitter.GetTextLengthRemain("hogehoge example.com"));
384 // スキーム (http://) を省略かつ末尾が ccTLD の場合は t.co に短縮されない
385 Assert.Equal(130, twitter.GetTextLengthRemain("example.jp"));
386 // ただし、末尾にパスが続く場合は t.co に短縮される
387 Assert.Equal(120, twitter.GetTextLengthRemain("example.jp/hogehoge"));
392 public void GetTextLengthRemain_SurrogatePairTest()
394 using (var twitter = new Twitter())
396 Assert.Equal(139, twitter.GetTextLengthRemain("🍣"));
397 Assert.Equal(133, twitter.GetTextLengthRemain("🔥🐔🔥 焼き鳥"));