Assert.True(CultureInfo.InvariantCulture.Contains(new CultureInfo("ja")));
Assert.True(CultureInfo.InvariantCulture.Contains(CultureInfo.InvariantCulture));
}
+
+ [Theory]
+ [InlineData("abc", 0, (int)'a')]
+ [InlineData("abc", 1, (int)'b')]
+ [InlineData("abc", 2, (int)'c')]
+ [InlineData("🍣", 0, 0x1f363)] // サロゲートペア
+ public void GetCodepointAtSafe_Test(string s, int index, int expected)
+ {
+ Assert.Equal(expected, s.GetCodepointAtSafe(index));
+ }
+
+ [Theory]
+ // char.ConvertToUtf32 をそのまま使用するとエラーになるパターン
+ [InlineData(new[] { '\ud83c', '\udf63' }, 1, 0xdf63)] // pos がサロゲートペアの後半部分を指している
+ [InlineData(new[] { '\ud83c' }, 0, 0xd83c)] // 壊れたサロゲートペア (LowSurrogate が無い)
+ [InlineData(new[] { '\udf63' }, 0, 0xdf63)] // 壊れたサロゲートペア (HighSurrogate が無い)
+ public void GetCodepointAtSafe_BrokenSurrogateTest(char[] s, int index, int expected)
+ {
+ // InlineDataAttribute で壊れたサロゲートペアの string を扱えないため char[] を使う
+ Assert.Equal(expected, new string(s).GetCodepointAtSafe(index));
+ }
+
+ [Fact]
+ public void GetCodepointAtSafe_ErrorTest()
+ {
+ Assert.Throws<ArgumentNullException>(() => ((string)null).GetCodepointAtSafe(0));
+ Assert.Throws<ArgumentOutOfRangeException>(() => "a".GetCodepointAtSafe(-1));
+ Assert.Throws<ArgumentOutOfRangeException>(() => "a".GetCodepointAtSafe(1));
+ }
}
}
Assert.Equal(267, twitter.GetTextLengthRemain("🔥🐔🔥 焼き鳥"));
}
}
+
+ [Fact]
+ public void GetTextLengthRemain_BrokenSurrogateTest()
+ {
+ using (var twitter = new Twitter())
+ {
+ // 投稿欄に IME から絵文字を入力すると HighSurrogate のみ入力された状態で TextChanged イベントが呼ばれることがある
+ Assert.Equal(278, twitter.GetTextLengthRemain("\ud83d"));
+ Assert.Equal(9999, twitter.GetTextLengthRemain("D twitter \ud83d"));
+ }
+ }
}
}
key = kvp.Key;
value = kvp.Value;
}
+
+ /// <summary>
+ /// 文字列中の指定された位置にある文字のコードポイントを返します
+ /// </summary>
+ public static int GetCodepointAtSafe(this string s, int index)
+ {
+ // IsSurrogatePair が true を返す場合のみ ConvertToUtf32 メソッドを使用する
+ if (char.IsSurrogatePair(s, index))
+ return char.ConvertToUtf32(s, index);
+
+ return s[index];
+ }
}
}