Index: Source/platform/text/BidiResolverTest.cpp |
diff --git a/Source/platform/text/BidiResolverTest.cpp b/Source/platform/text/BidiResolverTest.cpp |
index 90c294dd636e45cc6aa53ca4781f07f0eec6b6ba..9f24a781a02f6a48b0ea88c0115daeddae0049b8 100644 |
--- a/Source/platform/text/BidiResolverTest.cpp |
+++ b/Source/platform/text/BidiResolverTest.cpp |
@@ -32,7 +32,9 @@ |
#include "platform/text/BidiResolver.h" |
#include "platform/graphics/TextRunIterator.h" |
+#include "platform/text/BidiTestHarness.h" |
#include "wtf/OwnPtr.h" |
+#include <fstream> |
#include <gtest/gtest.h> |
namespace { |
@@ -53,4 +55,194 @@ TEST(BidiResolver, Basic) |
EXPECT_EQ(direction, LTR); |
} |
+class BidiTestRunner { |
+public: |
+ BidiTestRunner() |
+ : m_testsRun(0) |
+ , m_testsSkipped(0) |
+ , m_ignoredCharFailures(0) |
+ , m_levelFailures(0) |
+ , m_orderFailures(0) |
+ { |
+ } |
+ |
+ void skipTestsWith(UChar codepoint) |
+ { |
+ m_skippedCodePoints.insert(codepoint); |
+ } |
+ |
+ void runTest(const std::basic_string<UChar>& input, const std::vector<int>& reorder, |
+ const std::vector<int>& levels, bidi_test::ParagraphDirection, |
+ const std::string& line, size_t lineNumber); |
+ |
+ size_t m_testsRun; |
+ size_t m_testsSkipped; |
+ std::set<UChar> m_skippedCodePoints; |
+ size_t m_ignoredCharFailures; |
+ size_t m_levelFailures; |
+ size_t m_orderFailures; |
+}; |
+ |
+// Blink's UBA does not filter out control characters, etc. Maybe it should? |
+// Instead it depends on later layers of Blink to simply ignore them. |
+// This function helps us emulate that to be compatible with BidiTest.txt expectations. |
+static bool isNonRenderedCodePoint(UChar c) |
+{ |
+ // The tests also expect us to ignore soft-hyphen. |
+ if (c == 0xAD) |
+ return true; |
+ // Control characters are not rendered: |
+ return c >= 0x202A && c <= 0x202E; |
+ // But it seems to expect LRI, etc. to be rendered!? |
+} |
+ |
+std::string diffString(const std::vector<int>& actual, const std::vector<int>& expected) |
+{ |
+ std::ostringstream diff; |
+ diff << "actual: "; |
+ // This is the magical way to print a vector to a stream, clear, right? |
+ std::copy(actual.begin(), actual.end(), std::ostream_iterator<int>(diff, " ")); |
+ diff << " expected: "; |
+ std::copy(expected.begin(), expected.end(), std::ostream_iterator<int>(diff, " ")); |
+ return diff.str(); |
+} |
+ |
+TextDirection determineParagraphDirectionality(const TextRun& textRun) |
+{ |
+ BidiResolver<TextRunIterator, BidiCharacterRun> resolver; |
+ resolver.setStatus(BidiStatus(LTR, false)); |
+ resolver.setPositionIgnoringNestedIsolates(TextRunIterator(&textRun, 0)); |
+ return resolver.determineParagraphDirectionality(); |
+} |
+ |
+void BidiTestRunner::runTest(const std::basic_string<UChar>& input, const std::vector<int>& expectedOrder, |
+ const std::vector<int>& expectedLevels, bidi_test::ParagraphDirection paragraphDirection, |
+ const std::string& line, size_t lineNumber) |
+{ |
+ if (!m_skippedCodePoints.empty()) { |
+ for (size_t i = 0; i < input.size(); i++) { |
+ if (m_skippedCodePoints.count(input[i])) { |
+ m_testsSkipped++; |
+ return; |
+ } |
+ } |
+ } |
+ |
+ m_testsRun++; |
+ |
+ TextRun textRun(input.data(), input.size()); |
+ switch (paragraphDirection) { |
+ case bidi_test::DirectionAutoLTR: |
+ textRun.setDirection(determineParagraphDirectionality(textRun)); |
+ break; |
+ case bidi_test::DirectionLTR: |
+ textRun.setDirection(LTR); |
+ break; |
+ case bidi_test::DirectionRTL: |
+ textRun.setDirection(RTL); |
+ break; |
+ } |
+ BidiResolver<TextRunIterator, BidiCharacterRun> resolver; |
+ resolver.setStatus(BidiStatus(textRun.direction(), textRun.directionalOverride())); |
+ resolver.setPositionIgnoringNestedIsolates(TextRunIterator(&textRun, 0)); |
+ |
+ BidiRunList<BidiCharacterRun>& runs = resolver.runs(); |
+ resolver.createBidiRunsForLine(TextRunIterator(&textRun, textRun.length())); |
+ |
+ std::ostringstream errorContext; |
+ errorContext << ", line " << lineNumber << " \"" << line << "\""; |
+ errorContext << " context: " << bidi_test::nameFromParagraphDirection(paragraphDirection); |
+ |
+ std::vector<int> actualOrder; |
+ std::vector<int> actualLevels; |
+ actualLevels.assign(input.size(), -1); |
+ BidiCharacterRun* run = runs.firstRun(); |
+ while (run) { |
+ // Blink's UBA just makes runs, the actual ordering of the display of characters |
+ // is handled later in our pipeline, so we fake it here: |
+ bool reversed = run->reversed(false); |
+ ASSERT(run->stop() >= run->start()); |
+ size_t length = run->stop() - run->start(); |
+ for (size_t i = 0; i < length; i++) { |
+ int inputIndex = reversed ? run->stop() - i - 1 : run->start() + i; |
+ if (!isNonRenderedCodePoint(input[inputIndex])) |
+ actualOrder.push_back(inputIndex); |
+ // BidiTest.txt gives expected level data in the order of the original input. |
+ actualLevels[inputIndex] = run->level(); |
+ } |
+ run = run->next(); |
+ } |
+ |
+ if (expectedOrder.size() != actualOrder.size()) { |
+ m_ignoredCharFailures++; |
+ EXPECT_EQ(expectedOrder.size(), actualOrder.size()) << errorContext.str(); |
+ } else if (expectedOrder != actualOrder) { |
+ m_orderFailures++; |
+ printf("ORDER %s%s\n", diffString(actualOrder, expectedOrder).c_str(), errorContext.str().c_str()); |
+ } |
+ |
+ if (expectedLevels.size() != actualLevels.size()) { |
+ m_ignoredCharFailures++; |
+ EXPECT_EQ(expectedLevels.size(), actualLevels.size()) << errorContext.str(); |
+ } else { |
+ for (size_t i = 0; i < expectedLevels.size(); i++) { |
+ // level == -1 means the level should be ignored. |
+ if (expectedLevels[i] == actualLevels[i] || expectedLevels[i] == -1) |
+ continue; |
+ |
+ printf("LEVELS %s%s\n", diffString(actualLevels, expectedLevels).c_str(), errorContext.str().c_str()); |
+ m_levelFailures++; |
+ break; |
+ } |
+ } |
+} |
+ |
+ |
+TEST(BidiResolver, BidiTest_txt) |
+{ |
+ BidiTestRunner runner; |
+ // Blink's Unicode Bidi Algorithm (UBA) doesn't yet support the |
+ // new isolate directives from Unicode 6.3: |
+ // http://www.unicode.org/reports/tr9/#Explicit_Directional_Isolates |
+ runner.skipTestsWith(0x2066); // LRI |
+ runner.skipTestsWith(0x2067); // RLI |
+ runner.skipTestsWith(0x2068); // FSI |
+ runner.skipTestsWith(0x2069); // PDI |
+ |
+ // This code wants to use PathService from base/path_service.h |
+ // but we aren't allowed to depend on base/ directly from Blink yet. |
+ // Alternatively we could use: |
+ // WebKit::Platform::current()->unitTestSupport()->webKitRootDir() |
+ // and a relative path, but that would require running inside |
+ // webkit_unit_tests (to have a functioning Platform object). |
+ // The file we want is: |
+ // src/third_party/icu/source/test/testdata/BidiTest.txt |
+ // but we don't have any good way to find it from this unittest. |
+ // Just assume we're running this test manually for now. On the |
+ // bots we just print a warning that we can't find the test file. |
+ std::string bidiTestPath = "BidiTest.txt"; |
+ std::ifstream bidiTestFile(bidiTestPath.c_str()); |
+ if (!bidiTestFile.is_open()) { |
+ printf("ERROR: Failed to open BidiTest.txt, cannot run tests.\n"); |
+ return; |
+ } |
+ |
+ bidi_test::Harness<BidiTestRunner> harness(runner); |
+ harness.parse(bidiTestFile); |
+ bidiTestFile.close(); |
+ |
+ if (runner.m_testsSkipped) |
+ printf("WARNING: Skipped %zu tests.\n", runner.m_testsSkipped); |
+ printf("Ran %zu tests: %zu level failures %zu order failures.\n", |
+ runner.m_testsRun, runner.m_levelFailures, runner.m_orderFailures); |
+ |
+ // The unittest harness only pays attention to GTest output, so we verify |
+ // that the tests behaved as expected: |
+ EXPECT_EQ(runner.m_testsRun, 352098u); |
+ EXPECT_EQ(runner.m_testsSkipped, 418143u); |
+ EXPECT_EQ(runner.m_ignoredCharFailures, 0u); |
+ EXPECT_EQ(runner.m_levelFailures, 44887u); |
+ EXPECT_EQ(runner.m_orderFailures, 19153u); |
+} |
+ |
} |