Chromium Code Reviews| 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? | |
|
leviw_travelin_and_unemployed
2013/10/28 21:33:11
Do you mean that we put them into the BidiRuns? If
| |
| 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!? | |
|
leviw_travelin_and_unemployed
2013/10/28 21:33:11
Wat? This seems so weird...
| |
| 56 } | 97 } |
| 98 | |
| 99 std::string diffString(const std::vector<int>& actual, const std::vector<int>& e xpected) | |
| 100 { | |
| 101 std::ostringstream diff; | |
|
leviw_travelin_and_unemployed
2013/10/28 21:33:11
Seems like you may just want "using namespace std"
| |
| 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 size_t actualIndex = 0; | |
| 161 size_t actualLength = 0; | |
| 162 while (run) { | |
| 163 // Blink's UBA just makes runs, the actual ordering of the display of ch aracters | |
| 164 // is handled later in our pipeline, so we fake it here: | |
| 165 bool reversed = run->reversed(false); | |
| 166 int length = run->stop() - run->start(); | |
| 167 for (size_t i = 0; i < length; i++) { | |
| 168 int inputIndex = reversed ? run->stop() - i - 1 : run->start() + i; | |
| 169 if (!isNonRenderedCodePoint(input[inputIndex])) | |
| 170 actualOrder.push_back(inputIndex); | |
| 171 // BidiTest.txt gives expected level data in the order of the origin al input. | |
| 172 actualLevels[inputIndex] = run->level(); | |
| 173 } | |
| 174 run = run->next(); | |
| 175 } | |
| 176 | |
| 177 if (expectedOrder.size() != actualOrder.size()) { | |
| 178 m_ignoredCharFailures++; | |
| 179 EXPECT_EQ(expectedOrder.size(), actualOrder.size()) << errorContext.str( ); | |
| 180 } else if (expectedOrder != actualOrder) { | |
| 181 m_orderFailures++; | |
| 182 std::cout << "ORDER " << diffString(actualOrder, expectedOrder) << error Context.str() << std::endl; | |
| 183 } | |
| 184 | |
| 185 if (expectedLevels.size() != actualLevels.size()) { | |
| 186 m_ignoredCharFailures++; | |
| 187 EXPECT_EQ(expectedLevels.size(), actualLevels.size()) << errorContext.st r(); | |
| 188 } else { | |
| 189 for (size_t i = 0; i < expectedLevels.size(); i++) { | |
| 190 // level == -1 means the level should be ignored. | |
| 191 if (expectedLevels[i] == actualLevels[i] || expectedLevels[i] == -1) | |
| 192 continue; | |
| 193 | |
| 194 std::cout << "LEVELS " << diffString(actualLevels, expectedLevels) < < errorContext.str() << std::endl; | |
| 195 m_levelFailures++; | |
| 196 break; | |
| 197 } | |
| 198 } | |
| 199 } | |
| 200 | |
| 201 | |
| 202 TEST(BidiResolver, BidiTest_txt) | |
| 203 { | |
| 204 BidiTestRunner runner; | |
| 205 // Blink's Unicode Bidi Algorithm (UBA) doesn't yet support the | |
| 206 // new isolate directives from Unicode 6.3: | |
| 207 // http://www.unicode.org/reports/tr9/#Explicit_Directional_Isolates | |
| 208 runner.skipTestsWith(0x2066); // LRI | |
| 209 runner.skipTestsWith(0x2067); // RLI | |
| 210 runner.skipTestsWith(0x2068); // FSI | |
| 211 runner.skipTestsWith(0x2069); // PDI | |
| 212 | |
| 213 // This code wants to use PathService from base/path_service.h | |
| 214 // but we aren't allowed to depend on base/ directly from Blink yet. | |
| 215 // Alternatively we could use: | |
| 216 // WebKit::Platform::current()->unitTestSupport()->webKitRootDir() | |
| 217 // and a relative path, but that would require running inside | |
| 218 // webkit_unit_tests (to have a functioning Platform object). | |
| 219 // The file we want is: | |
| 220 // src/third_party/icu/source/test/testdata/BidiTest.txt | |
| 221 // but we don't have any good way to find it from this unittest. | |
| 222 // Just assume we're running this test manually for now. On the | |
| 223 // bots we just print a warning that we can't find the test file. | |
| 224 std::string bidiTestPath = "BidiTest.txt"; | |
| 225 std::ifstream bidiTestFile(bidiTestPath); | |
| 226 if (!bidiTestFile.is_open()) { | |
| 227 std::cout << "ERROR: Failed to open BidiTest.txt, cannot run tests." << std::endl; | |
| 228 return; | |
| 229 } | |
| 230 | |
| 231 bidi_test::Harness<BidiTestRunner> harness(runner); | |
| 232 harness.parse(bidiTestFile); | |
| 233 bidiTestFile.close(); | |
| 234 | |
| 235 if (runner.m_testsSkipped) | |
| 236 std::cout << "WARNING: Skipped " << runner.m_testsSkipped << " tests." < < std::endl; | |
| 237 std::cout << "Ran " << runner.m_testsRun << " tests: "; | |
| 238 std::cout << runner.m_levelFailures << " level failures, "; | |
| 239 std::cout << runner.m_orderFailures << " order failures." << std::endl; | |
| 240 | |
| 241 // The unittest harness only pays attention to GTest output, so we verify | |
| 242 // that the tests behaved as expected: | |
| 243 EXPECT_EQ(runner.m_testsRun, 352098); | |
| 244 EXPECT_EQ(runner.m_testsSkipped, 418143); | |
| 245 EXPECT_EQ(runner.m_ignoredCharFailures, 0); | |
| 246 EXPECT_EQ(runner.m_levelFailures, 44887); | |
| 247 EXPECT_EQ(runner.m_orderFailures, 19153); | |
| 248 } | |
| 249 | |
| 250 } | |
| OLD | NEW |