mirror of https://github.com/dotnet/runtime
Merge 5d3733bc05
into 02596ba8d9
This commit is contained in:
commit
76891ec859
|
@ -5,6 +5,7 @@
|
|||
</Folder>
|
||||
<Folder Name="/tests/">
|
||||
<Project Path="tests/Common.Tests.csproj" />
|
||||
<Project Path="tests/ComplianceTests/Compliance.Tests.csproj" />
|
||||
<Project Path="tests/StreamConformanceTests/StreamConformanceTests.csproj" />
|
||||
<Project Path="tests/TestUtilities/TestUtilities.csproj" />
|
||||
</Folder>
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>$(NetCoreAppCurrent);$(NetFrameworkCurrent)</TargetFrameworks>
|
||||
<Nullable>enable</Nullable>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<IncludeRemoteExecutor>true</IncludeRemoteExecutor>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Include="GB18030\TestHelper.cs" />
|
||||
<Compile Include="GB18030\Tests\ConsoleTests.cs" />
|
||||
<Compile Include="GB18030\Tests\DirectoryInfoTests.cs" />
|
||||
<Compile Include="GB18030\Tests\DirectoryTestBase.cs" />
|
||||
<Compile Include="GB18030\Tests\DirectoryTests.cs" />
|
||||
<Compile Include="GB18030\Tests\EncodingTests.cs" />
|
||||
<Compile Include="GB18030\Tests\FileInfoTests.cs" />
|
||||
<Compile Include="GB18030\Tests\FileTestBase.cs" />
|
||||
<Compile Include="GB18030\Tests\FileTests.cs" />
|
||||
<Compile Include="GB18030\Tests\StringTests.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="GB18030\Level3+Amendment_Test_Data_for_Mid_to_High_Volume_cases.txt">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -0,0 +1,45 @@
|
|||
Short sample strings for inputs with length limitation:
|
||||
龭唉𫓧G㐁A𫟦D𠳐ⅷ𫇭C丂𬣙𪛗⼀<EFBFBD>𱍐𰀀䶶㎎9𫜵荳鿰𬺰
|
||||
鬃𮚮Q鿃𫦠d㐇𬉼𪨰C鍲𫠊棗𪛘8g𱍓⿕𫜶C𰀇㎡䶷𠁠<EFBFBD>鿱
|
||||
麥𬴂c𮮐d堃𬣙𨙸αa苘𫠋龨䡫𫜷⽦<EFBFBD>𪛙𰀑G𱍝㏒䶸A鿲𪾢
|
||||
䒚d𫠆鿀𨓌遫𮟮C𫠜𫥹ǒ𪣻蜒𪛚𫜸<EFBFBD>㏄䶹⼵枛鿳s𱐺𰂼
|
||||
𫚭齅䶱5𮯠灋𬘭r𫟼蝌龯𪛒<EFBFBD>𪛛㊣𫜹⾢Z𱔟𫍲䶺𰆬a
|
||||
A龬𮧄〇r𫖳G𡿯𪢧隣𫞫崸𬺡O䶻𫜵⽭𪛜㒹𰇻馈<EFBFBD>𱖄鿵
|
||||
怐𪧂䶮𢃾鿂磷㘥N𫟰D𮓇𬸚㎏𫜶㈢⽥<EFBFBD>䶼𬺠鿶𪛝2𰉖𱘍
|
||||
霹褷㦬A𬹼鿁𫝦D扏𬒇6𪛞𢳂ǒ㏑⽆<EFBFBD>𫜷𰋵𱙁鿷𭸌G䶽𪴈
|
||||
蘏龿蔚P汘㰀O𥫔Ω𫇢v𫟄𬯎𰌸q𫜸⿃<EFBFBD>𪛟𬂪𱚃𭇋〇鿸䶾
|
||||
龩胤獫㰜𫚻0狛𫯪𬭳𫠓D𨰾㎝𫜹<EFBFBD>6𲎯𪛘𱍊⾁鿿䶿𬺲
|
||||
|
||||
Long sample strings for inputs with extended length limitation:
|
||||
Group0:
|
||||
鿀鿃啊齄𬉼𫚭狜﨩㐀䶵8e𠀀𪛖丂狛𫠠𬺡⿻〇𬺰𮯠𫝀𫠝rP𪛗𪛟<EFBFBD><EFBFBD><EFBFBD>U1<EFBFBD><EFBFBD><EFBFBD>U2<EFBFBD><EFBFBD><EFBFBD>U3𱍐𲎯①ㄩ𫜵𫜹⼀⿕鿰鿿䶶䶿𰀀𱍊𪜀𫜴6n
|
||||
|
||||
Group1:
|
||||
啊唉𬉼𠳐0u㐀㐇A𫝏𫝕狧龍丏丒丟𠀀𠀅ⅶǒ𪾢𫘝i𪛗𪛘龦龪𬺰𮯙𮨴8鿰鿱𫠠𫠣𫠥㎎㎡𫜵𫜶䶶䶷𰀀𰁒𰃋𰃌𱍐𱍑𱎄𱎅⼀⽴<EFBFBD><EFBFBD>U1<EFBFBD><EFBFBD>U2<EFBFBD><EFBFBD><EFBFBD>U3
|
||||
|
||||
Group2:
|
||||
癌隘鼾𫚭𫓯丣妺圵㐈㫒䀄狜珵𫓧𫄨𫓯8A𬺞𬷻𬶆𫍣𫛸𫖳FOC𱎒𱎓𱏱𱓃𠀈𠀉𤴠⒚k𪛙𪛚𫜷𫜸㏎鿲鿳⼈⽍𰃖𰄩𰄱𰆆𰆇𰆈<EFBFBD><EFBFBD><EFBFBD>U1<EFBFBD><EFBFBD><EFBFBD>U2<EFBFBD><EFBFBD><EFBFBD>U3䶸䶹𫠆𫟹𫞩𮨸𮡬𮓶
|
||||
|
||||
Group3:
|
||||
𫓧𬬮舰齄D屌囍囧垔囒珸珹䲟珺陫cz㩹㩽䀃𫟼𫟅𫠜ao鿀龬ɑQ𤴤𤴨𬸈𬴌㏄㏑⿻〇𫜹𫜵𪛛𪛜𮚸𮄄𰆊𰆋𰆌𰆍<EFBFBD><EFBFBD>U1<EFBFBD><EFBFBD><EFBFBD>U2<EFBFBD><EFBFBD><EFBFBD>U3𱐎𱐏𱑛𱑯𱗄E9z𫍯𫍲𫐐鿴鿵⽬⾒䶺䶻C
|
||||
|
||||
Group4:
|
||||
龫龭姜江疆蒋𬴂𫘦琀琂琇琌50R𫖮𫓶𫓹𨸠𪎑𪛒𫠊𫠆𫟹8U5㸰䒯䢀𬝄𬰼𬩹𪛝𪛞㏒﹫𫜸𫜹8U5𮄃𭾁𮇏<EFBFBD><EFBFBD><EFBFBD>U1<EFBFBD><EFBFBD><EFBFBD>U2<EFBFBD><EFBFBD><EFBFBD>U3鿶鿷⾔⾧䶼䶽Ⅷ4ξ𰉣𰉩𰎙𰐼𱘕𱖬𱙼𱙵凃喛囘
|
||||
|
||||
Group5:
|
||||
纮肣繤魴𫠆𨐈龲龳䦼䦽䦾8b亘兀丐廿𪨊𪞝b𫠆𫞩𫠜怋怐怓怗𭮂𭦬𱚹𱛢𱪥𱜢鿸鿹𪛓𩚳∨∪𪛟𪛗䶾䶿⾨⾲𫜵𫜷<EFBFBD><EFBFBD><EFBFBD><EFBFBD>U1<EFBFBD><EFBFBD><EFBFBD>U2<EFBFBD><EFBFBD><EFBFBD>U3𬩫𬬪㎝⿻〇𰑇𰑈58b
|
||||
|
||||
Group6:
|
||||
䶭㑆㢶龼龮孬噩澧澹𪎄𪎇GΩ羓缽翋豖𫌀𫖳𫘪𫚕0wL𭡆𭨬𫟅𫞩𫟦𰠄𰠅𰡪𰤌𬢝𬢙𫜸𫜶𱝋𱝌𱝍𱝎𱝏A鿺鿻𫄧𪟝<EFBFBD><EFBFBD><EFBFBD><EFBFBD>U1<EFBFBD><EFBFBD><EFBFBD>U2<EFBFBD><EFBFBD><EFBFBD>U3䶶䶹㏕〒𪛘𪛚g9⾽⿅怘怞怢怣妢
|
||||
|
||||
Group7:
|
||||
龫龯澶濂濡濞濠瀚𫔶𫌀pW5㿻㧜㗝䦸氿崑汈汋嫾𪩘𫗴𫔍𫄸@pU䀅𤴩𤴡𤴣ěγt㎎㎞𫞩𫟦𫠜𭁾𭁽𬺳馬鴻齊镸閉𪛛𪛜e𫜸𫜷𱝻𱝼𱠺𱡟鿲鿺䶷䶼⿆⿌𰧬𰧭𰧮𰩎𰪥𰪦𬚺𬘐𬖱<F0AC9890><F0AC96B1>U1<55><31><EFBFBD>U2<55><32><EFBFBD>U3
|
||||
|
||||
Group8:
|
||||
窣甪療裏闫囡猬嗦𩾃𬭯龾龽𫍣𫑡yG㚉㟰㕩㓺A𠃉𠃊Ⅱ9AZ𫟹𫟼𫟦𫠜y0R汎汑汒汖狆𬓮𬓯𬔙𬔵㎎㏒𪛘𪛟𫜸𫜷鿼鿽<EFBFBD><EFBFBD><EFBFBD><EFBFBD>U1<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>U2<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>U3䶾䶿𬺹𬺺𬺻y0R𰮤𰯴𰰛𰰜𰱺𱡦𱡻𱤽𱥮𱧓⿋⾿
|
||||
|
||||
Group9:
|
||||
狇狉狑狓㝎㝒㝓Qa隣﨤﨧A3c鿃龿黩黧黥黯鼢𠗥𠕤𠔌𦈡𫄸𫠊𫞩𫟦𫠜V9𬻱𬻴𬻵㏑﹫𫜸𫜹𰸛𰹇𰹭𰺨𰻦Xβě𪛟𪛛𱯄𱪡𱫦𱫧𱮴𪾢𪟝𫐄𫘜m0⾣⾋鿾鿿𬓭𬅝<EFBFBD><EFBFBD><EFBFBD>U1<EFBFBD><EFBFBD><EFBFBD>U2<EFBFBD><EFBFBD><EFBFBD>U3䶶䶷
|
||||
|
||||
Group10:
|
||||
鿁鿂鼬鼯鼹鼷𬕂𡎚㖞䎱䲣狽䴙䀅㩶㪷a4𬅁𫽵𫶟𫟼𫟅𫟦𫠜𤴩𤴪𤴣𤴫g⑶ū︔𫜶𫓯𫘦𫐐䶼䶿鿱鿽𭀛𭀜𭀞㎎﹦𪛝𪛟牘狕狖狘丗𲎪𲎫𲎬𲎭𲎮𲎯𱍆𱍇𱍈𱍉𱍊⽏⽐<EFBFBD><EFBFBD><EFBFBD>U1<EFBFBD><EFBFBD><EFBFBD><EFBFBD>U2<EFBFBD><EFBFBD><EFBFBD>U3iv
|
|
@ -0,0 +1,96 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace GB18030.Tests;
|
||||
|
||||
public static class TestHelper
|
||||
{
|
||||
internal static CultureInfo[] s_cultureInfos = [
|
||||
CultureInfo.CurrentCulture,
|
||||
CultureInfo.InvariantCulture,
|
||||
new CultureInfo("zh-CN")];
|
||||
|
||||
internal static CompareOptions[] s_compareOptions = [
|
||||
CompareOptions.None,
|
||||
CompareOptions.IgnoreCase,
|
||||
CompareOptions.Ordinal,
|
||||
CompareOptions.OrdinalIgnoreCase];
|
||||
|
||||
internal static readonly StringComparison[] s_ordinalStringComparisons = [
|
||||
StringComparison.Ordinal,
|
||||
StringComparison.OrdinalIgnoreCase];
|
||||
|
||||
internal static readonly StringComparison[] s_nonOrdinalStringComparisons = [
|
||||
StringComparison.CurrentCulture,
|
||||
StringComparison.CurrentCultureIgnoreCase,
|
||||
StringComparison.InvariantCulture,
|
||||
StringComparison.InvariantCultureIgnoreCase];
|
||||
|
||||
internal static readonly StringComparison[] s_allStringComparisons = [
|
||||
.. s_ordinalStringComparisons,
|
||||
.. s_nonOrdinalStringComparisons];
|
||||
|
||||
internal static string s_testDataFilePath = Path.Combine(AppContext.BaseDirectory, "GB18030", "Level3+Amendment_Test_Data_for_Mid_to_High_Volume_cases.txt");
|
||||
|
||||
private static Encoding? s_gb18030Encoding;
|
||||
internal static Encoding GB18030Encoding
|
||||
{
|
||||
get
|
||||
{
|
||||
#if !NETFRAMEWORK
|
||||
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
|
||||
#endif
|
||||
return s_gb18030Encoding ??= Encoding.GetEncoding("gb18030");
|
||||
}
|
||||
}
|
||||
|
||||
private static readonly IEnumerable<byte[]> s_encodedTestData = GetTestData();
|
||||
internal static readonly IEnumerable<string> s_decodedTestData = s_encodedTestData.Select(data => GB18030Encoding.GetString(data));
|
||||
internal static readonly IEnumerable<string> s_splitNewLineDecodedTestData = s_decodedTestData.SelectMany(
|
||||
data => data.Split([Environment.NewLine], StringSplitOptions.RemoveEmptyEntries));
|
||||
|
||||
public static IEnumerable<object[]> EncodedTestData { get; } = s_encodedTestData.Select(data => new object[] { data });
|
||||
public static IEnumerable<object[]> DecodedTestData { get; } = s_decodedTestData.Select(data => new object[] { data });
|
||||
public static IEnumerable<object[]> SplitNewLineDecodedTestData { get; } = s_splitNewLineDecodedTestData.Select(data => new object[] { data });
|
||||
|
||||
private static IEnumerable<byte[]> GetTestData()
|
||||
{
|
||||
byte[] startDelimiter = GB18030Encoding.GetBytes($":{Environment.NewLine}");
|
||||
byte[] endDelimiter = GB18030Encoding.GetBytes($"{Environment.NewLine}{Environment.NewLine}");
|
||||
|
||||
// Instead of inlining the data in source, parse the test data from the file to prevent encoding issues.
|
||||
ReadOnlyMemory<byte> testFileBytes = File.ReadAllBytes(s_testDataFilePath);
|
||||
|
||||
while (testFileBytes.Length > 0)
|
||||
{
|
||||
int start = testFileBytes.Span.IndexOf(startDelimiter);
|
||||
testFileBytes = testFileBytes.Slice(start + startDelimiter.Length);
|
||||
|
||||
int end = testFileBytes.Span.IndexOf(endDelimiter);
|
||||
if (end == -1)
|
||||
end = testFileBytes.Length;
|
||||
|
||||
yield return testFileBytes.Slice(0, end).ToArray();
|
||||
|
||||
testFileBytes = testFileBytes.Slice(end);
|
||||
}
|
||||
|
||||
// Add a few additional test cases to exercise test correctness.
|
||||
yield return GB18030Encoding.GetBytes("aaa");
|
||||
yield return GB18030Encoding.GetBytes("abc");
|
||||
yield return GB18030Encoding.GetBytes("𫓧𫓧");
|
||||
}
|
||||
|
||||
internal static IEnumerable<string> GetTextElements(string input)
|
||||
{
|
||||
TextElementEnumerator enumerator = StringInfo.GetTextElementEnumerator(input);
|
||||
while (enumerator.MoveNext())
|
||||
{
|
||||
yield return enumerator.GetTextElement();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
using System;
|
||||
using Microsoft.DotNet.RemoteExecutor;
|
||||
using Xunit;
|
||||
|
||||
namespace GB18030.Tests;
|
||||
|
||||
public class ConsoleTests
|
||||
{
|
||||
[Theory]
|
||||
[MemberData(nameof(TestHelper.DecodedTestData), MemberType = typeof(TestHelper))]
|
||||
public void StandardOutput(string decodedText)
|
||||
{
|
||||
var remoteOptions = new RemoteInvokeOptions();
|
||||
remoteOptions.StartInfo.RedirectStandardOutput = true;
|
||||
remoteOptions.StartInfo.StandardOutputEncoding = TestHelper.GB18030Encoding;
|
||||
|
||||
using RemoteInvokeHandle remoteHandle = RemoteExecutor.Invoke(line =>
|
||||
{
|
||||
Console.OutputEncoding = TestHelper.GB18030Encoding;
|
||||
Console.Write(line);
|
||||
|
||||
return 42;
|
||||
}, decodedText, remoteOptions);
|
||||
|
||||
|
||||
Assert.Equal(decodedText, remoteHandle.Process.StandardOutput.ReadToEnd());
|
||||
Assert.True(remoteHandle.Process.WaitForExit(5_000));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(TestHelper.DecodedTestData), MemberType = typeof(TestHelper))]
|
||||
public void StandardInput(string decodedText)
|
||||
{
|
||||
var remoteOptions = new RemoteInvokeOptions();
|
||||
remoteOptions.StartInfo.RedirectStandardInput = true;
|
||||
#if !NETFRAMEWORK
|
||||
remoteOptions.StartInfo.StandardInputEncoding = TestHelper.GB18030Encoding;
|
||||
#endif
|
||||
|
||||
using RemoteInvokeHandle remoteHandle = RemoteExecutor.Invoke(line =>
|
||||
{
|
||||
Console.InputEncoding = TestHelper.GB18030Encoding;
|
||||
Assert.Equal(line, Console.In.ReadToEnd());
|
||||
|
||||
return 42;
|
||||
}, decodedText, remoteOptions);
|
||||
|
||||
if (PlatformDetection.IsNetFramework)
|
||||
{
|
||||
// there's no StandardInputEncoding in .NET Framework, re-encode and write.
|
||||
byte[] encoded = TestHelper.GB18030Encoding.GetBytes(decodedText);
|
||||
remoteHandle.Process.StandardInput.BaseStream.Write(encoded, 0, encoded.Length);
|
||||
remoteHandle.Process.StandardInput.Close();
|
||||
}
|
||||
else
|
||||
{
|
||||
remoteHandle.Process.StandardInput.Write(decodedText);
|
||||
remoteHandle.Process.StandardInput.Close();
|
||||
}
|
||||
|
||||
Assert.True(remoteHandle.Process.WaitForExit(5_000));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(TestHelper.DecodedTestData), MemberType = typeof(TestHelper))]
|
||||
public void StandardError(string decodedText)
|
||||
{
|
||||
var remoteOptions = new RemoteInvokeOptions();
|
||||
remoteOptions.StartInfo.RedirectStandardError = true;
|
||||
remoteOptions.StartInfo.StandardErrorEncoding = TestHelper.GB18030Encoding;
|
||||
|
||||
using RemoteInvokeHandle remoteHandle = RemoteExecutor.Invoke(line =>
|
||||
{
|
||||
Console.OutputEncoding = TestHelper.GB18030Encoding;
|
||||
Console.Error.Write(line);
|
||||
|
||||
return 42;
|
||||
}, decodedText, remoteOptions);
|
||||
|
||||
|
||||
Assert.Equal(decodedText, remoteHandle.Process.StandardError.ReadToEnd());
|
||||
Assert.True(remoteHandle.Process.WaitForExit(5_000));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Xunit;
|
||||
|
||||
namespace GB18030.Tests;
|
||||
|
||||
public class DirectoryInfoTests : DirectoryTestBase
|
||||
{
|
||||
protected override void CreateDirectory(string path) => new DirectoryInfo(path).Create();
|
||||
protected override void DeleteDirectory(string path, bool recursive) => new DirectoryInfo(path).Delete(recursive);
|
||||
protected override void MoveDirectory(string source, string destination) => new DirectoryInfo(source).MoveTo(destination);
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(TestHelper.DecodedTestData), MemberType = typeof(TestHelper))]
|
||||
public void CreateSubdirectory(string decoded)
|
||||
{
|
||||
foreach (string gb18030Line in decoded.Split([Environment.NewLine], StringSplitOptions.None))
|
||||
{
|
||||
var subDirInfo = TempDirectory.CreateSubdirectory(gb18030Line);
|
||||
|
||||
Assert.True(subDirInfo.Exists);
|
||||
Assert.Equal(gb18030Line, subDirInfo.Name);
|
||||
Assert.Equal(Path.Combine(TempDirectory.FullName, gb18030Line), subDirInfo.FullName);
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(TestHelper.DecodedTestData), MemberType = typeof(TestHelper))]
|
||||
public void EnumerateFileSystemInfos(string decoded)
|
||||
{
|
||||
string rootDir = TempDirectory.FullName;
|
||||
List<FileSystemInfo> expected = [];
|
||||
|
||||
foreach (string gb18030Line in decoded.Split([Environment.NewLine], StringSplitOptions.None))
|
||||
{
|
||||
string gb18030Dir = Path.Combine(rootDir, gb18030Line);
|
||||
var dirInfo = new DirectoryInfo(gb18030Dir);
|
||||
dirInfo.Create();
|
||||
expected.Add(dirInfo);
|
||||
|
||||
string gb18030File = Path.Combine(rootDir, gb18030Line + ".txt");
|
||||
var fileInfo = new FileInfo(gb18030File);
|
||||
fileInfo.Create().Dispose();
|
||||
expected.Add(fileInfo);
|
||||
}
|
||||
|
||||
Assert.Equivalent(expected, new DirectoryInfo(rootDir).EnumerateFileSystemInfos());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Xunit;
|
||||
|
||||
namespace GB18030.Tests;
|
||||
|
||||
public abstract class DirectoryTestBase : IDisposable
|
||||
{
|
||||
protected abstract void CreateDirectory(string path);
|
||||
protected abstract void DeleteDirectory(string path, bool recursive);
|
||||
protected abstract void MoveDirectory(string source, string destination);
|
||||
|
||||
protected DirectoryInfo TempDirectory { get; }
|
||||
|
||||
public DirectoryTestBase()
|
||||
{
|
||||
TempDirectory = Directory.CreateDirectory(Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(TestHelper.SplitNewLineDecodedTestData), MemberType = typeof(TestHelper))]
|
||||
public void Create(string gb18030Line)
|
||||
{
|
||||
string gb18030Path = Path.Combine(TempDirectory.FullName, gb18030Line);
|
||||
CreateDirectory(gb18030Path);
|
||||
|
||||
var dirInfo = new DirectoryInfo(gb18030Path);
|
||||
Assert.True(dirInfo.Exists);
|
||||
Assert.Equal(gb18030Line, dirInfo.Name);
|
||||
}
|
||||
|
||||
public static IEnumerable<object[]> Delete_TestData() =>
|
||||
new int[] { 0, 2, 8 }.SelectMany(recurseLevel =>
|
||||
TestHelper.s_splitNewLineDecodedTestData.Select(testData => new object[] { recurseLevel, testData }));
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(Delete_TestData))]
|
||||
public void Delete(int recurseLevel, string gb18030Line)
|
||||
{
|
||||
string firstPath = Path.Combine(TempDirectory.FullName, gb18030Line);
|
||||
string nestedDirPath = Path.Combine(firstPath, Path.Combine(Enumerable.Repeat(gb18030Line, recurseLevel).ToArray()));
|
||||
Assert.True(recurseLevel > 0 || firstPath.Equals(nestedDirPath));
|
||||
|
||||
Directory.CreateDirectory(nestedDirPath);
|
||||
Assert.True(Directory.Exists(nestedDirPath));
|
||||
|
||||
DeleteDirectory(firstPath, recursive: recurseLevel > 0);
|
||||
|
||||
Assert.False(Directory.Exists(firstPath));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(TestHelper.SplitNewLineDecodedTestData), MemberType = typeof(TestHelper))]
|
||||
public void Move(string gb18030Line)
|
||||
{
|
||||
string gb18030Path = Path.Combine(TempDirectory.FullName, gb18030Line);
|
||||
Directory.CreateDirectory(gb18030Path);
|
||||
Assert.True(Directory.Exists(gb18030Path));
|
||||
|
||||
string newPath = Path.Combine(TempDirectory.FullName, Path.GetRandomFileName());
|
||||
MoveDirectory(gb18030Path, newPath);
|
||||
Assert.True(Directory.Exists(newPath));
|
||||
Assert.False(Directory.Exists(gb18030Path));
|
||||
|
||||
MoveDirectory(newPath, gb18030Path);
|
||||
Assert.True(Directory.Exists(gb18030Path));
|
||||
Assert.False(Directory.Exists(newPath));
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
TempDirectory.Delete(true);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Xunit;
|
||||
|
||||
namespace GB18030.Tests;
|
||||
|
||||
public class DirectoryTests : DirectoryTestBase
|
||||
{
|
||||
protected override void CreateDirectory(string path) => Directory.CreateDirectory(path);
|
||||
protected override void DeleteDirectory(string path, bool recursive) => Directory.Delete(path, recursive);
|
||||
protected override void MoveDirectory(string source, string destination) => Directory.Move(source, destination);
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(TestHelper.DecodedTestData), MemberType = typeof(TestHelper))]
|
||||
public void EnumerateFileSystemEntries(string decoded)
|
||||
{
|
||||
string rootDir = TempDirectory.FullName;
|
||||
List<string> expected = [];
|
||||
|
||||
foreach (string gb18030Line in decoded.Split([Environment.NewLine], StringSplitOptions.None))
|
||||
{
|
||||
string gb18030Dir = Path.Combine(rootDir, gb18030Line);
|
||||
Directory.CreateDirectory(gb18030Dir);
|
||||
expected.Add(gb18030Dir);
|
||||
|
||||
string gb18030File = Path.Combine(rootDir, gb18030Line + ".txt");
|
||||
File.Create(gb18030File).Dispose();
|
||||
expected.Add(gb18030File);
|
||||
}
|
||||
|
||||
Assert.Equivalent(expected, Directory.EnumerateFileSystemEntries(rootDir));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
using System;
|
||||
using Xunit;
|
||||
|
||||
namespace GB18030.Tests;
|
||||
|
||||
public class EncodingTests
|
||||
{
|
||||
[Theory]
|
||||
[MemberData(nameof(TestHelper.EncodedTestData), MemberType = typeof(TestHelper))]
|
||||
public void Roundtrips(byte[] testData)
|
||||
{
|
||||
Assert.True(testData.AsSpan().SequenceEqual(
|
||||
TestHelper.GB18030Encoding.GetBytes(TestHelper.GB18030Encoding.GetString(testData))));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
using System.IO;
|
||||
|
||||
namespace GB18030.Tests;
|
||||
|
||||
public class FileInfoTests : FileTestBase
|
||||
{
|
||||
protected override void CreateFile(string path) => new FileInfo(path).Create().Dispose();
|
||||
protected override void DeleteFile(string path) => new FileInfo(path).Delete();
|
||||
protected override void MoveFile(string source, string destination) => new FileInfo(source).MoveTo(destination);
|
||||
protected override void CopyFile(string source, string destination) => new FileInfo(source).CopyTo(destination);
|
||||
}
|
|
@ -0,0 +1,87 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using Xunit;
|
||||
|
||||
namespace GB18030.Tests;
|
||||
|
||||
public abstract class FileTestBase : IDisposable
|
||||
{
|
||||
protected abstract void CreateFile(string path);
|
||||
protected abstract void DeleteFile(string path);
|
||||
protected abstract void MoveFile(string source, string destination);
|
||||
protected abstract void CopyFile(string source, string destination);
|
||||
|
||||
protected DirectoryInfo TempDirectory { get; }
|
||||
|
||||
public FileTestBase()
|
||||
{
|
||||
TempDirectory = Directory.CreateDirectory(Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()));
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
TempDirectory.Delete(true);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(TestHelper.SplitNewLineDecodedTestData), MemberType = typeof(TestHelper))]
|
||||
public void Create(string gb18030Line)
|
||||
{
|
||||
string gb18030Path = Path.Combine(TempDirectory.FullName, gb18030Line);
|
||||
CreateFile(gb18030Path);
|
||||
|
||||
var fileInfo = new FileInfo(gb18030Path);
|
||||
Assert.True(fileInfo.Exists);
|
||||
Assert.Equal(fileInfo.Name, gb18030Line);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(TestHelper.SplitNewLineDecodedTestData), MemberType = typeof(TestHelper))]
|
||||
public void Delete(string gb18030Line)
|
||||
{
|
||||
string gb18030Path = Path.Combine(TempDirectory.FullName, gb18030Line);
|
||||
File.Create(gb18030Path).Dispose();
|
||||
Assert.True(File.Exists(gb18030Path));
|
||||
|
||||
DeleteFile(gb18030Path);
|
||||
|
||||
Assert.False(File.Exists(gb18030Path));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(TestHelper.SplitNewLineDecodedTestData), MemberType = typeof(TestHelper))]
|
||||
public void Move(string gb18030Line)
|
||||
{
|
||||
string gb18030Path = Path.Combine(TempDirectory.FullName, gb18030Line);
|
||||
File.Create(gb18030Path).Dispose();
|
||||
|
||||
string newPath = Path.Combine(TempDirectory.FullName, Path.GetRandomFileName());
|
||||
MoveFile(gb18030Path, newPath);
|
||||
Assert.True(File.Exists(newPath));
|
||||
Assert.False(File.Exists(gb18030Path));
|
||||
|
||||
File.Move(newPath, gb18030Path);
|
||||
Assert.True(File.Exists(gb18030Path));
|
||||
Assert.False(File.Exists(newPath));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(TestHelper.SplitNewLineDecodedTestData), MemberType = typeof(TestHelper))]
|
||||
public void Copy(string gb18030Line)
|
||||
{
|
||||
ReadOnlySpan<byte> sampleContent = "File_Copy"u8;
|
||||
string gb18030Path = Path.Combine(TempDirectory.FullName, gb18030Line);
|
||||
File.WriteAllBytes(gb18030Path, sampleContent.ToArray());
|
||||
|
||||
string newPath = Path.Combine(TempDirectory.FullName, Path.GetRandomFileName());
|
||||
CopyFile(gb18030Path, newPath);
|
||||
Assert.True(File.Exists(newPath));
|
||||
|
||||
File.Delete(gb18030Path);
|
||||
Assert.False(File.Exists(gb18030Path));
|
||||
|
||||
CopyFile(newPath, gb18030Path);
|
||||
Assert.True(File.Exists(gb18030Path));
|
||||
Assert.True(sampleContent.SequenceEqual(File.ReadAllBytes(gb18030Path)));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Xunit;
|
||||
|
||||
namespace GB18030.Tests;
|
||||
|
||||
public class FileTests : FileTestBase
|
||||
{
|
||||
private static readonly byte[] s_expectedBytes = File.ReadAllBytes(TestHelper.s_testDataFilePath);
|
||||
private static readonly string s_expectedText = TestHelper.GB18030Encoding.GetString(s_expectedBytes);
|
||||
|
||||
protected override void CreateFile(string path) => File.Create(path).Dispose();
|
||||
protected override void DeleteFile(string path) => File.Delete(path);
|
||||
protected override void MoveFile(string source, string destination) => File.Move(source, destination);
|
||||
protected override void CopyFile(string source, string destination) => File.Copy(source, destination);
|
||||
|
||||
[Fact]
|
||||
public void ReadAllText()
|
||||
{
|
||||
Assert.Equal(s_expectedText, File.ReadAllText(TestHelper.s_testDataFilePath, TestHelper.GB18030Encoding));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadAllLines()
|
||||
{
|
||||
Assert.Equal(
|
||||
s_expectedText.Split([Environment.NewLine], StringSplitOptions.None),
|
||||
File.ReadAllLines(TestHelper.s_testDataFilePath, TestHelper.GB18030Encoding));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteAllText()
|
||||
{
|
||||
string tempFile = Path.Combine(TempDirectory.FullName, Path.GetRandomFileName());
|
||||
File.WriteAllText(tempFile, s_expectedText, TestHelper.GB18030Encoding);
|
||||
|
||||
Assert.True(s_expectedBytes.AsSpan().SequenceEqual(File.ReadAllBytes(tempFile)));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteAllLines()
|
||||
{
|
||||
string tempFile = Path.Combine(TempDirectory.FullName, Path.GetRandomFileName());
|
||||
string[] lines = s_expectedText.Split([Environment.NewLine], StringSplitOptions.None);
|
||||
File.WriteAllLines(tempFile, lines, TestHelper.GB18030Encoding);
|
||||
|
||||
// WriteAllLines uses TextWriter.WriteLine which concats a newline to each provided line,
|
||||
// the result is the expected text with an additional newline at the end.
|
||||
byte[] expected = TestHelper.GB18030Encoding.GetBytes(s_expectedText + Environment.NewLine);
|
||||
Assert.True(expected.AsSpan().SequenceEqual(File.ReadAllBytes(tempFile)));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AppendAllText()
|
||||
{
|
||||
string tempFile = Path.Combine(TempDirectory.FullName, Path.GetRandomFileName());
|
||||
const string initialContent = "Initial content: ";
|
||||
File.WriteAllText(tempFile, initialContent, TestHelper.GB18030Encoding);
|
||||
File.AppendAllText(tempFile, s_expectedText, TestHelper.GB18030Encoding);
|
||||
|
||||
byte[] expected = TestHelper.GB18030Encoding.GetBytes(initialContent + s_expectedText);
|
||||
Assert.True(expected.AsSpan().SequenceEqual(File.ReadAllBytes(tempFile)));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,405 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using Xunit;
|
||||
#pragma warning disable xUnit2009 // Do not use boolean check to check for substrings
|
||||
#pragma warning disable xUnit2010 // Do not use boolean check to check for string equality
|
||||
|
||||
namespace GB18030.Tests;
|
||||
|
||||
public class StringTests
|
||||
{
|
||||
private const string Dummy = "\uFFFF";
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(TestHelper.EncodedTestData), MemberType = typeof(TestHelper))]
|
||||
public unsafe void Ctor(byte[] encoded)
|
||||
{
|
||||
fixed (sbyte* p = (sbyte[])(object)encoded)
|
||||
{
|
||||
string s = new string(p, 0, encoded.Length, TestHelper.GB18030Encoding);
|
||||
Assert.True(encoded.AsSpan().SequenceEqual(TestHelper.GB18030Encoding.GetBytes(s)));
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<object[]> Compare_TestData() =>
|
||||
TestHelper.s_cultureInfos.SelectMany(culture =>
|
||||
TestHelper.s_compareOptions.SelectMany(option =>
|
||||
TestHelper.s_decodedTestData.Select(testData => new object[] { culture, option, testData })));
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(Compare_TestData))]
|
||||
public void Compare(CultureInfo culture, CompareOptions option, string decoded)
|
||||
{
|
||||
#pragma warning disable 0618 // suppress obsolete warning for String.Copy
|
||||
string copy = string.Copy(decoded);
|
||||
#pragma warning restore 0618
|
||||
Assert.True(string.Compare(decoded, copy, culture, option) == 0);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(TestHelper.DecodedTestData), MemberType = typeof(TestHelper))]
|
||||
public void Contains(string decoded)
|
||||
{
|
||||
for (int i = 0; i < decoded.Length; i++)
|
||||
Assert.True(decoded.Contains(decoded.Substring(0, i)));
|
||||
|
||||
for (int i = decoded.Length - 1; i >= 0; i--)
|
||||
Assert.True(decoded.Contains(decoded.Substring(i)));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(TestHelper.DecodedTestData), MemberType = typeof(TestHelper))]
|
||||
public void String_Equals(string decoded)
|
||||
{
|
||||
#pragma warning disable 0618 // suppress obsolete warning for String.Copy
|
||||
string copy = string.Copy(decoded);
|
||||
#pragma warning restore 0618
|
||||
Assert.True(decoded.Equals(decoded));
|
||||
Assert.True(decoded.Equals(copy));
|
||||
Assert.True(decoded.Equals((object)copy));
|
||||
Assert.True(string.Equals(decoded, copy));
|
||||
|
||||
Assert.False(decoded.Equals(copy + Dummy));
|
||||
Assert.False(decoded.Equals(Dummy + copy));
|
||||
Assert.False(decoded.Equals(copy.Substring(0, copy.Length / 2) + Dummy + copy.Substring(copy.Length / 2)));
|
||||
Assert.False(decoded.Equals(null));
|
||||
}
|
||||
|
||||
public static IEnumerable<object[]> StringComparison_TestData() =>
|
||||
TestHelper.s_allStringComparisons.SelectMany(comparison =>
|
||||
TestHelper.s_decodedTestData.Select(decoded => new object[] { comparison, decoded }));
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(StringComparison_TestData))]
|
||||
public void String_Equals_StringComparison(StringComparison comparison, string decoded)
|
||||
{
|
||||
#pragma warning disable 0618 // suppress obsolete warning for String.Copy
|
||||
string copy = string.Copy(decoded);
|
||||
#pragma warning restore 0618
|
||||
if ((int)comparison % 2 != 0) // Odd values are *IgnoreCase
|
||||
{
|
||||
Assert.True(decoded.ToLower().Equals(copy.ToUpper(), comparison));
|
||||
Assert.True(string.Equals(decoded.ToUpper(), copy.ToLower(), comparison));
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.True(decoded.Equals(copy, comparison));
|
||||
Assert.True(string.Equals(decoded, copy, comparison));
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<object[]> EndsStartsWith_TestData() =>
|
||||
TestHelper.s_cultureInfos.SelectMany(culture =>
|
||||
new bool[] { true, false }.SelectMany(ignoreCase =>
|
||||
TestHelper.s_decodedTestData.Select(testData => new object[] { culture, ignoreCase, testData })));
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(EndsStartsWith_TestData))]
|
||||
public void EndsWith(CultureInfo culture, bool ignoreCase, string decoded)
|
||||
{
|
||||
string suffix = string.Empty;
|
||||
foreach (string textElement in TestHelper.GetTextElements(decoded).Reverse())
|
||||
{
|
||||
suffix = textElement + suffix;
|
||||
if (ignoreCase)
|
||||
Assert.True(decoded.ToUpper().EndsWith(suffix.ToLower(), ignoreCase, culture));
|
||||
else
|
||||
Assert.True(decoded.EndsWith(suffix, ignoreCase, culture));
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<object[]> EndsStartsWith_Ordinal_TestData() =>
|
||||
new StringComparison[]
|
||||
{
|
||||
StringComparison.Ordinal,
|
||||
StringComparison.OrdinalIgnoreCase
|
||||
}
|
||||
.SelectMany(culture =>
|
||||
TestHelper.s_decodedTestData.Select(testData => new object[] { culture, testData }));
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(EndsStartsWith_Ordinal_TestData))]
|
||||
public void EndsWith_Ordinal(StringComparison comparison, string decoded)
|
||||
{
|
||||
for (int i = decoded.Length - 1; i >= 0; i--)
|
||||
{
|
||||
if (comparison == StringComparison.OrdinalIgnoreCase)
|
||||
Assert.True(decoded.ToLower().EndsWith(decoded.Substring(i).ToUpper(), comparison));
|
||||
else
|
||||
Assert.True(decoded.EndsWith(decoded.Substring(i), comparison));
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(EndsStartsWith_TestData))]
|
||||
public void StartsWith(CultureInfo culture, bool ignoreCase, string decoded)
|
||||
{
|
||||
string prefix = string.Empty;
|
||||
foreach (string textElement in TestHelper.GetTextElements(decoded))
|
||||
{
|
||||
prefix += textElement;
|
||||
if (ignoreCase)
|
||||
Assert.True(decoded.ToUpper().StartsWith(prefix.ToLower(), ignoreCase, culture));
|
||||
else
|
||||
Assert.True(decoded.StartsWith(prefix, ignoreCase, culture));
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(EndsStartsWith_Ordinal_TestData))]
|
||||
public void StartsWith_Ordinal(StringComparison comparison, string decoded)
|
||||
{
|
||||
for (int i = 0; i < decoded.Length; i++)
|
||||
{
|
||||
if (comparison == StringComparison.OrdinalIgnoreCase)
|
||||
Assert.True(decoded.ToLower().StartsWith(decoded.Substring(0, i).ToUpper(), comparison));
|
||||
else
|
||||
Assert.True(decoded.StartsWith(decoded.Substring(0, i), comparison));
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(StringComparison_TestData))]
|
||||
public void IndexOf_MultipleElements(StringComparison comparison, string decoded)
|
||||
{
|
||||
bool ignoreCase = (int)comparison % 2 != 0;
|
||||
if (comparison is StringComparison.Ordinal or StringComparison.OrdinalIgnoreCase)
|
||||
{
|
||||
for (int i = 0; i < decoded.Length; i++)
|
||||
{
|
||||
string left = decoded.Substring(0, i);
|
||||
string right = decoded.Substring(i);
|
||||
Assert.Equal(decoded.Length, left.Length + right.Length);
|
||||
|
||||
Assert.Equal(0, decoded.IndexOf(left, comparison));
|
||||
// right substring starts at the end of left one.
|
||||
Assert.Equal(left.Length, decoded.IndexOf(right, startIndex: left.Length, comparison));
|
||||
|
||||
if (ignoreCase)
|
||||
{
|
||||
Assert.Equal(0, decoded.ToLower().IndexOf(left.ToUpper(), comparison));
|
||||
Assert.Equal(left.Length, decoded.ToLower().IndexOf(right.ToUpper(), startIndex: left.Length, comparison));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
string[] textElements = TestHelper.GetTextElements(decoded).ToArray();
|
||||
for (int i = 0; i < textElements.Length; i++)
|
||||
{
|
||||
string left = string.Concat(textElements.Take(i));
|
||||
string right = string.Concat(textElements.Skip(i));
|
||||
Assert.Equal(decoded.Length, left.Length + right.Length);
|
||||
|
||||
Assert.Equal(0, decoded.IndexOf(left, comparison));
|
||||
Assert.Equal(left.Length, decoded.IndexOf(right, startIndex: left.Length, comparison));
|
||||
|
||||
if (ignoreCase)
|
||||
{
|
||||
Assert.Equal(0, decoded.ToLower().IndexOf(left.ToUpper(), comparison));
|
||||
Assert.Equal(left.Length, decoded.ToLower().IndexOf(right.ToUpper(), startIndex: left.Length, comparison));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(StringComparison_TestData))]
|
||||
public void IndexOf_SingleElement(StringComparison comparison, string decoded)
|
||||
{
|
||||
bool ignoreCase = (int)comparison % 2 != 0;
|
||||
if (comparison is StringComparison.Ordinal or StringComparison.OrdinalIgnoreCase)
|
||||
{
|
||||
for (int i = 0; i < decoded.Length; i++)
|
||||
{
|
||||
string current = decoded.Substring(i, 1);
|
||||
// Fast-check the expected index.
|
||||
Assert.Equal(i, decoded.IndexOf(current, startIndex: i, comparison));
|
||||
IndexOf_SingleElement_Core(current, expectedIndex: i, ignoreCase);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
string[] textElements = TestHelper.GetTextElements(decoded).ToArray();
|
||||
for (int i = 0; i < textElements.Length; i++)
|
||||
{
|
||||
string current = textElements[i];
|
||||
int expectedIndex = textElements.Take(i).Sum(e => e.Length);
|
||||
// Fast-check the expected index.
|
||||
Assert.Equal(expectedIndex, decoded.IndexOf(current, startIndex: expectedIndex, comparison));
|
||||
IndexOf_SingleElement_Core(current, expectedIndex, ignoreCase);
|
||||
}
|
||||
}
|
||||
|
||||
void IndexOf_SingleElement_Core(string current, int expectedIndex, bool ignoreCase)
|
||||
{
|
||||
int startIndex = 0;
|
||||
while (true)
|
||||
{
|
||||
int result = ignoreCase ?
|
||||
decoded.ToLower().IndexOf(current.ToUpper(), startIndex, comparison) :
|
||||
decoded.IndexOf(current, startIndex, comparison);
|
||||
|
||||
if (result == -1 || result > expectedIndex)
|
||||
Assert.Fail($"'{current}' not found or found too late in '{decoded}'");
|
||||
else if (result < expectedIndex)
|
||||
startIndex = result + current.Length;
|
||||
else
|
||||
{
|
||||
Assert.Equal(expectedIndex, result);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(StringComparison_TestData))]
|
||||
public void LastIndexOf_MultipleElements(StringComparison comparison, string decoded)
|
||||
{
|
||||
bool ignoreCase = (int)comparison % 2 != 0;
|
||||
|
||||
// Don't deal with LastIndexOf(string.Empty) nuances, test against full length outside of the loop.
|
||||
// see https://learn.microsoft.com/dotnet/core/compatibility/core-libraries/5.0/lastindexof-improved-handling-of-empty-values
|
||||
Assert.Equal(0, decoded.LastIndexOf(decoded, comparison));
|
||||
if (ignoreCase)
|
||||
Assert.Equal(0, decoded.ToLower().LastIndexOf(decoded.ToUpper(), comparison));
|
||||
|
||||
if (comparison is StringComparison.Ordinal or StringComparison.OrdinalIgnoreCase)
|
||||
{
|
||||
|
||||
for (int i = 1; i < decoded.Length; i++)
|
||||
{
|
||||
string left = decoded.Substring(0, i);
|
||||
string right = decoded.Substring(i);
|
||||
Assert.Equal(decoded.Length, left.Length + right.Length);
|
||||
|
||||
Assert.Equal(0, decoded.LastIndexOf(left, startIndex: left.Length - 1, comparison));
|
||||
// right substring starts at the end of left one.
|
||||
Assert.Equal(left.Length, decoded.LastIndexOf(right, comparison));
|
||||
|
||||
if (ignoreCase)
|
||||
{
|
||||
Assert.Equal(0, decoded.ToLower().LastIndexOf(left.ToUpper(), startIndex: left.Length - 1, comparison));
|
||||
Assert.Equal(left.Length, decoded.ToLower().LastIndexOf(right.ToUpper(), comparison));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
string[] textElements = TestHelper.GetTextElements(decoded).ToArray();
|
||||
for (int i = 1; i < textElements.Length; i++)
|
||||
{
|
||||
string left = string.Concat(textElements.Take(i));
|
||||
string right = string.Concat(textElements.Skip(i));
|
||||
Assert.Equal(decoded.Length, left.Length + right.Length);
|
||||
|
||||
Assert.Equal(0, decoded.LastIndexOf(left, startIndex: left.Length - 1, comparison));
|
||||
Assert.Equal(left.Length, decoded.LastIndexOf(right, comparison));
|
||||
|
||||
if (ignoreCase)
|
||||
{
|
||||
Assert.Equal(0, decoded.ToLower().LastIndexOf(left.ToUpper(), startIndex: left.Length - 1, comparison));
|
||||
Assert.Equal(left.Length, decoded.ToLower().LastIndexOf(right.ToUpper(), comparison));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(StringComparison_TestData))]
|
||||
public void LastIndexOf_SingleElement(StringComparison comparison, string decoded)
|
||||
{
|
||||
bool ignoreCase = (int)comparison % 2 != 0;
|
||||
if (comparison is StringComparison.Ordinal or StringComparison.OrdinalIgnoreCase)
|
||||
{
|
||||
for (int i = 0; i < decoded.Length; i++)
|
||||
{
|
||||
string current = decoded.Substring(i, 1);
|
||||
// Fast-check the expected index.
|
||||
Assert.Equal(i, decoded.LastIndexOf(current, startIndex: i, comparison));
|
||||
LastIndexOf_SingleElement_Core(current, expectedIndex: i, ignoreCase);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
string[] textElements = TestHelper.GetTextElements(decoded).ToArray();
|
||||
for (int i = 0; i < textElements.Length; i++)
|
||||
{
|
||||
string current = textElements[i];
|
||||
int expectedIndex = textElements.Take(i).Sum(e => e.Length);
|
||||
// Fast-check the expected index.
|
||||
Assert.Equal(expectedIndex, decoded.LastIndexOf(current, startIndex: expectedIndex + current.Length - 1, comparison));
|
||||
LastIndexOf_SingleElement_Core(current, expectedIndex, ignoreCase);
|
||||
}
|
||||
}
|
||||
|
||||
void LastIndexOf_SingleElement_Core(string current, int expectedIndex, bool ignoreCase)
|
||||
{
|
||||
int startIndex = decoded.Length - 1;
|
||||
while (true)
|
||||
{
|
||||
int result = ignoreCase ?
|
||||
decoded.ToLower().LastIndexOf(current.ToUpper(), startIndex, comparison) :
|
||||
decoded.LastIndexOf(current, startIndex, comparison);
|
||||
|
||||
if (result == -1 || result < expectedIndex)
|
||||
Assert.Fail($"'{current}' not found or found too late in '{decoded}'");
|
||||
else if (result > expectedIndex)
|
||||
startIndex = result - 1;
|
||||
else
|
||||
{
|
||||
Assert.Equal(expectedIndex, result);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(TestHelper.DecodedTestData), MemberType = typeof(TestHelper))]
|
||||
public void Replace(string decoded)
|
||||
{
|
||||
Assert.False(decoded.Contains(Dummy));
|
||||
|
||||
foreach (string textElement in TestHelper.GetTextElements(decoded))
|
||||
{
|
||||
int occurrences = decoded.Split([textElement], StringSplitOptions.None).Length;
|
||||
|
||||
string replaced = decoded.Replace(textElement, Dummy);
|
||||
Assert.Equal(occurrences, replaced.Split([Dummy], StringSplitOptions.None).Length);
|
||||
|
||||
if (textElement.Length == 1)
|
||||
{
|
||||
replaced = decoded.Replace(textElement[0], Dummy[0]);
|
||||
Assert.Equal(occurrences, replaced.Split([Dummy], StringSplitOptions.None).Length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(TestHelper.DecodedTestData), MemberType = typeof(TestHelper))]
|
||||
public void Split(string decoded)
|
||||
{
|
||||
Assert.Single(Split(decoded, Dummy));
|
||||
Assert.Single(Split(decoded, Dummy[0]));
|
||||
|
||||
string[] textElements = TestHelper.GetTextElements(decoded).ToArray();
|
||||
|
||||
for (int i = 0; i < textElements.Length; i++)
|
||||
{
|
||||
var result = Split(decoded, textElements[i]);
|
||||
Assert.True(result.Length > 1);
|
||||
}
|
||||
|
||||
static string[] Split<T>(string str, params T[] separators) =>
|
||||
separators switch
|
||||
{
|
||||
char[] chars => str.Split(chars, StringSplitOptions.None),
|
||||
string[] strings => str.Split(strings, StringSplitOptions.None),
|
||||
_ => throw new ArgumentException()
|
||||
};
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue