Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.

Commit eea46fa

Browse files
committed
Add TextInfo casing tests
An initial set of TextInfo casing tests. I looked at the scinerios we have coverage for in ToF, but wrote the actual tests from scratch in idomatic XUnit style. In addition, I have added some specific tests for the Turkish I casing, since the characters have special casing behavior in Turkish and some other locales as well as test cases to ensure we have the simple casing in places where the Unicode standard normally defines special cases which are either context sensitive or would change the number of codepoints in a cased string.
1 parent 9b24cf6 commit eea46fa

File tree

5 files changed

+910
-0
lines changed

5 files changed

+910
-0
lines changed
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
2+
Microsoft Visual Studio Solution File, Format Version 12.00
3+
# Visual Studio 2013
4+
VisualStudioVersion = 12.0.31101.0
5+
MinimumVisualStudioVersion = 10.0.40219.1
6+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Globalization.Tests", "tests\System.Globalization.Tests.csproj", "{7F5F5134-00FE-4DE8-B20C-3DA8BA2EBA68}"
7+
EndProject
8+
Global
9+
GlobalSection(SolutionConfigurationPlatforms) = preSolution
10+
Debug|Any CPU = Debug|Any CPU
11+
Release|Any CPU = Release|Any CPU
12+
EndGlobalSection
13+
GlobalSection(ProjectConfigurationPlatforms) = postSolution
14+
{7F5F5134-00FE-4DE8-B20C-3DA8BA2EBA68}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15+
{7F5F5134-00FE-4DE8-B20C-3DA8BA2EBA68}.Debug|Any CPU.Build.0 = Debug|Any CPU
16+
{7F5F5134-00FE-4DE8-B20C-3DA8BA2EBA68}.Release|Any CPU.ActiveCfg = Release|Any CPU
17+
{7F5F5134-00FE-4DE8-B20C-3DA8BA2EBA68}.Release|Any CPU.Build.0 = Release|Any CPU
18+
EndGlobalSection
19+
GlobalSection(SolutionProperties) = preSolution
20+
HideSolutionNode = FALSE
21+
EndGlobalSection
22+
EndGlobal
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3+
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
4+
<PropertyGroup>
5+
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
6+
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
7+
<ProjectGuid>{7F5F5134-00FE-4DE8-B20C-3DA8BA2EBA68}</ProjectGuid>
8+
<OutputType>Library</OutputType>
9+
<RootNamespace>System.Globalization.Tests</RootNamespace>
10+
<AssemblyName>System.Globalization.Tests</AssemblyName>
11+
<RestorePackages>true</RestorePackages>
12+
</PropertyGroup>
13+
<!-- Default configurations to help VS understand the configurations -->
14+
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
15+
</PropertyGroup>
16+
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
17+
</PropertyGroup>
18+
<ItemGroup>
19+
<Compile Include="TextInfo.cs" />
20+
</ItemGroup>
21+
<ItemGroup>
22+
<None Include="project.json" />
23+
</ItemGroup>
24+
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
25+
</Project>
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
// Copyright (c) Microsoft. All rights reserved.
2+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
3+
4+
using System;
5+
using System.Globalization;
6+
using Xunit;
7+
8+
public class TextInfoTests
9+
{
10+
[Theory]
11+
[InlineData("")]
12+
[InlineData("en-US")]
13+
[InlineData("fr")]
14+
[ActiveIssue(846, PlatformID.AnyUnix)]
15+
public static void ToUpper(string localeName)
16+
{
17+
TextInfo ti = new CultureInfo(localeName).TextInfo;
18+
19+
Assert.Equal('A', ti.ToUpper('a'));
20+
Assert.Equal("ABC", ti.ToUpper("abc"));
21+
Assert.Equal('A', ti.ToUpper('A'));
22+
Assert.Equal("ABC", ti.ToUpper("ABC"));
23+
24+
Assert.Equal("THIS IS A LONGER TEST CASE", ti.ToUpper("this is a longer test case"));
25+
Assert.Equal("THIS IS A LONGER MIXED CASE TEST CASE", ti.ToUpper("this Is A LONGER mIXEd casE test case"));
26+
27+
Assert.Equal("THIS \t HAS \t SOME \t TABS", ti.ToUpper("this \t HaS \t somE \t TABS"));
28+
29+
Assert.Equal('1', ti.ToUpper('1'));
30+
Assert.Equal("123", ti.ToUpper("123"));
31+
32+
Assert.Equal("EMBEDDED\0NULL\0BYTE\0", ti.ToUpper("embedded\0NuLL\0Byte\0"));
33+
34+
// LATIN SMALL LETTER O WITH ACUTE, which has an upper case variant.
35+
Assert.Equal('\u00D3', ti.ToUpper('\u00F3'));
36+
Assert.Equal("\u00D3", ti.ToUpper("\u00F3"));
37+
38+
// SNOWMAN, which does not have an upper case variant.
39+
Assert.Equal('\u2603', ti.ToUpper('\u2603'));
40+
Assert.Equal("\u2603", ti.ToUpper("\u2603"));
41+
42+
// DESERT SMALL LETTER LONG I has an upperc case variant.
43+
Assert.Equal("\U00010400", ti.ToUpper("\U00010428"));
44+
45+
// RAINBOW (outside the BMP and does not case)
46+
Assert.Equal("\U0001F308", ti.ToLower("\U0001F308"));
47+
48+
// These are cases where we have invalid UTF-16 in a string (mismatched surrogate pairs). They should be
49+
// unchanged by casing.
50+
Assert.Equal("BE CAREFUL, \uD83C\uD83C, THIS ONE IS TRICKY", ti.ToUpper("be careful, \uD83C\uD83C, this one is tricky"));
51+
Assert.Equal("BE CAREFUL, \uDF08\uD83C, THIS ONE IS TRICKY", ti.ToUpper("be careful, \uDF08\uD83C, this one is tricky"));
52+
Assert.Equal("BE CAREFUL, \uDF08\uDF08, THIS ONE IS TRICKY", ti.ToUpper("be careful, \uDF08\uDF08, this one is tricky"));
53+
54+
Assert.Throws<ArgumentNullException>(() => ti.ToUpper(null));
55+
}
56+
57+
[Theory]
58+
[InlineData("")]
59+
[InlineData("en-US")]
60+
[InlineData("fr")]
61+
[ActiveIssue(846, PlatformID.AnyUnix)]
62+
public static void ToLower(string localeName)
63+
{
64+
TextInfo ti = new CultureInfo(localeName).TextInfo;
65+
66+
Assert.Equal('a', ti.ToLower('A'));
67+
Assert.Equal("abc", ti.ToLower("ABC"));
68+
Assert.Equal('a', ti.ToLower('a'));
69+
Assert.Equal("abc", ti.ToLower("abc"));
70+
71+
Assert.Equal("this is a longer test case", ti.ToLower("THIS IS A LONGER TEST CASE"));
72+
Assert.Equal("this is a longer mixed case test case", ti.ToLower("this Is A LONGER mIXEd casE test case"));
73+
74+
Assert.Equal("this \t has \t some \t tabs", ti.ToLower("THIS \t hAs \t SOMe \t tabs"));
75+
76+
Assert.Equal('1', ti.ToLower('1'));
77+
Assert.Equal("123", ti.ToLower("123"));
78+
79+
Assert.Equal("embedded\0null\0byte\0", ti.ToLower("EMBEDDED\0NuLL\0Byte\0"));
80+
81+
// LATIN CAPITAL LETTER O WITH ACUTE, which has a lower case variant.
82+
Assert.Equal('\u00F3', ti.ToLower('\u00D3'));
83+
Assert.Equal("\u00F3", ti.ToLower("\u00D3"));
84+
85+
// SNOWMAN, which does not have a lower case variant.
86+
Assert.Equal('\u2603', ti.ToLower('\u2603'));
87+
Assert.Equal("\u2603", ti.ToLower("\u2603"));
88+
89+
// DESERT CAPITAL LETTER LONG I has a lower case variant.
90+
Assert.Equal("\U00010428", ti.ToLower("\U00010400"));
91+
92+
// RAINBOW (outside the BMP and does not case)
93+
Assert.Equal("\U0001F308", ti.ToLower("\U0001F308"));
94+
95+
// These are cases where we have invalid UTF-16 in a string (mismatched surrogate pairs). They should be
96+
// unchanged by casing.
97+
Assert.Equal("be careful, \uD83C\uD83C, this one is tricky", ti.ToLower("BE CAREFUL, \uD83C\uD83C, THIS ONE IS TRICKY"));
98+
Assert.Equal("be careful, \uDF08\uD83C, this one is tricky", ti.ToLower("BE CAREFUL, \uDF08\uD83C, THIS ONE IS TRICKY"));
99+
Assert.Equal("be careful, \uDF08\uDF08, this one is tricky", ti.ToLower("BE CAREFUL, \uDF08\uDF08, THIS ONE IS TRICKY"));
100+
101+
Assert.Throws<ArgumentNullException>(() => ti.ToLower(null));
102+
}
103+
104+
[Theory]
105+
[InlineData("tr")]
106+
[InlineData("tr-TR")]
107+
[InlineData("az")]
108+
[InlineData("az-Latn-AZ")]
109+
[ActiveIssue(846, PlatformID.AnyUnix)]
110+
public static void TurkishICasing(string localeName)
111+
{
112+
TextInfo ti = new CultureInfo(localeName).TextInfo;
113+
114+
Assert.Equal('i', ti.ToLower('\u0130'));
115+
Assert.Equal("i", ti.ToLower("\u0130"));
116+
117+
Assert.Equal('\u0130', ti.ToUpper('i'));
118+
Assert.Equal("\u0130", ti.ToUpper("i"));
119+
120+
Assert.Equal('\u0131', ti.ToLower('I'));
121+
Assert.Equal("\u0131", ti.ToLower("I"));
122+
123+
Assert.Equal('I', ti.ToUpper('\u0131'));
124+
Assert.Equal("I", ti.ToUpper("\u0131"));
125+
}
126+
127+
[Theory]
128+
[InlineData("")]
129+
[InlineData("en-US")]
130+
[InlineData("fr-FR")]
131+
[ActiveIssue(846, PlatformID.AnyUnix)]
132+
public static void NoUnicodeSpecalCases(string localeName)
133+
{
134+
// Unicode defines some codepoints which expand into multiple codepoints
135+
// when cased (see SpecialCasing.txt from UNIDATA for some examples). We have never done
136+
// these sorts of expansions, since it would cause string lengths to change when cased,
137+
// which is non-intuative. In addition, there are some context sensitive mappings which
138+
// we also don't preform.
139+
140+
TextInfo ti = new CultureInfo(localeName).TextInfo;
141+
142+
// es-zed does not case to SS when upercased.
143+
Assert.Equal("\u00DF", ti.ToUpper("\u00DF"));
144+
145+
// Ligatures do not expand when cased.
146+
Assert.Equal("\uFB00", ti.ToUpper("\uFB00"));
147+
148+
// Precomposed character with no uppercase variaint, we don't want to "decompose" this
149+
// as part of casing.
150+
Assert.Equal("\u0149", ti.ToUpper("\u0149"));
151+
152+
// Greek Capital Letter Sigma (does not to case to U+03C2 with "final sigma" rule).
153+
Assert.Equal("\u03C3", ti.ToLower("\u03A3"));
154+
}
155+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"dependencies": {
3+
"System.Console": "4.0.0-beta-*",
4+
"System.Globalization": "4.0.10-beta-*",
5+
"System.Reflection": "4.0.10-beta-*",
6+
"System.Runtime": "4.0.20-beta-*",
7+
"System.Runtime.Extensions": "4.0.10-beta-*",
8+
"System.Runtime.InteropServices": "4.0.20-beta-*",
9+
"System.Threading": "4.0.10-beta-*",
10+
"System.Threading.Tasks": "4.0.10-beta-*",
11+
"xunit": "2.0.0-beta5-build2785",
12+
"xunit.abstractions.netcore": "1.0.0-prerelease",
13+
"xunit.assert": "2.0.0-beta5-build2785",
14+
"xunit.core.netcore": "1.0.1-prerelease",
15+
"xunit.netcore.extensions": "1.0.0-prerelease-*"
16+
},
17+
"frameworks": {
18+
"dnxcore50": {}
19+
}
20+
}

0 commit comments

Comments
 (0)