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" |
36 #include <gtest/gtest.h> | 37 #include <gtest/gtest.h> |
38 #include <istream> | |
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 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 printf("ORDER %s%s\n", diffString(actualOrder, expectedOrder).c_str(), e rrorContext.str().c_str()); | |
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 printf("LEVELS %s%s\n", diffString(actualLevels, expectedLevels).c_s tr(), errorContext.str().c_str()); | |
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); | |
Jeffrey Yasskin
2013/10/29 23:16:03
This needs the #include <fstream>; it's the other
| |
226 if (!bidiTestFile.is_open()) { | |
227 printf("ERROR: Failed to open BidiTest.txt, cannot run tests.\n"); | |
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 printf("WARNING: Skipped %d tests.\n", runner.m_testsSkipped); | |
237 printf("Ran %d tests: %d level failures %d order failures.\n", | |
238 runner.m_testsRun, runner.m_levelFailures, runner.m_orderFailures); | |
239 | |
240 // The unittest harness only pays attention to GTest output, so we verify | |
241 // that the tests behaved as expected: | |
242 EXPECT_EQ(runner.m_testsRun, 352098); | |
243 EXPECT_EQ(runner.m_testsSkipped, 418143); | |
244 EXPECT_EQ(runner.m_ignoredCharFailures, 0); | |
245 EXPECT_EQ(runner.m_levelFailures, 44887); | |
246 EXPECT_EQ(runner.m_orderFailures, 19153); | |
247 } | |
248 | |
249 } | |
OLD | NEW |