OSDN Git Service

URLをWebブラウザで開く時にスキームが正しいか検証する
[opentween/open-tween.git] / OpenTween.Tests / MyCommonTest.cs
1 // OpenTween - Client of Twitter
2 // Copyright (c) 2012 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.IO;
25 using System.Linq;
26 using System.Reflection;
27 using System.Runtime.InteropServices;
28 using System.Runtime.Serialization;
29 using System.Text;
30 using System.Windows.Forms;
31 using Moq;
32 using OpenTween;
33 using OpenTween.Models;
34 using Xunit;
35 using Xunit.Extensions;
36
37 namespace OpenTween
38 {
39     public class MyCommonTest
40     {
41         [Theory]
42         [InlineData("http://日本語.idn.icann.org/", "http://xn--wgv71a119e.idn.icann.org/")]
43         [InlineData("http://例え.テスト/", "http://xn--r8jz45g.xn--zckzah/")]
44         public void IDNEncodeTest(string uri, string expected)
45             => Assert.Equal(expected, MyCommon.IDNEncode(uri));
46
47         [Theory]
48         [InlineData("http://xn--wgv71a119e.idn.icann.org/", "http://日本語.idn.icann.org/")]
49         [InlineData("http://xn--r8jz45g.xn--zckzah/", "http://例え.テスト/")]
50         [InlineData("http://xn--a/", "http://xn--a/")] // 不正なpunycode
51         public void IDNDecodeTest(string uri, string expected)
52             => Assert.Equal(expected, MyCommon.IDNDecode(uri));
53
54         [Theory]
55         [InlineData("http://xn--r8jz45g.xn--zckzah/", "http://例え.テスト/")]
56         [InlineData("http://ja.wikipedia.org/wiki/%3F", "http://ja.wikipedia.org/wiki/%3F")] // "?" に変換しない
57         [InlineData("http://ja.wikipedia.org/wiki/%E3%83%9E%E3%82%B8LOVE1000%25",
58             "http://ja.wikipedia.org/wiki/マジLOVE1000%25")] // "%" も変換しない
59         [InlineData("http://xn--a/%E3%81%82", "http://xn--a/あ")] // 不正なpunycode
60         [InlineData("http://example..com/", "http://example..com/")] // 不正なURL
61         [InlineData("http://example.com/%E3%81%82%FF", "http://example.com/あ%FF")] // 不正なUTF-8シーケンス
62         [InlineData("http://example.com/%E3%81%82%ED%A0%80", "http://example.com/あ%ED%A0%80")] // 不正なUTF-8シーケンス (high surrogate)
63         public void ConvertToReadableUrl(string url, string expected)
64             => Assert.Equal(expected, MyCommon.ConvertToReadableUrl(url));
65
66         [Theory]
67         [InlineData(new int[] { 1, 2, 3, 4 }, 0, 3, new int[] { 2, 3, 4, 1 })] // 左ローテイト?
68         [InlineData(new int[] { 1, 2, 3, 4 }, 3, 0, new int[] { 4, 1, 2, 3 })] // 右ローテイト?
69         [InlineData(new int[] { 1, 2, 3, 4, 5 }, 1, 3, new int[] { 1, 3, 4, 2, 5 })]
70         [InlineData(new int[] { 1, 2, 3, 4, 5 }, 3, 1, new int[] { 1, 4, 2, 3, 5 })]
71         public void MoveArrayItemTest(int[] values, int idx_fr, int idx_to, int[] expected)
72         {
73             // MoveArrayItem は values を直接変更するため複製を用意する
74             var copy = new int[values.Length];
75             Array.Copy(values, copy, values.Length);
76
77             MyCommon.MoveArrayItem(copy, idx_fr, idx_to);
78             Assert.Equal(expected, copy);
79         }
80
81         [Fact]
82         public void EncryptStringTest()
83         {
84             var str = "hogehoge";
85
86             var crypto = MyCommon.EncryptString(str);
87             Assert.NotEqual(str, crypto);
88
89             var decrypt = MyCommon.DecryptString(crypto);
90             Assert.Equal(str, decrypt);
91         }
92
93         [Theory]
94         [InlineData(new byte[] { 0x01, 0x02 }, 3, new byte[] { 0x01, 0x02, 0x00 })]
95         [InlineData(new byte[] { 0x01, 0x02 }, 2, new byte[] { 0x01, 0x02 })]
96         [InlineData(new byte[] { 0x01, 0x02 }, 1, new byte[] { 0x03 })]
97         public void ResizeBytesArrayTest(byte[] bytes, int size, byte[] expected)
98             => Assert.Equal(expected, MyCommon.ResizeBytesArray(bytes, size));
99
100         [Theory]
101         [InlineData("Resources/re.gif", true)]
102         [InlineData("Resources/re1.gif", false)]
103         [InlineData("Resources/re1.png", false)]
104         public void IsAnimatedGifTest(string filename, bool expected)
105             => Assert.Equal(expected, MyCommon.IsAnimatedGif(filename));
106
107         public static readonly TheoryData<string, DateTimeUtc> DateTimeParseTestCase = new()
108         {
109             { "Sun Nov 25 06:10:00 +00:00 2012", new DateTimeUtc(2012, 11, 25, 6, 10, 0) },
110             { "Sun, 25 Nov 2012 06:10:00 +00:00", new DateTimeUtc(2012, 11, 25, 6, 10, 0) },
111         };
112
113         [Theory]
114         [MemberData(nameof(DateTimeParseTestCase))]
115         public void DateTimeParseTest(string date, DateTimeUtc excepted)
116             => Assert.Equal(excepted, MyCommon.DateTimeParse(date));
117
118         [DataContract]
119         public struct JsonData
120         {
121             [DataMember(Name = "id")]
122             public string Id { get; set; }
123
124             [DataMember(Name = "body")]
125             public string Body { get; set; }
126         }
127
128         public static readonly TheoryData<string, JsonData> CreateDataFromJsonTestCase = new()
129         {
130             {
131                 @"{""id"":""1"", ""body"":""hogehoge""}",
132                 new JsonData { Id = "1", Body = "hogehoge" }
133             },
134         };
135
136         [Theory]
137         [MemberData(nameof(CreateDataFromJsonTestCase))]
138         public void CreateDataFromJsonTest<T>(string json, T expected)
139             => Assert.Equal(expected, MyCommon.CreateDataFromJson<T>(json));
140
141         [Theory]
142         [InlineData("hoge123@example.com", true)]
143         [InlineData("hogehoge", false)]
144         [InlineData("foo.bar@example.com", true)]
145         [InlineData("foo..bar@example.com", false)]
146         [InlineData("foobar.@example.com", false)]
147         [InlineData("foo+bar@example.com", true)]
148         public void IsValidEmailTest(string email, bool expected)
149             => Assert.Equal(expected, MyCommon.IsValidEmail(email));
150
151         [Theory]
152         [InlineData(Keys.Shift, new[] { Keys.Shift }, true)]
153         [InlineData(Keys.Shift, new[] { Keys.Control }, false)]
154         [InlineData(Keys.Control | Keys.Alt, new[] { Keys.Control }, true)]
155         [InlineData(Keys.Control | Keys.Alt, new[] { Keys.Alt }, true)]
156         [InlineData(Keys.Control | Keys.Alt, new[] { Keys.Control, Keys.Alt }, true)]
157         [InlineData(Keys.Control | Keys.Alt, new[] { Keys.Shift }, false)]
158         public void IsKeyDownTest(Keys modifierKeys, Keys[] checkKeys, bool expected)
159             => Assert.Equal(expected, MyCommon.IsKeyDownInternal(modifierKeys, checkKeys));
160
161         [Fact]
162         public void GetAssemblyNameTest()
163         {
164             var mockAssembly = new Mock<_Assembly>();
165             mockAssembly.Setup(m => m.GetName()).Returns(new AssemblyName("OpenTween"));
166             MyCommon.EntryAssembly = mockAssembly.Object;
167
168             Assert.Equal("OpenTween", MyCommon.GetAssemblyName());
169         }
170
171         [Theory]
172         [InlineData("", "")]
173         [InlineData("%AppName%", "OpenTween")]
174         [InlineData("%AppName% %AppName%", "OpenTween OpenTween")]
175         public void ReplaceAppNameTest(string str, string excepted)
176             => Assert.Equal(excepted, MyCommon.ReplaceAppName(str, "OpenTween"));
177
178         [Theory]
179         [InlineData("1.0.0.0", "1.0.0")]
180         [InlineData("1.0.0.1", "1.0.1-dev")]
181         [InlineData("1.0.0.12", "1.0.1-dev+build.12")]
182         [InlineData("1.0.1.0", "1.0.1")]
183         [InlineData("1.0.9.1", "1.0.10-dev")]
184         [InlineData("1.1.0.0", "1.1.0")]
185         [InlineData("1.9.9.1", "1.9.10-dev")]
186         public void GetReadableVersionTest(string fileVersion, string expected)
187             => Assert.Equal(expected, MyCommon.GetReadableVersion(fileVersion));
188
189         public static readonly TheoryData<PostClass, string> GetStatusUrlTest1TestCase = new()
190         {
191             {
192                 new PostClass { StatusId = 249493863826350080L, ScreenName = "Favstar_LM", RetweetedId = null, RetweetedBy = null },
193                 "https://twitter.com/Favstar_LM/status/249493863826350080"
194             },
195             {
196                 new PostClass { StatusId = 216033842434289664L, ScreenName = "haru067", RetweetedId = 200245741443235840L, RetweetedBy = "re4k" },
197                 "https://twitter.com/haru067/status/200245741443235840"
198             },
199         };
200
201         [Theory]
202         [MemberData(nameof(GetStatusUrlTest1TestCase))]
203         public void GetStatusUrlTest1(PostClass post, string expected)
204             => Assert.Equal(expected, MyCommon.GetStatusUrl(post));
205
206         [Theory]
207         [InlineData("Favstar_LM", 249493863826350080L, "https://twitter.com/Favstar_LM/status/249493863826350080")]
208         [InlineData("haru067", 200245741443235840L, "https://twitter.com/haru067/status/200245741443235840")]
209         public void GetStatusUrlTest2(string screenName, long statusId, string expected)
210             => Assert.Equal(expected, MyCommon.GetStatusUrl(screenName, statusId));
211
212         [Fact]
213         public void GetErrorLogPathTest()
214         {
215             if (Environment.OSVersion.Platform == PlatformID.Win32NT)
216             {
217                 var mockAssembly = new Mock<_Assembly>();
218                 mockAssembly.Setup(m => m.Location).Returns(@"C:\hogehoge\OpenTween\OpenTween.exe");
219                 MyCommon.EntryAssembly = mockAssembly.Object;
220
221                 Assert.Equal(@"C:\hogehoge\OpenTween\ErrorLogs", MyCommon.GetErrorLogPath());
222             }
223             else
224             {
225                 var mockAssembly = new Mock<_Assembly>();
226                 mockAssembly.Setup(m => m.Location).Returns(@"/hogehoge/OpenTween/OpenTween.exe");
227                 MyCommon.EntryAssembly = mockAssembly.Object;
228
229                 Assert.Equal(@"/hogehoge/OpenTween/ErrorLogs", MyCommon.GetErrorLogPath());
230             }
231         }
232
233         [Fact]
234         public void CountUp_Test()
235         {
236             var actual = MyCommon.CountUp(from: 1, to: 5);
237
238             Assert.Equal(new[] { 1, 2, 3, 4, 5 }, actual);
239         }
240
241         [Fact]
242         public void CountUp_FromAndToAreEqualTest()
243         {
244             var actual = MyCommon.CountUp(from: 1, to: 1);
245
246             Assert.Equal(new[] { 1 }, actual);
247         }
248
249         [Fact]
250         public void CountUp_ToIsLessThanFromTest()
251         {
252             var actual = MyCommon.CountUp(from: 1, to: 0);
253
254             Assert.Empty(actual);
255         }
256
257         [Fact]
258         public void CountDown_Test()
259         {
260             var actual = MyCommon.CountDown(from: 5, to: 1);
261
262             Assert.Equal(new[] { 5, 4, 3, 2, 1 }, actual);
263         }
264
265         [Fact]
266         public void CountDown_FromAndToAreEqualTest()
267         {
268             var actual = MyCommon.CountDown(from: 5, to: 5);
269
270             Assert.Equal(new[] { 5 }, actual);
271         }
272
273         [Fact]
274         public void CountDown_ToIsGreaterThanFromTest()
275         {
276             var actual = MyCommon.CountDown(from: 5, to: 6);
277
278             Assert.Empty(actual);
279         }
280
281         [Fact]
282         public void CircularCountUp_Test()
283         {
284             var actual = MyCommon.CircularCountUp(length: 6, startIndex: 3);
285
286             Assert.Equal(new[] { 3, 4, 5, 0, 1, 2 }, actual);
287         }
288
289         [Fact]
290         public void CircularCountUp_StartFromZeroTest()
291         {
292             var actual = MyCommon.CircularCountUp(length: 6, startIndex: 0);
293
294             Assert.Equal(new[] { 0, 1, 2, 3, 4, 5 }, actual);
295         }
296
297         [Fact]
298         public void CircularCountDown_Test()
299         {
300             var actual = MyCommon.CircularCountDown(length: 6, startIndex: 3);
301
302             Assert.Equal(new[] { 3, 2, 1, 0, 5, 4 }, actual);
303         }
304
305         [Fact]
306         public void CircularCountDown_StartFromLastIndexTest()
307         {
308             var actual = MyCommon.CircularCountDown(length: 6, startIndex: 5);
309
310             Assert.Equal(new[] { 5, 4, 3, 2, 1, 0 }, actual);
311         }
312
313         [Fact]
314         public void CreateBrowserProcessStartInfo_BrowserPathTest()
315         {
316             var startInfo = MyCommon.CreateBrowserProcessStartInfo("C:\\browser.exe", "https://example.com/");
317             Assert.Equal("C:\\browser.exe", startInfo.FileName);
318             Assert.Equal("\"https://example.com/\"", startInfo.Arguments);
319             Assert.False(startInfo.UseShellExecute);
320         }
321
322         [Fact]
323         public void CreateBrowserProcessStartInfo_BrowserPathWithSpacesTest()
324         {
325             var startInfo = MyCommon.CreateBrowserProcessStartInfo("C:\\Program Files\\browser.exe", "https://example.com/");
326             Assert.Equal("C:\\Program Files\\browser.exe", startInfo.FileName);
327             Assert.Equal("\"https://example.com/\"", startInfo.Arguments);
328             Assert.False(startInfo.UseShellExecute);
329         }
330
331         [Fact]
332         public void CreateBrowserProcessStartInfo_QuotedBrowserPathTest()
333         {
334             var startInfo = MyCommon.CreateBrowserProcessStartInfo("\"C:\\Program Files\\browser.exe\"", "https://example.com/");
335             Assert.Equal("C:\\Program Files\\browser.exe", startInfo.FileName);
336             Assert.Equal("\"https://example.com/\"", startInfo.Arguments);
337             Assert.False(startInfo.UseShellExecute);
338         }
339
340         [Fact]
341         public void CreateBrowserProcessStartInfo_QuotedBrowserPathWithArgsTest()
342         {
343             var startInfo = MyCommon.CreateBrowserProcessStartInfo("\"C:\\Program Files\\browser.exe\" /hoge", "https://example.com/");
344             Assert.Equal("C:\\Program Files\\browser.exe", startInfo.FileName);
345             Assert.Equal("/hoge \"https://example.com/\"", startInfo.Arguments);
346             Assert.False(startInfo.UseShellExecute);
347         }
348
349         public static readonly TheoryData<int[], (int, int)[]> ToRangeChunkTestCase = new()
350         {
351             {
352                 new[] { 1 },
353                 new[] { (1, 1) }
354             },
355             {
356                 new[] { 1, 2 },
357                 new[] { (1, 2) }
358             },
359             {
360                 new[] { 1, 3 },
361                 new[] { (1, 1), (3, 3) }
362             },
363         };
364
365         [Theory]
366         [MemberData(nameof(ToRangeChunkTestCase))]
367         public void ToRangeChunk_Test(int[] values, (int Start, int End)[] expected)
368         {
369             Assert.Equal(expected, MyCommon.ToRangeChunk(values));
370         }
371     }
372 }