| 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);
 | 
| +}
 | 
| +
 | 
|  }
 | 
| 
 |