1 // OpenTween - Client of Twitter
2 // Copyright (c) 2012 kim_upsilon (@kim_upsilon) <https://upsilo.net/~upsilon/>
3 // All rights reserved.
5 // This file is part of OpenTween.
7 // This program is free software; you can redistribute it and/or modify it
8 // under the terms of the GNU General Public License as published by the Free
9 // Software Foundation; either version 3 of the License, or (at your option)
12 // This program is distributed in the hope that it will be useful, but
13 // WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14 // or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
17 // You should have received a copy of the GNU General Public License along
18 // with this program. If not, see <http://www.gnu.org/licenses/>, or write to
19 // the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
20 // Boston, MA 02110-1301, USA.
23 using System.Collections.Generic;
25 using System.Reflection;
27 using System.Threading.Tasks;
29 using Xunit.Extensions;
33 public class PostClassTest
35 class TestPostClass : PostClass
37 protected override PostClass RetweetSource
41 var retweetedId = this.RetweetedId.Value;
43 return PostClassTest.TestCases.ContainsKey(retweetedId) ?
44 PostClassTest.TestCases[retweetedId] :
50 private static Dictionary<long, PostClass> TestCases;
52 public PostClassTest()
54 PostClassTest.TestCases = new Dictionary<long, PostClass>
56 [1L] = new TestPostClass { StatusId = 1L },
57 [2L] = new TestPostClass { StatusId = 2L, IsFav = true },
58 [3L] = new TestPostClass { StatusId = 3L, IsFav = false, RetweetedId = 2L },
63 public void CloneTest()
65 var post = new PostClass();
66 var clonePost = post.Clone();
68 TestUtils.CheckDeepCloning(post, clonePost);
72 [InlineData(null, null)]
74 [InlineData("aaa\nbbb", "aaa bbb")]
75 public void TextSingleLineTest(string text, string expected)
77 var post = new PostClass { TextFromApi = text };
79 Assert.Equal(expected, post.TextSingleLine);
83 [InlineData(1L, false)]
84 [InlineData(2L, true)]
85 [InlineData(3L, true)]
86 public void GetIsFavTest(long statusId, bool expected)
88 Assert.Equal(expected, PostClassTest.TestCases[statusId].IsFav);
92 [InlineData(2L, true)]
93 [InlineData(2L, false)]
94 [InlineData(3L, true)]
95 [InlineData(3L, false)]
96 public void SetIsFavTest(long statusId, bool isFav)
98 var post = PostClassTest.TestCases[statusId];
101 Assert.Equal(isFav, post.IsFav);
103 if (post.RetweetedId != null)
104 Assert.Equal(isFav, PostClassTest.TestCases[post.RetweetedId.Value].IsFav);
108 [InlineData(false, false, false, false, -0x01)]
109 [InlineData( true, false, false, false, 0x00)]
110 [InlineData(false, true, false, false, 0x01)]
111 [InlineData( true, true, false, false, 0x02)]
112 [InlineData(false, false, true, false, 0x03)]
113 [InlineData( true, false, true, false, 0x04)]
114 [InlineData(false, true, true, false, 0x05)]
115 [InlineData( true, true, true, false, 0x06)]
116 [InlineData(false, false, false, true, 0x07)]
117 [InlineData( true, false, false, true, 0x08)]
118 [InlineData(false, true, false, true, 0x09)]
119 [InlineData( true, true, false, true, 0x0A)]
120 [InlineData(false, false, true, true, 0x0B)]
121 [InlineData( true, false, true, true, 0x0C)]
122 [InlineData(false, true, true, true, 0x0D)]
123 [InlineData( true, true, true, true, 0x0E)]
124 public void StateIndexTest(bool protect, bool mark, bool reply, bool geo, int expected)
126 var post = new TestPostClass
130 InReplyToStatusId = reply ? (long?)100L : null,
131 PostGeo = geo ? new PostClass.StatusGeo(-126.716667, -47.15) : (PostClass.StatusGeo?)null,
134 Assert.Equal(expected, post.StateIndex);
138 public void SourceHtml_Test()
140 var post = new TestPostClass
142 Source = "Twitter Web Client",
143 SourceUri = new Uri("http://twitter.com/"),
146 Assert.Equal("<a href=\"http://twitter.com/\" rel=\"nofollow\">Twitter Web Client</a>", post.SourceHtml);
150 public void SourceHtml_PlainTextTest()
152 var post = new TestPostClass
158 Assert.Equal("web", post.SourceHtml);
162 public void SourceHtml_EscapeTest()
164 var post = new TestPostClass
166 Source = "<script>alert(1)</script>",
167 SourceUri = new Uri("http://example.com/?aaa=123&bbb=456"),
170 Assert.Equal("<a href=\"http://example.com/?aaa=123&bbb=456\" rel=\"nofollow\"><script>alert(1)</script></a>", post.SourceHtml);
174 public void SourceHtml_EscapePlainTextTest()
176 var post = new TestPostClass
178 Source = "<script>alert(1)</script>",
182 Assert.Equal("<script>alert(1)</script>", post.SourceHtml);
186 public void DeleteTest()
188 var post = new TestPostClass
190 InReplyToStatusId = 10L,
191 InReplyToUser = "hogehoge",
192 InReplyToUserId = 100L,
194 ReplyToList = new List<string> {"hogehoge"},
197 post.IsDeleted = true;
199 Assert.Null(post.InReplyToStatusId);
200 Assert.Equal("", post.InReplyToUser);
201 Assert.Null(post.InReplyToUserId);
202 Assert.False(post.IsReply);
203 Assert.Empty(post.ReplyToList);
204 Assert.Equal(-1, post.StateIndex);
208 public void CanDeleteBy_SentDMTest()
210 var post = new TestPostClass
213 IsMe = true, // 自分が送信した DM
214 UserId = 222L, // 送信先ユーザーID
217 Assert.True(post.CanDeleteBy(selfUserId: 111L));
221 public void CanDeleteBy_ReceivedDMTest()
223 var post = new TestPostClass
226 IsMe = false, // 自分が受け取った DM
227 UserId = 222L, // 送信元ユーザーID
230 Assert.True(post.CanDeleteBy(selfUserId: 111L));
234 public void CanDeleteBy_MyTweetTest()
236 var post = new TestPostClass
238 UserId = 111L, // 自分のツイート
241 Assert.True(post.CanDeleteBy(selfUserId: 111L));
245 public void CanDeleteBy_OthersTweetTest()
247 var post = new TestPostClass
249 UserId = 222L, // 他人のツイート
252 Assert.False(post.CanDeleteBy(selfUserId: 111L));
256 public void CanDeleteBy_RetweetedByMeTest()
258 var post = new TestPostClass
260 RetweetedByUserId = 111L, // 自分がリツイートした
261 UserId = 222L, // 他人のツイート
264 Assert.True(post.CanDeleteBy(selfUserId: 111L));
268 public void CanDeleteBy_RetweetedByOthersTest()
270 var post = new TestPostClass
272 RetweetedByUserId = 333L, // 他人がリツイートした
273 UserId = 222L, // 他人のツイート
276 Assert.False(post.CanDeleteBy(selfUserId: 111L));
280 public void CanDeleteBy_MyTweetHaveBeenRetweetedByOthersTest()
282 var post = new TestPostClass
284 RetweetedByUserId = 222L, // 他人がリツイートした
285 UserId = 111L, // 自分のツイート
288 Assert.True(post.CanDeleteBy(selfUserId: 111L));
292 public void ConvertToOriginalPost_Test()
294 var retweetPost = new PostClass
301 RetweetedBy = "@bbb",
302 RetweetedByUserId = 2L,
306 var originalPost = retweetPost.ConvertToOriginalPost();
308 Assert.Equal(50L, originalPost.StatusId);
309 Assert.Equal("@aaa", originalPost.ScreenName);
310 Assert.Equal(1L, originalPost.UserId);
312 Assert.Equal(null, originalPost.RetweetedId);
313 Assert.Equal("", originalPost.RetweetedBy);
314 Assert.Equal(null, originalPost.RetweetedByUserId);
315 Assert.Equal(1, originalPost.RetweetedCount);
319 public void ConvertToOriginalPost_ErrorTest()
322 var post = new PostClass { StatusId = 100L, RetweetedId = null };
324 Assert.Throws<InvalidOperationException>(() => post.ConvertToOriginalPost());
327 class FakeExpandedUrlInfo : PostClass.ExpandedUrlInfo
329 public TaskCompletionSource<string> fakeResult;
331 public FakeExpandedUrlInfo(string url, string expandedUrl, bool deepExpand)
332 : base(url, expandedUrl, deepExpand)
336 protected override async Task DeepExpandAsync()
338 this.fakeResult = new TaskCompletionSource<string>();
339 this._expandedUrl = await this.fakeResult.Task;
344 public async Task ExpandedUrls_BasicScenario()
346 var post = new PostClass
348 Text = "<a href=\"http://t.co/aaaaaaa\" title=\"http://t.co/aaaaaaa\">bit.ly/abcde</a>",
349 ExpandedUrls = new Dictionary<string, PostClass.ExpandedUrlInfo>
351 ["http://t.co/aaaaaaa"] = new FakeExpandedUrlInfo(
352 // 展開前の t.co ドメインの URL
353 url: "http://t.co/aaaaaaa",
355 // Entity の expanded_url に含まれる URL
356 expandedUrl: "http://bit.ly/abcde",
358 // expandedUrl をさらに ShortUrl クラスで再帰的に展開する
364 var urlInfo = (FakeExpandedUrlInfo)post.ExpandedUrls.Values.Single();
366 // ExpandedUrlInfo による展開が完了していない状態
367 // → この段階では Entity に含まれる expanded_url の URL が使用される
368 Assert.False(urlInfo.ExpandedCompleted);
369 Assert.Equal("http://bit.ly/abcde", urlInfo.ExpandedUrl);
370 Assert.Equal("http://bit.ly/abcde", post.GetExpandedUrl("http://t.co/aaaaaaa"));
371 Assert.Equal(new[] { "http://bit.ly/abcde" }, post.GetExpandedUrls());
372 Assert.Equal("<a href=\"http://t.co/aaaaaaa\" title=\"http://bit.ly/abcde\">bit.ly/abcde</a>", post.Text);
374 // bit.ly 展開後の URL は「http://example.com/abcde」
375 urlInfo.fakeResult.SetResult("http://example.com/abcde");
376 await urlInfo.ExpandTask;
378 // ExpandedUrlInfo による展開が完了した後の状態
379 // → 再帰的な展開後の URL が使用される
380 Assert.True(urlInfo.ExpandedCompleted);
381 Assert.Equal("http://example.com/abcde", urlInfo.ExpandedUrl);
382 Assert.Equal("http://example.com/abcde", post.GetExpandedUrl("http://t.co/aaaaaaa"));
383 Assert.Equal(new[] { "http://example.com/abcde" }, post.GetExpandedUrls());
384 Assert.Equal("<a href=\"http://t.co/aaaaaaa\" title=\"http://example.com/abcde\">bit.ly/abcde</a>", post.Text);