diff --git a/UnicodeInformation.Builder/Program.cs b/UnicodeInformation.Builder/Program.cs index c68b2f5..1174058 100644 --- a/UnicodeInformation.Builder/Program.cs +++ b/UnicodeInformation.Builder/Program.cs @@ -13,7 +13,7 @@ namespace System.Unicode.Builder { public const string UnihanDirectoryName = "Unihan"; public const string UnihanArchiveName = "Unihan.zip"; - public const string directoryName = "UCD"; + public const string UcdDirectoryName = "UCD"; public const string UcdArchiveName = "UCD.zip"; public static readonly string[] ucdRequiredFiles = new[] @@ -32,9 +32,18 @@ namespace System.Unicode.Builder "Unihan_Variants.txt", }; + private static HttpMessageHandler httpMessageHandler; + + // The only purpose of this is for tests… + internal static HttpMessageHandler HttpMessageHandler + { + get { return httpMessageHandler ?? (httpMessageHandler = new HttpClientHandler()); } + set { httpMessageHandler = value != null ? value : new HttpClientHandler(); } + } + private static byte[] DownloadDataArchive(string archiveName) { - using (var httpClient = new HttpClient()) + using (var httpClient = new HttpClient(HttpMessageHandler)) { return httpClient.GetByteArrayAsync(HttpDataSource.UnicodeCharacterDataUri + archiveName).Result; } @@ -43,7 +52,7 @@ namespace System.Unicode.Builder internal static IDataSource GetDataSource(string archiveName, string directoryName, string[] requiredFiles, bool? shouldDownload, bool? shouldSaveArchive, bool? shouldExtract) { string baseDirectory = Environment.CurrentDirectory; - string dataDirectory = Path.Combine(baseDirectory, Program.directoryName); + string dataDirectory = Path.Combine(baseDirectory, UcdDirectoryName); string dataArchiveFileName = Path.Combine(baseDirectory, archiveName); if (shouldDownload != true) @@ -126,7 +135,7 @@ namespace System.Unicode.Builder { UnicodeInfoBuilder data; - using (var ucdSource = GetDataSource(UcdArchiveName, directoryName, ucdRequiredFiles, null, null, null)) + using (var ucdSource = GetDataSource(UcdArchiveName, UcdDirectoryName, ucdRequiredFiles, null, null, null)) using (var unihanSource = GetDataSource(UnihanArchiveName, UnihanDirectoryName, unihanRequiredFiles, null, null, null)) { data = UnicodeDataProcessor.BuildDataAsync(ucdSource, unihanSource).Result; diff --git a/UnicodeInformation.Tests/AssertEx.cs b/UnicodeInformation.Tests/AssertEx.cs new file mode 100644 index 0000000..3bf5b74 --- /dev/null +++ b/UnicodeInformation.Tests/AssertEx.cs @@ -0,0 +1,30 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace UnicodeInformation.Tests +{ + public static class AssertEx + { + public static void ThrowsExactly(Action action, string methodName) + where TException : Exception + { + try + { + action(); + } + catch (TException ex) + { + if (ex.GetType() != typeof(TException)) + Assert.Fail("The " + methodName + " method should throw an exception of type " + typeof(TException).Name + " but got " + ex.GetType().Name + "."); + else + return; + } + + Assert.Fail("The " + methodName + " method should throw an exception of type " + typeof(TException).Name + "."); + } + } +} diff --git a/UnicodeInformation.Tests/UnicodeDataManagerTests.cs b/UnicodeInformation.Tests/UnicodeDataManagerTests.cs deleted file mode 100644 index 587a862..0000000 --- a/UnicodeInformation.Tests/UnicodeDataManagerTests.cs +++ /dev/null @@ -1,86 +0,0 @@ -using System; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using System.Threading.Tasks; -using System.Net; -using System.IO; -using System.IO.Compression; -using System.Unicode.Builder; -using System.Text; - -namespace System.Unicode.Tests -{ - [TestClass] - public class UnicodeDataManagerTests - { - private const string UcdDirectoryName = "UCD"; - - [TestInitialize] - public void Initialize() - { - var directoryName = Path.GetFullPath(UcdDirectoryName); - - if (!Directory.Exists(directoryName)) - { - Directory.CreateDirectory(directoryName); - - string ucdFileName = Path.Combine(directoryName, "UCD.zip"); - string unihanFileName = Path.Combine(directoryName, "Unihan.zip"); - - if (!File.Exists(ucdFileName)) - { - new WebClient().DownloadFile("http://www.unicode.org/Public/UCD/latest/ucd/UCD.zip", ucdFileName); - ZipFile.ExtractToDirectory(ucdFileName, directoryName); - } - - if (!File.Exists(unihanFileName)) - { - new WebClient().DownloadFile("http://www.unicode.org/Public/UCD/latest/ucd/Unihan.zip", unihanFileName); - ZipFile.ExtractToDirectory(unihanFileName, directoryName); - } - } - } - - [TestMethod] - public async Task BuildDataAsync() - { - var source = new FileDataSource(UcdDirectoryName); - - var data = (await UnicodeDataProcessor.BuildDataAsync(source, source)); - - Assert.AreEqual((int)'\t', data.GetUcd('\t').CodePointRange.FirstCodePoint); - } - - [TestMethod] - public async Task BuildAndWriteDataAsync() - { - var source = new FileDataSource(UcdDirectoryName); - - var data = await UnicodeDataProcessor.BuildDataAsync(source, source); - - using (var stream = new DeflateStream(File.Create("ucd.dat"), CompressionLevel.Optimal, false)) - { - data.WriteToStream(stream); - } - } - -#if DEBUG - [TestMethod] - public void TestCodePointEncoding() - { - using (var stream = new MemoryStream(4)) - using (var writer = new BinaryWriter(stream, Encoding.UTF8, true)) - using (var reader = new BinaryReader(stream, Encoding.UTF8, true)) - { - for (int i = 0; i <= 0x10FFFF; ++i) - { - writer.WriteCodePoint(i); - writer.Flush(); - stream.Position = 0; - Assert.AreEqual(i, UnicodeInfo.ReadCodePoint(reader)); - stream.Position = 0; - } - } - } -#endif - } -} diff --git a/UnicodeInformation.Tests/UnicodeInfoBuilderTests.cs b/UnicodeInformation.Tests/UnicodeInfoBuilderTests.cs new file mode 100644 index 0000000..8cd95c6 --- /dev/null +++ b/UnicodeInformation.Tests/UnicodeInfoBuilderTests.cs @@ -0,0 +1,178 @@ +using System; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System.Threading.Tasks; +using System.Net; +using System.IO; +using System.IO.Compression; +using System.Unicode.Builder; +using System.Text; +using System.Net.Http; +using System.Collections.Generic; + +namespace System.Unicode.Tests +{ + [TestClass] + public class UnicodeInfoBuilderTests + { + private sealed class FileHttpResponseHandler : HttpMessageHandler + { + private readonly Dictionary registeredFiles = new Dictionary(); + + public void RegisterFile(Uri uri, string fileName) + { + registeredFiles.Add(uri, fileName); + } + + protected async override Task SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken) + { + string fileName; + + if (registeredFiles.TryGetValue(request.RequestUri, out fileName)) + { + return new HttpResponseMessage(HttpStatusCode.OK) { Content = new ByteArrayContent(await Task.Run(() => File.ReadAllBytes(fileName)).ConfigureAwait(false)) }; + } + else + { + return new HttpResponseMessage(HttpStatusCode.NotFound) { RequestMessage = request }; + } + } + } + + private const string HttpCacheDirectory = "_HttpCache"; + + private static async Task RegisterAndDownloadFile(FileHttpResponseHandler handler, string httpCacheDirectory, Uri baseUri, string fileName) + { + var uri = new Uri(baseUri, fileName); + string path = Path.Combine(httpCacheDirectory, fileName); + + if (!File.Exists(path)) + { + using (var httpClient = new HttpClient()) + { + var data = await httpClient.GetByteArrayAsync(uri).ConfigureAwait(false); + + File.WriteAllBytes(path, data); + } + } + + handler.RegisterFile(uri, path); + } + + [TestInitialize] + public void Initialize() + { + var httpCacheDirectory = Path.GetFullPath(HttpCacheDirectory); + + if (!Directory.Exists(httpCacheDirectory)) Directory.CreateDirectory(httpCacheDirectory); + + var handler = new FileHttpResponseHandler(); + + var ucdTask = RegisterAndDownloadFile(handler, httpCacheDirectory, HttpDataSource.UnicodeCharacterDataUri, Program.UcdArchiveName); + var unihanTask = RegisterAndDownloadFile(handler, httpCacheDirectory, HttpDataSource.UnicodeCharacterDataUri, Program.UnihanArchiveName); + + Program.HttpMessageHandler = handler; + + if (Directory.Exists(Program.UcdDirectoryName)) Directory.Delete(Program.UcdDirectoryName, true); + if (File.Exists(Program.UcdArchiveName)) File.Delete(Program.UcdArchiveName); + if (Directory.Exists(Program.UnihanDirectoryName)) Directory.Delete(Program.UnihanDirectoryName, true); + if (File.Exists(Program.UnihanArchiveName)) File.Delete(Program.UnihanArchiveName); + + Task.WaitAll(ucdTask, unihanTask); + } + + [TestMethod] + public void DownloadUcdArchive() + { + using (var source = Program.GetDataSource(Program.UcdArchiveName, Program.UcdDirectoryName, Program.ucdRequiredFiles, true, false, false)) + { + } + } + + [TestMethod] + public void DownloadAndSaveUcdArchive() + { + using (var source = Program.GetDataSource(Program.UcdArchiveName, Program.UcdDirectoryName, Program.ucdRequiredFiles, true, true, false)) + { + } + } + + [TestMethod] + public void ExtractUcdArchive() + { + using (var source = Program.GetDataSource(Program.UcdArchiveName, Program.UcdDirectoryName, Program.ucdRequiredFiles, true, true, true)) + { + } + } + + [TestMethod] + public void DownloadUnihanArchive() + { + using (var source = Program.GetDataSource(Program.UnihanArchiveName, Program.UnihanDirectoryName, Program.ucdRequiredFiles, true, false, false)) + { + } + } + + [TestMethod] + public void DownloadAndSaveUnihanArchive() + { + using (var source = Program.GetDataSource(Program.UnihanArchiveName, Program.UnihanDirectoryName, Program.ucdRequiredFiles, true, true, false)) + { + } + } + + [TestMethod] + public void ExtractUnihanArchive() + { + using (var source = Program.GetDataSource(Program.UnihanArchiveName, Program.UnihanDirectoryName, Program.ucdRequiredFiles, true, true, true)) + { + } + } + + [TestMethod] + public async Task BuildDataAsync() + { + using (var ucdSource = Program.GetDataSource(Program.UcdArchiveName, Program.UcdDirectoryName, Program.ucdRequiredFiles, null, null, null)) + using (var unihanSource = Program.GetDataSource(Program.UnihanArchiveName, Program.UnihanDirectoryName, Program.ucdRequiredFiles, null, null, null)) + { + var data = (await UnicodeDataProcessor.BuildDataAsync(ucdSource, unihanSource)); + + Assert.AreEqual((int)'\t', data.GetUcd('\t').CodePointRange.FirstCodePoint); + } + } + + [TestMethod] + public async Task BuildAndWriteDataAsync() + { + using (var ucdSource = Program.GetDataSource(Program.UcdArchiveName, Program.UcdDirectoryName, Program.ucdRequiredFiles, null, null, null)) + using (var unihanSource = Program.GetDataSource(Program.UnihanArchiveName, Program.UnihanDirectoryName, Program.ucdRequiredFiles, null, null, null)) + { + var data = (await UnicodeDataProcessor.BuildDataAsync(ucdSource, unihanSource)); + + using (var stream = new DeflateStream(File.Create("ucd.dat"), CompressionLevel.Optimal, false)) + { + data.WriteToStream(stream); + } + } + } + +#if DEBUG + [TestMethod] + public void CodePointEncodingTest() + { + using (var stream = new MemoryStream(4)) + using (var writer = new BinaryWriter(stream, Encoding.UTF8, true)) + using (var reader = new BinaryReader(stream, Encoding.UTF8, true)) + { + for (int i = 0; i <= 0x10FFFF; ++i) + { + writer.WriteCodePoint(i); + writer.Flush(); + stream.Position = 0; + Assert.AreEqual(i, UnicodeInfo.ReadCodePoint(reader)); + stream.Position = 0; + } + } + } +#endif + } +} diff --git a/UnicodeInformation.Tests/UnicodeInfoTests.cs b/UnicodeInformation.Tests/UnicodeInfoTests.cs new file mode 100644 index 0000000..246f747 --- /dev/null +++ b/UnicodeInformation.Tests/UnicodeInfoTests.cs @@ -0,0 +1,187 @@ +using System; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System.Unicode; +using System.Globalization; +using System.Collections.Generic; +using System.Collections; + +namespace UnicodeInformation.Tests +{ + [TestClass] + public class UnicodeInfoTests + { + [TestMethod] + public void CodePointEnumeratorTest() + { + string text = "\u0041\U0001F600\u00E9"; + + var enumerable = text.AsCodePointEnumerable(); + + Assert.AreEqual(text, enumerable.Text); + + var enumerator = enumerable.GetEnumerator(); + + Assert.AreEqual(true, enumerator.MoveNext()); + Assert.AreEqual(0x0041, enumerator.Current); + Assert.AreEqual(true, enumerator.MoveNext()); + Assert.AreEqual(0x1F600, enumerator.Current); + Assert.AreEqual(true, enumerator.MoveNext()); + Assert.AreEqual(0x00E9, enumerator.Current); + Assert.AreEqual(false, enumerator.MoveNext()); + + var genericEnumerator = ((IEnumerable)enumerable).GetEnumerator(); + + Assert.AreEqual(true, genericEnumerator.MoveNext()); + Assert.AreEqual(0x0041, genericEnumerator.Current); + Assert.AreEqual(true, genericEnumerator.MoveNext()); + Assert.AreEqual(0x1F600, genericEnumerator.Current); + Assert.AreEqual(true, genericEnumerator.MoveNext()); + Assert.AreEqual(0x00E9, genericEnumerator.Current); + Assert.AreEqual(false, genericEnumerator.MoveNext()); + + var legacyEnumerator = ((IEnumerable)enumerable).GetEnumerator(); + + Assert.AreEqual(true, legacyEnumerator.MoveNext()); + Assert.AreEqual(0x0041, legacyEnumerator.Current); + Assert.AreEqual(true, legacyEnumerator.MoveNext()); + Assert.AreEqual(0x1F600, legacyEnumerator.Current); + Assert.AreEqual(true, legacyEnumerator.MoveNext()); + Assert.AreEqual(0x00E9, legacyEnumerator.Current); + Assert.AreEqual(false, legacyEnumerator.MoveNext()); + } + + [TestMethod] + public void DisplayTextTest() + { + for (int i = 0; i <= 0x20; ++i) + { + Assert.AreEqual(char.ConvertFromUtf32(0x2400 + i), UnicodeInfo.GetDisplayText(i)); + Assert.AreEqual(char.ConvertFromUtf32(0x2400 + i), UnicodeInfo.GetDisplayText(UnicodeInfo.GetCharInfo(i))); + } + + Assert.AreEqual("\u0041", UnicodeInfo.GetDisplayText(0x0041)); + Assert.AreEqual("\U0001F600", UnicodeInfo.GetDisplayText(0x1F600)); + Assert.AreEqual("\u00E9", UnicodeInfo.GetDisplayText(0x00E9)); + } + + private static void AssertChar(int codePoint, UnicodeCategory category, string name) + { + var info = UnicodeInfo.GetCharInfo(codePoint); + Assert.AreEqual(codePoint, info.CodePoint); + Assert.AreEqual(category, info.Category); + Assert.AreEqual(name, info.Name); + } + + [TestMethod] + public void CharacterInfoTest() + { + AssertChar(0x0041, UnicodeCategory.UppercaseLetter, "LATIN CAPITAL LETTER A"); + AssertChar(0x1F600, UnicodeCategory.OtherSymbol, "GRINNING FACE"); + AssertChar(0x00E9, UnicodeCategory.LowercaseLetter, "LATIN SMALL LETTER E WITH ACUTE"); + } + + [TestMethod] + public void RationalNumberTest() + { + Assert.AreEqual(true, default(UnicodeRationalNumber).IsDefaultValue); + Assert.AreEqual("1", new UnicodeRationalNumber(1).ToString()); + Assert.AreEqual("1", new UnicodeRationalNumber(1, 1).ToString()); + Assert.AreEqual(new UnicodeRationalNumber(1), new UnicodeRationalNumber(1, 1)); + Assert.AreEqual("1/100", new UnicodeRationalNumber(1, 100).ToString()); + Assert.AreEqual("-20/7", new UnicodeRationalNumber(-20, 7).ToString()); + Assert.AreEqual("-5", new UnicodeRationalNumber(-5).ToString()); + Assert.AreEqual(long.MaxValue.ToString(), new UnicodeRationalNumber(long.MaxValue).ToString()); + Assert.AreEqual(long.MaxValue.ToString() + "/" + byte.MaxValue.ToString(), new UnicodeRationalNumber(long.MaxValue, byte.MaxValue).ToString()); + Assert.AreEqual(string.Empty, default(UnicodeRationalNumber).ToString()); + + Assert.AreEqual(new UnicodeRationalNumber(0), UnicodeRationalNumber.Parse("0")); + Assert.AreEqual(new UnicodeRationalNumber(1), UnicodeRationalNumber.Parse("1")); + Assert.AreEqual(new UnicodeRationalNumber(1), UnicodeRationalNumber.Parse("1/1")); + Assert.AreEqual(new UnicodeRationalNumber(1, 10), UnicodeRationalNumber.Parse("1/10")); + Assert.AreNotEqual(new UnicodeRationalNumber(2, 10), UnicodeRationalNumber.Parse("1/10")); + Assert.AreNotEqual(new UnicodeRationalNumber(1, 20), UnicodeRationalNumber.Parse("1/10")); + Assert.AreNotEqual(new UnicodeRationalNumber(2, 2), new UnicodeRationalNumber(1, 1)); + + AssertEx.ThrowsExactly(() => UnicodeRationalNumber.Parse(null), "UnicodeRationalNumber.Parse"); + AssertEx.ThrowsExactly(() => UnicodeRationalNumber.Parse(string.Empty), "UnicodeRationalNumber.Parse"); + + var numbers = new[] + { + default(UnicodeRationalNumber), + new UnicodeRationalNumber(0), + new UnicodeRationalNumber(1), + new UnicodeRationalNumber(1, 10), + new UnicodeRationalNumber(1, 100), + new UnicodeRationalNumber(10), + new UnicodeRationalNumber(100), + new UnicodeRationalNumber(1000), + new UnicodeRationalNumber(1000000), + new UnicodeRationalNumber(1000000000), + new UnicodeRationalNumber(1000000000000), + }; + + var hashSet = new HashSet(); + + // Verify that all numbers are unique + foreach (var number in numbers) + Assert.AreEqual(true, hashSet.Add(number)); + + // Verify that all numbers are already in the list + foreach (var number in numbers) + Assert.AreEqual(false, hashSet.Add(number)); + } + +#if DEBUG + [TestMethod] + public void UnihanCodePointPackingTest() + { + for (int i = 0x3400; i < 0x4E00; ++i) + Assert.AreEqual(i, UnihanCharacterData.UnpackCodePoint(UnihanCharacterData.PackCodePoint(i))); + for (int i = 0x4E00; i < 0xA000; ++i) + Assert.AreEqual(i, UnihanCharacterData.UnpackCodePoint(UnihanCharacterData.PackCodePoint(i))); + for (int i = 0xF900; i < 0xFB00; ++i) + Assert.AreEqual(i, UnihanCharacterData.UnpackCodePoint(UnihanCharacterData.PackCodePoint(i))); + for (int i = 0x20000; i < 0x2F800; ++i) + Assert.AreEqual(i, UnihanCharacterData.UnpackCodePoint(UnihanCharacterData.PackCodePoint(i))); + for (int i = 0x2F800; i < 0x30000; ++i) + Assert.AreEqual(i, UnihanCharacterData.UnpackCodePoint(UnihanCharacterData.PackCodePoint(i))); + + try + { + UnihanCharacterData.PackCodePoint(0xA000); + Assert.Fail("The PackCodePoint method should fail for code points outside of the valid range."); + } + catch (ArgumentOutOfRangeException) + { + } + + try + { + UnihanCharacterData.PackCodePoint(0xFB00); + Assert.Fail("The PackCodePoint method should fail for code points outside of the valid range."); + } + catch (ArgumentOutOfRangeException) + { + } + + try + { + UnihanCharacterData.PackCodePoint(0x30000); + Assert.Fail("The PackCodePoint method should fail for code points outside of the valid range."); + } + catch (ArgumentOutOfRangeException) + { + } + + try + { + UnihanCharacterData.UnpackCodePoint(0x20000); + Assert.Fail("The UnpackCodePoint method should fail for code points outside of the valid range."); + } + catch (ArgumentOutOfRangeException) + { + } + } +#endif + } +} diff --git a/UnicodeInformation.Tests/UnicodeInformation.Tests.csproj b/UnicodeInformation.Tests/UnicodeInformation.Tests.csproj index 4f98d32..1da1343 100644 --- a/UnicodeInformation.Tests/UnicodeInformation.Tests.csproj +++ b/UnicodeInformation.Tests/UnicodeInformation.Tests.csproj @@ -60,8 +60,10 @@ - + + +