michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include michael@0: #include "nsXPCOM.h" michael@0: #include "nsIUnicodeNormalizer.h" michael@0: #include "nsStringAPI.h" michael@0: #include "nsCharTraits.h" michael@0: #include "nsServiceManagerUtils.h" michael@0: michael@0: struct testcaseLine { michael@0: wchar_t* c1; michael@0: wchar_t* c2; michael@0: wchar_t* c3; michael@0: wchar_t* c4; michael@0: wchar_t* c5; michael@0: char* description; michael@0: }; michael@0: michael@0: #ifdef DEBUG_smontagu michael@0: #define DEBUG_NAMED_TESTCASE(t, s) \ michael@0: printf(t ": "); \ michael@0: for (uint32_t i = 0; i < s.Length(); ++i) \ michael@0: printf("%x ", s.CharAt(i)); \ michael@0: printf("\n") michael@0: #else michael@0: #define DEBUG_NAMED_TESTCASE(t, s) michael@0: #endif michael@0: michael@0: #define DEBUG_TESTCASE(x) DEBUG_NAMED_TESTCASE(#x, x) michael@0: michael@0: #define NORMALIZE_AND_COMPARE(base, comparison, form, description) \ michael@0: normalized.Truncate();\ michael@0: normalizer->NormalizeUnicode##form(comparison, normalized);\ michael@0: DEBUG_NAMED_TESTCASE(#form "(" #comparison ")", normalized);\ michael@0: if (!base.Equals(normalized)) {\ michael@0: rv = false;\ michael@0: showError(description, #base " != " #form "(" #comparison ")\n");\ michael@0: } michael@0: michael@0: NS_DEFINE_CID(kUnicodeNormalizerCID, NS_UNICODE_NORMALIZER_CID); michael@0: michael@0: nsIUnicodeNormalizer *normalizer; michael@0: bool verboseMode = false; michael@0: michael@0: #include "NormalizationData.h" michael@0: michael@0: void showError(const char* description, const char* errorText) michael@0: { michael@0: if (verboseMode) michael@0: printf("%s failed: %s", description, errorText); michael@0: } michael@0: michael@0: bool TestInvariants(testcaseLine* testLine) michael@0: { michael@0: nsAutoString c1, c2, c3, c4, c5, normalized; michael@0: c1 = nsDependentString((char16_t*)testLine->c1); michael@0: c2 = nsDependentString((char16_t*)testLine->c2); michael@0: c3 = nsDependentString((char16_t*)testLine->c3); michael@0: c4 = nsDependentString((char16_t*)testLine->c4); michael@0: c5 = nsDependentString((char16_t*)testLine->c5); michael@0: bool rv = true; michael@0: michael@0: /* michael@0: 1. The following invariants must be true for all conformant implementations michael@0: michael@0: NFC michael@0: c2 == NFC(c1) == NFC(c2) == NFC(c3) michael@0: */ michael@0: DEBUG_TESTCASE(c2); michael@0: NORMALIZE_AND_COMPARE(c2, c1, NFC, testLine->description); michael@0: NORMALIZE_AND_COMPARE(c2, c2, NFC, testLine->description); michael@0: NORMALIZE_AND_COMPARE(c2, c3, NFC, testLine->description); michael@0: michael@0: /* michael@0: c4 == NFC(c4) == NFC(c5) michael@0: */ michael@0: DEBUG_TESTCASE(c4); michael@0: NORMALIZE_AND_COMPARE(c4, c4, NFC, testLine->description); michael@0: NORMALIZE_AND_COMPARE(c4, c5, NFC, testLine->description); michael@0: michael@0: /* michael@0: NFD michael@0: c3 == NFD(c1) == NFD(c2) == NFD(c3) michael@0: */ michael@0: DEBUG_TESTCASE(c3); michael@0: NORMALIZE_AND_COMPARE(c3, c1, NFD, testLine->description); michael@0: NORMALIZE_AND_COMPARE(c3, c2, NFD, testLine->description); michael@0: NORMALIZE_AND_COMPARE(c3, c3, NFD, testLine->description); michael@0: /* michael@0: c5 == NFD(c4) == NFD(c5) michael@0: */ michael@0: DEBUG_TESTCASE(c5); michael@0: NORMALIZE_AND_COMPARE(c5, c4, NFD, testLine->description); michael@0: NORMALIZE_AND_COMPARE(c5, c5, NFD, testLine->description); michael@0: michael@0: /* michael@0: NFKC michael@0: c4 == NFKC(c1) == NFKC(c2) == NFKC(c3) == NFKC(c4) == NFKC(c5) michael@0: */ michael@0: DEBUG_TESTCASE(c4); michael@0: NORMALIZE_AND_COMPARE(c4, c1, NFKC, testLine->description); michael@0: NORMALIZE_AND_COMPARE(c4, c2, NFKC, testLine->description); michael@0: NORMALIZE_AND_COMPARE(c4, c3, NFKC, testLine->description); michael@0: NORMALIZE_AND_COMPARE(c4, c4, NFKC, testLine->description); michael@0: NORMALIZE_AND_COMPARE(c4, c5, NFKC, testLine->description); michael@0: michael@0: /* michael@0: NFKD michael@0: c5 == NFKD(c1) == NFKD(c2) == NFKD(c3) == NFKD(c4) == NFKD(c5) michael@0: */ michael@0: DEBUG_TESTCASE(c5); michael@0: NORMALIZE_AND_COMPARE(c5, c1, NFKD, testLine->description); michael@0: NORMALIZE_AND_COMPARE(c5, c2, NFKD, testLine->description); michael@0: NORMALIZE_AND_COMPARE(c5, c3, NFKD, testLine->description); michael@0: NORMALIZE_AND_COMPARE(c5, c4, NFKD, testLine->description); michael@0: NORMALIZE_AND_COMPARE(c5, c5, NFKD, testLine->description); michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: uint32_t UTF32CodepointFromTestcase(testcaseLine* testLine) michael@0: { michael@0: if (!IS_SURROGATE(testLine->c1[0])) michael@0: return testLine->c1[0]; michael@0: michael@0: NS_ASSERTION(NS_IS_HIGH_SURROGATE(testLine->c1[0]) && michael@0: NS_IS_LOW_SURROGATE(testLine->c1[1]), michael@0: "Test data neither in BMP nor legal surrogate pair"); michael@0: return SURROGATE_TO_UCS4(testLine->c1[0], testLine->c1[1]); michael@0: } michael@0: michael@0: bool TestUnspecifiedCodepoint(uint32_t codepoint) michael@0: { michael@0: bool rv = true; michael@0: char16_t unicharArray[3]; michael@0: nsAutoString X, normalized; michael@0: char description[9]; michael@0: michael@0: if (IS_IN_BMP(codepoint)) { michael@0: unicharArray[0] = codepoint; michael@0: unicharArray[1] = 0; michael@0: X = nsDependentString(unicharArray); michael@0: } michael@0: else { michael@0: unicharArray[0] = H_SURROGATE(codepoint); michael@0: unicharArray[1] = L_SURROGATE(codepoint); michael@0: unicharArray[2] = 0; michael@0: X = nsDependentString(unicharArray); michael@0: } michael@0: michael@0: /* michael@0: 2. For every code point X assigned in this version of Unicode that is not specifically michael@0: listed in Part 1, the following invariants must be true for all conformant michael@0: implementations: michael@0: michael@0: X == NFC(X) == NFD(X) == NFKC(X) == NFKD(X) michael@0: */ michael@0: DEBUG_TESTCASE(X); michael@0: sprintf(description, "U+%04X", codepoint); michael@0: NORMALIZE_AND_COMPARE(X, X, NFC, description); michael@0: NORMALIZE_AND_COMPARE(X, X, NFD, description); michael@0: NORMALIZE_AND_COMPARE(X, X, NFKC, description); michael@0: NORMALIZE_AND_COMPARE(X, X, NFKD, description); michael@0: return rv; michael@0: } michael@0: michael@0: void TestPart0() michael@0: { michael@0: printf("Test Part0: Specific cases\n"); michael@0: michael@0: uint32_t i = 0; michael@0: uint32_t numFailed = 0; michael@0: uint32_t numPassed = 0; michael@0: michael@0: while (Part0TestData[i].c1[0] != 0) { michael@0: if (TestInvariants(&Part0TestData[i++])) michael@0: ++numPassed; michael@0: else michael@0: ++numFailed; michael@0: } michael@0: printf(" %d cases passed, %d failed\n\n", numPassed, numFailed); michael@0: } michael@0: michael@0: void TestPart1() michael@0: { michael@0: printf("Test Part1: Character by character test\n"); michael@0: michael@0: uint32_t i = 0; michael@0: uint32_t numFailed = 0; michael@0: uint32_t numPassed = 0; michael@0: uint32_t codepoint; michael@0: uint32_t testDataCodepoint = UTF32CodepointFromTestcase(&Part1TestData[i]); michael@0: michael@0: for (codepoint = 1; codepoint < 0x110000; ++codepoint) { michael@0: if (testDataCodepoint == codepoint) { michael@0: if (TestInvariants(&Part1TestData[i])) michael@0: ++numPassed; michael@0: else michael@0: ++numFailed; michael@0: testDataCodepoint = UTF32CodepointFromTestcase(&Part1TestData[++i]); michael@0: } else { michael@0: if (TestUnspecifiedCodepoint(codepoint)) michael@0: ++numPassed; michael@0: else michael@0: ++numFailed; michael@0: } michael@0: } michael@0: printf(" %d cases passed, %d failed\n\n", numPassed, numFailed); michael@0: } michael@0: michael@0: void TestPart2() michael@0: { michael@0: printf("Test Part2: Canonical Order Test\n"); michael@0: michael@0: uint32_t i = 0; michael@0: uint32_t numFailed = 0; michael@0: uint32_t numPassed = 0; michael@0: michael@0: while (Part2TestData[i].c1[0] != 0) { michael@0: if (TestInvariants(&Part2TestData[i++])) michael@0: ++numPassed; michael@0: else michael@0: ++numFailed; michael@0: } michael@0: printf(" %d cases passed, %d failed\n\n", numPassed, numFailed); michael@0: } michael@0: michael@0: void TestPart3() michael@0: { michael@0: printf("Test Part3: PRI #29 Test\n"); michael@0: michael@0: uint32_t i = 0; michael@0: uint32_t numFailed = 0; michael@0: uint32_t numPassed = 0; michael@0: michael@0: while (Part3TestData[i].c1[0] != 0) { michael@0: if (TestInvariants(&Part3TestData[i++])) michael@0: ++numPassed; michael@0: else michael@0: ++numFailed; michael@0: } michael@0: printf(" %d cases passed, %d failed\n\n", numPassed, numFailed); michael@0: } michael@0: michael@0: int main(int argc, char** argv) { michael@0: if (sizeof(wchar_t) != 2) { michael@0: printf("This test can only be run where sizeof(wchar_t) == 2\n"); michael@0: return 1; michael@0: } michael@0: if (strlen(versionText) == 0) { michael@0: printf("No testcases: to run the tests generate the header file using\n"); michael@0: printf(" perl genNormalizationData.pl\n"); michael@0: printf("in intl/unichar/tools and rebuild\n"); michael@0: return 1; michael@0: } michael@0: michael@0: printf("NormalizationTest: test nsIUnicodeNormalizer. UCD version: %s\n", michael@0: versionText); michael@0: if (argc <= 1) michael@0: verboseMode = false; michael@0: else if ((argc == 2) && (!strcmp(argv[1], "-v"))) michael@0: verboseMode = true; michael@0: else { michael@0: printf(" Usage: NormalizationTest [OPTION]..\n"); michael@0: printf("Options:\n"); michael@0: printf(" -v Verbose mode\n"); michael@0: return 1; michael@0: } michael@0: michael@0: nsresult rv = NS_InitXPCOM2(nullptr, nullptr, nullptr); michael@0: if (NS_FAILED(rv)) { michael@0: printf("NS_InitXPCOM2 failed\n"); michael@0: return 1; michael@0: } michael@0: michael@0: normalizer = nullptr; michael@0: nsresult res; michael@0: res = CallGetService(kUnicodeNormalizerCID, &normalizer); michael@0: michael@0: if(NS_FAILED(res) || !normalizer) { michael@0: printf("GetService failed\n"); michael@0: return 1; michael@0: } michael@0: michael@0: TestPart0(); michael@0: TestPart1(); michael@0: TestPart2(); michael@0: TestPart3(); michael@0: michael@0: NS_RELEASE(normalizer); michael@0: michael@0: printf("Test finished \n"); michael@0: return 0; michael@0: }