OLD | NEW |
1 /* | 1 /* |
2 * Copyright (C) 2013 Google Inc. All rights reserved. | 2 * Copyright (C) 2013 Google Inc. All rights reserved. |
3 * | 3 * |
4 * Redistribution and use in source and binary forms, with or without | 4 * Redistribution and use in source and binary forms, with or without |
5 * modification, are permitted provided that the following conditions are | 5 * modification, are permitted provided that the following conditions are |
6 * met: | 6 * met: |
7 * | 7 * |
8 * * Redistributions of source code must retain the above copyright | 8 * * Redistributions of source code must retain the above copyright |
9 * notice, this list of conditions and the following disclaimer. | 9 * notice, this list of conditions and the following disclaimer. |
10 * * Redistributions in binary form must reproduce the above | 10 * * Redistributions in binary form must reproduce the above |
(...skipping 14 matching lines...) Expand all Loading... |
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
29 */ | 29 */ |
30 | 30 |
31 #include "config.h" | 31 #include "config.h" |
32 #include "platform/text/BidiResolver.h" | 32 #include "platform/text/BidiResolver.h" |
33 | 33 |
34 #include "platform/graphics/TextRunIterator.h" | 34 #include "platform/graphics/TextRunIterator.h" |
| 35 #include "platform/text/BidiTestHarness.h" |
35 #include "wtf/OwnPtr.h" | 36 #include "wtf/OwnPtr.h" |
| 37 #include <fstream> |
36 #include <gtest/gtest.h> | 38 #include <gtest/gtest.h> |
37 | 39 |
38 namespace { | 40 namespace { |
39 | 41 |
40 using namespace WTF; | 42 using namespace WTF; |
41 using namespace WebCore; | 43 using namespace WebCore; |
42 | 44 |
43 TEST(BidiResolver, Basic) | 45 TEST(BidiResolver, Basic) |
44 { | 46 { |
45 bool hasStrongDirectionality; | 47 bool hasStrongDirectionality; |
46 String value("foo"); | 48 String value("foo"); |
47 TextRun run(value); | 49 TextRun run(value); |
48 BidiResolver<TextRunIterator, BidiCharacterRun> bidiResolver; | 50 BidiResolver<TextRunIterator, BidiCharacterRun> bidiResolver; |
49 bidiResolver.setStatus(BidiStatus(run.direction(), run.directionalOverride()
)); | 51 bidiResolver.setStatus(BidiStatus(run.direction(), run.directionalOverride()
)); |
50 bidiResolver.setPositionIgnoringNestedIsolates(TextRunIterator(&run, 0)); | 52 bidiResolver.setPositionIgnoringNestedIsolates(TextRunIterator(&run, 0)); |
51 TextDirection direction = bidiResolver.determineParagraphDirectionality(&has
StrongDirectionality); | 53 TextDirection direction = bidiResolver.determineParagraphDirectionality(&has
StrongDirectionality); |
52 EXPECT_TRUE(hasStrongDirectionality); | 54 EXPECT_TRUE(hasStrongDirectionality); |
53 EXPECT_EQ(direction, LTR); | 55 EXPECT_EQ(direction, LTR); |
54 } | 56 } |
55 | 57 |
| 58 class BidiTestRunner { |
| 59 public: |
| 60 BidiTestRunner() |
| 61 : m_testsRun(0) |
| 62 , m_testsSkipped(0) |
| 63 , m_ignoredCharFailures(0) |
| 64 , m_levelFailures(0) |
| 65 , m_orderFailures(0) |
| 66 { |
| 67 } |
| 68 |
| 69 void skipTestsWith(UChar codepoint) |
| 70 { |
| 71 m_skippedCodePoints.insert(codepoint); |
| 72 } |
| 73 |
| 74 void runTest(const std::basic_string<UChar>& input, const std::vector<int>&
reorder, |
| 75 const std::vector<int>& levels, bidi_test::ParagraphDirection, |
| 76 const std::string& line, size_t lineNumber); |
| 77 |
| 78 size_t m_testsRun; |
| 79 size_t m_testsSkipped; |
| 80 std::set<UChar> m_skippedCodePoints; |
| 81 size_t m_ignoredCharFailures; |
| 82 size_t m_levelFailures; |
| 83 size_t m_orderFailures; |
| 84 }; |
| 85 |
| 86 // Blink's UBA does not filter out control characters, etc. Maybe it should? |
| 87 // Instead it depends on later layers of Blink to simply ignore them. |
| 88 // This function helps us emulate that to be compatible with BidiTest.txt expect
ations. |
| 89 static bool isNonRenderedCodePoint(UChar c) |
| 90 { |
| 91 // The tests also expect us to ignore soft-hyphen. |
| 92 if (c == 0xAD) |
| 93 return true; |
| 94 // Control characters are not rendered: |
| 95 return c >= 0x202A && c <= 0x202E; |
| 96 // But it seems to expect LRI, etc. to be rendered!? |
56 } | 97 } |
| 98 |
| 99 std::string diffString(const std::vector<int>& actual, const std::vector<int>& e
xpected) |
| 100 { |
| 101 std::ostringstream diff; |
| 102 diff << "actual: "; |
| 103 // This is the magical way to print a vector to a stream, clear, right? |
| 104 std::copy(actual.begin(), actual.end(), std::ostream_iterator<int>(diff, " "
)); |
| 105 diff << " expected: "; |
| 106 std::copy(expected.begin(), expected.end(), std::ostream_iterator<int>(diff,
" ")); |
| 107 return diff.str(); |
| 108 } |
| 109 |
| 110 TextDirection determineParagraphDirectionality(const TextRun& textRun) |
| 111 { |
| 112 BidiResolver<TextRunIterator, BidiCharacterRun> resolver; |
| 113 resolver.setStatus(BidiStatus(LTR, false)); |
| 114 resolver.setPositionIgnoringNestedIsolates(TextRunIterator(&textRun, 0)); |
| 115 return resolver.determineParagraphDirectionality(); |
| 116 } |
| 117 |
| 118 void BidiTestRunner::runTest(const std::basic_string<UChar>& input, const std::v
ector<int>& expectedOrder, |
| 119 const std::vector<int>& expectedLevels, bidi_test::ParagraphDirection paragr
aphDirection, |
| 120 const std::string& line, size_t lineNumber) |
| 121 { |
| 122 if (!m_skippedCodePoints.empty()) { |
| 123 for (size_t i = 0; i < input.size(); i++) { |
| 124 if (m_skippedCodePoints.count(input[i])) { |
| 125 m_testsSkipped++; |
| 126 return; |
| 127 } |
| 128 } |
| 129 } |
| 130 |
| 131 m_testsRun++; |
| 132 |
| 133 TextRun textRun(input.data(), input.size()); |
| 134 switch (paragraphDirection) { |
| 135 case bidi_test::DirectionAutoLTR: |
| 136 textRun.setDirection(determineParagraphDirectionality(textRun)); |
| 137 break; |
| 138 case bidi_test::DirectionLTR: |
| 139 textRun.setDirection(LTR); |
| 140 break; |
| 141 case bidi_test::DirectionRTL: |
| 142 textRun.setDirection(RTL); |
| 143 break; |
| 144 } |
| 145 BidiResolver<TextRunIterator, BidiCharacterRun> resolver; |
| 146 resolver.setStatus(BidiStatus(textRun.direction(), textRun.directionalOverri
de())); |
| 147 resolver.setPositionIgnoringNestedIsolates(TextRunIterator(&textRun, 0)); |
| 148 |
| 149 BidiRunList<BidiCharacterRun>& runs = resolver.runs(); |
| 150 resolver.createBidiRunsForLine(TextRunIterator(&textRun, textRun.length())); |
| 151 |
| 152 std::ostringstream errorContext; |
| 153 errorContext << ", line " << lineNumber << " \"" << line << "\""; |
| 154 errorContext << " context: " << bidi_test::nameFromParagraphDirection(paragr
aphDirection); |
| 155 |
| 156 std::vector<int> actualOrder; |
| 157 std::vector<int> actualLevels; |
| 158 actualLevels.assign(input.size(), -1); |
| 159 BidiCharacterRun* run = runs.firstRun(); |
| 160 while (run) { |
| 161 // Blink's UBA just makes runs, the actual ordering of the display of ch
aracters |
| 162 // is handled later in our pipeline, so we fake it here: |
| 163 bool reversed = run->reversed(false); |
| 164 ASSERT(run->stop() >= run->start()); |
| 165 size_t length = run->stop() - run->start(); |
| 166 for (size_t i = 0; i < length; i++) { |
| 167 int inputIndex = reversed ? run->stop() - i - 1 : run->start() + i; |
| 168 if (!isNonRenderedCodePoint(input[inputIndex])) |
| 169 actualOrder.push_back(inputIndex); |
| 170 // BidiTest.txt gives expected level data in the order of the origin
al input. |
| 171 actualLevels[inputIndex] = run->level(); |
| 172 } |
| 173 run = run->next(); |
| 174 } |
| 175 |
| 176 if (expectedOrder.size() != actualOrder.size()) { |
| 177 m_ignoredCharFailures++; |
| 178 EXPECT_EQ(expectedOrder.size(), actualOrder.size()) << errorContext.str(
); |
| 179 } else if (expectedOrder != actualOrder) { |
| 180 m_orderFailures++; |
| 181 printf("ORDER %s%s\n", diffString(actualOrder, expectedOrder).c_str(), e
rrorContext.str().c_str()); |
| 182 } |
| 183 |
| 184 if (expectedLevels.size() != actualLevels.size()) { |
| 185 m_ignoredCharFailures++; |
| 186 EXPECT_EQ(expectedLevels.size(), actualLevels.size()) << errorContext.st
r(); |
| 187 } else { |
| 188 for (size_t i = 0; i < expectedLevels.size(); i++) { |
| 189 // level == -1 means the level should be ignored. |
| 190 if (expectedLevels[i] == actualLevels[i] || expectedLevels[i] == -1) |
| 191 continue; |
| 192 |
| 193 printf("LEVELS %s%s\n", diffString(actualLevels, expectedLevels).c_s
tr(), errorContext.str().c_str()); |
| 194 m_levelFailures++; |
| 195 break; |
| 196 } |
| 197 } |
| 198 } |
| 199 |
| 200 |
| 201 TEST(BidiResolver, BidiTest_txt) |
| 202 { |
| 203 BidiTestRunner runner; |
| 204 // Blink's Unicode Bidi Algorithm (UBA) doesn't yet support the |
| 205 // new isolate directives from Unicode 6.3: |
| 206 // http://www.unicode.org/reports/tr9/#Explicit_Directional_Isolates |
| 207 runner.skipTestsWith(0x2066); // LRI |
| 208 runner.skipTestsWith(0x2067); // RLI |
| 209 runner.skipTestsWith(0x2068); // FSI |
| 210 runner.skipTestsWith(0x2069); // PDI |
| 211 |
| 212 // This code wants to use PathService from base/path_service.h |
| 213 // but we aren't allowed to depend on base/ directly from Blink yet. |
| 214 // Alternatively we could use: |
| 215 // WebKit::Platform::current()->unitTestSupport()->webKitRootDir() |
| 216 // and a relative path, but that would require running inside |
| 217 // webkit_unit_tests (to have a functioning Platform object). |
| 218 // The file we want is: |
| 219 // src/third_party/icu/source/test/testdata/BidiTest.txt |
| 220 // but we don't have any good way to find it from this unittest. |
| 221 // Just assume we're running this test manually for now. On the |
| 222 // bots we just print a warning that we can't find the test file. |
| 223 std::string bidiTestPath = "BidiTest.txt"; |
| 224 std::ifstream bidiTestFile(bidiTestPath.c_str()); |
| 225 if (!bidiTestFile.is_open()) { |
| 226 printf("ERROR: Failed to open BidiTest.txt, cannot run tests.\n"); |
| 227 return; |
| 228 } |
| 229 |
| 230 bidi_test::Harness<BidiTestRunner> harness(runner); |
| 231 harness.parse(bidiTestFile); |
| 232 bidiTestFile.close(); |
| 233 |
| 234 if (runner.m_testsSkipped) |
| 235 printf("WARNING: Skipped %zu tests.\n", runner.m_testsSkipped); |
| 236 printf("Ran %zu tests: %zu level failures %zu order failures.\n", |
| 237 runner.m_testsRun, runner.m_levelFailures, runner.m_orderFailures); |
| 238 |
| 239 // The unittest harness only pays attention to GTest output, so we verify |
| 240 // that the tests behaved as expected: |
| 241 EXPECT_EQ(runner.m_testsRun, 352098u); |
| 242 EXPECT_EQ(runner.m_testsSkipped, 418143u); |
| 243 EXPECT_EQ(runner.m_ignoredCharFailures, 0u); |
| 244 EXPECT_EQ(runner.m_levelFailures, 44887u); |
| 245 EXPECT_EQ(runner.m_orderFailures, 19153u); |
| 246 } |
| 247 |
| 248 } |
OLD | NEW |