|
1 /* |
|
2 * Copyright (c) 2002-2009 Nokia Corporation and/or its subsidiary(-ies). |
|
3 * All rights reserved. |
|
4 * This component and the accompanying materials are made available |
|
5 * under the terms of "Eclipse Public License v1.0" |
|
6 * which accompanies this distribution, and is available |
|
7 * at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
8 * |
|
9 * Initial Contributors: |
|
10 * Nokia Corporation - initial contribution. |
|
11 * |
|
12 * Contributors: |
|
13 * |
|
14 * Description: |
|
15 * Test code for MTmSource functionality |
|
16 * |
|
17 */ |
|
18 |
|
19 |
|
20 #include "TAGMA.H" |
|
21 #include <e32test.h> |
|
22 |
|
23 #ifdef SYMBIAN_ENABLE_SPLIT_HEADERS |
|
24 #include "TAGMA_INTERNAL.H" |
|
25 #endif |
|
26 |
|
27 #define UNUSED_VAR(a) a = a |
|
28 |
|
29 CTrapCleanup* TrapCleanup; |
|
30 RTest test(_L("TTmSource - MTmSource tests")); |
|
31 |
|
32 class TTestGraphicsDeviceMap : public MGraphicsDeviceMap |
|
33 { |
|
34 public: |
|
35 TInt HorizontalTwipsToPixels(TInt a) const { return a; } |
|
36 TInt VerticalTwipsToPixels(TInt a) const { return a; } |
|
37 TInt HorizontalPixelsToTwips(TInt a) const { return a; } |
|
38 TInt VerticalPixelsToTwips(TInt a) const { return a; } |
|
39 TInt GetNearestFontInTwips(CFont*&,const TFontSpec&) |
|
40 { |
|
41 return KErrGeneral; |
|
42 } |
|
43 void ReleaseFont(CFont*) {} |
|
44 }; |
|
45 |
|
46 class CTestPicture : public CPicture |
|
47 { |
|
48 public: |
|
49 CTestPicture() {} |
|
50 virtual void Draw(CGraphicsContext&, const TPoint&, const TRect&, MGraphicsDeviceMap*) const {} |
|
51 virtual void ExternalizeL(RWriteStream&) const {} |
|
52 virtual void GetOriginalSizeInTwips(TSize&) const {} |
|
53 virtual TBool LineBreakPossible(TUint aClass,TBool aBeforePicture,TBool aHaveSpaces) const |
|
54 { |
|
55 ++iRequestCount; |
|
56 if (aBeforePicture) |
|
57 { |
|
58 test(aClass == iClassBefore); |
|
59 test(aHaveSpaces == iSpacesBefore); |
|
60 return iResultBefore; |
|
61 } |
|
62 test(aClass == iClassAfter); |
|
63 test(aHaveSpaces == iSpacesAfter); |
|
64 return iResultAfter; |
|
65 } |
|
66 // expected parameters for LineBreakPossible |
|
67 TUint iClassBefore; |
|
68 TUint iClassAfter; |
|
69 TBool iSpacesBefore; |
|
70 TBool iSpacesAfter; |
|
71 // Results for breaking before/breaking after |
|
72 TBool iResultBefore; |
|
73 TBool iResultAfter; |
|
74 mutable TInt iRequestCount; |
|
75 }; |
|
76 |
|
77 class TTestSource : public MTmSource |
|
78 { |
|
79 public: |
|
80 TTestSource() : iPicturePos(-1) {} |
|
81 virtual ~TTestSource() {} |
|
82 MGraphicsDeviceMap& FormatDevice() const { return iGDM; } |
|
83 MGraphicsDeviceMap& InterpretDevice() const { return iGDM; } |
|
84 TInt DocumentLength() const |
|
85 { |
|
86 return iText->Length(); |
|
87 } |
|
88 void GetText(TInt aPos,TPtrC& aText, TTmCharFormat& aFormat) const |
|
89 { |
|
90 TTmCharFormat f; |
|
91 aFormat = f; |
|
92 aText.Set(iText->Mid(aPos)); |
|
93 } |
|
94 void GetParagraphFormatL(TInt, RTmParFormat& aFormat) const |
|
95 { |
|
96 RTmParFormat p; |
|
97 aFormat.CopyL(p); |
|
98 } |
|
99 CPicture* PictureL(TInt aPos) const |
|
100 { |
|
101 return aPos == iPicturePos? iPicture : 0; |
|
102 } |
|
103 TInt ParagraphStart(TInt) const { return 0; } |
|
104 |
|
105 virtual TUint LineBreakClass(TUint aCode, TUint& aRangeStart, |
|
106 TUint& aRangeEnd) const |
|
107 { |
|
108 if ('@' == aCode) |
|
109 { |
|
110 aRangeStart = aRangeEnd = aCode; |
|
111 return ESaLineBreakClass; |
|
112 } |
|
113 if ('0' <= aCode && aCode <= '9') |
|
114 { |
|
115 aRangeStart = aRangeEnd = aCode; |
|
116 return ELineBreakClasses + aCode - '0'; |
|
117 } |
|
118 return MTmSource::LineBreakClass(aCode, aRangeStart, aRangeEnd); |
|
119 } |
|
120 |
|
121 virtual TBool LineBreakPossible(TUint aPrevClass, TUint aNextClass, |
|
122 TBool aHaveSpaces) const |
|
123 { |
|
124 TInt first = static_cast<TInt>(aPrevClass); |
|
125 TInt second = static_cast<TInt>(aNextClass); |
|
126 TInt customCount = 0; |
|
127 if (iDirection < 0) |
|
128 { |
|
129 first = aNextClass; |
|
130 second = aPrevClass; |
|
131 } |
|
132 if (ELineBreakClasses <= first && first < ELineBreakClasses + 10) |
|
133 { |
|
134 ++customCount; |
|
135 test(first - ELineBreakClasses + '0' == FindNextCustomClass()); |
|
136 TInt countSpaces = CountSpaces(); |
|
137 test(!aHaveSpaces == !countSpaces); |
|
138 } |
|
139 if (ELineBreakClasses <= second && second < ELineBreakClasses + 10) |
|
140 { |
|
141 ++customCount; |
|
142 TInt c = FindNextCustomClass(); |
|
143 test(second - ELineBreakClasses + '0' == c); |
|
144 } |
|
145 if (0 == customCount) |
|
146 return MTmSource::LineBreakPossible(aPrevClass, aNextClass, aHaveSpaces); |
|
147 // Between custom and non-custom classes, allow a break only with spaces |
|
148 // or between @ and 5 |
|
149 if (1 == customCount) |
|
150 return aHaveSpaces |
|
151 || (first == ESaLineBreakClass && second == ELineBreakClasses + 5) |
|
152 || (second == ESaLineBreakClass && first == ELineBreakClasses + 5); |
|
153 // Allow a break with spaces except after '0' or before '9' |
|
154 if (aHaveSpaces) |
|
155 return aPrevClass != ELineBreakClasses && aNextClass != ELineBreakClasses + 9; |
|
156 // Allow a break only between a class and the class one more than it. |
|
157 return aPrevClass + 1 == aNextClass; |
|
158 } |
|
159 |
|
160 virtual TBool GetLineBreakInContext( |
|
161 const TDesC& aText, TInt aMinBreakPos, TInt aMaxBreakPos, |
|
162 TBool aForwards,TInt& aBreakPos) const |
|
163 { |
|
164 test (iDirection == (aForwards? 1 : -1)); |
|
165 // The allowable break-points should not include the first |
|
166 // and last characters of the run. |
|
167 test (aMinBreakPos != 0); |
|
168 for (TInt i = aMinBreakPos - 1; i <= aMaxBreakPos; ++i) |
|
169 test('@' == aText[i]); |
|
170 ++iSaRequestCount; |
|
171 aBreakPos = iText->Ptr() + iSaBreakpoint - aText.Ptr(); |
|
172 return aMinBreakPos <= aBreakPos && aBreakPos <= aMaxBreakPos; |
|
173 } |
|
174 |
|
175 virtual TBool IsHangingCharacter(TUint aChar) const |
|
176 { |
|
177 ++iHangingCharRequestCount; |
|
178 test(aChar == (*iText)[iMaxBreakPos]); |
|
179 if (!iHangingChar) |
|
180 return EFalse; |
|
181 if (iDirection < 0) |
|
182 ++iCurrentPos; |
|
183 return ETrue; |
|
184 } |
|
185 |
|
186 // non-virtual |
|
187 TBool GetLineBreakL(const TDesC& aText, TInt aDocPos, |
|
188 TInt aMinBreakPos, TInt aMaxBreakPos, TBool aForwards, |
|
189 TInt& aBreakPos, TInt& aHangingChars, TInt& aBreakPosAfterSpaces) const |
|
190 { |
|
191 iText = &aText; |
|
192 iMaxBreakPos = aMaxBreakPos; |
|
193 iMinBreakPos = aMinBreakPos; |
|
194 iHangingCharRequestCount = 0; |
|
195 iSaRequestCount = 0; |
|
196 iDirection = aForwards? 1 : -1; |
|
197 iCurrentPos = aForwards? aMinBreakPos : aMaxBreakPos - 1; |
|
198 TBool r = MTmSource::GetLineBreakL(aText, aDocPos, |
|
199 aMinBreakPos, aMaxBreakPos, aForwards, |
|
200 aBreakPos, aHangingChars, aBreakPosAfterSpaces); |
|
201 if (r) |
|
202 { |
|
203 test(aMinBreakPos <= aBreakPos); |
|
204 test(0 < aBreakPos); |
|
205 test(aBreakPos <= aHangingChars); |
|
206 test(aHangingChars <= aBreakPosAfterSpaces); |
|
207 test(aBreakPos <= aMaxBreakPos); |
|
208 test(aHangingChars == aBreakPos || iHangingChar); |
|
209 // If the direction was backwards, the algorithm should have |
|
210 // checked if a hanging character was allowed. |
|
211 // This condition could be relaxed to allow it not to be checked |
|
212 // if there is no break allowed between the possible hanging |
|
213 // character and the previous character. |
|
214 test(!aForwards || aText.Length() == aMaxBreakPos |
|
215 || 0 < iHangingCharRequestCount); |
|
216 // If the maximum break point was chosen or exceeded, the algorithm |
|
217 // should have checked to find out whether a hanging character is |
|
218 // allowed. |
|
219 test(aHangingChars < aMaxBreakPos |
|
220 || 0 < iHangingCharRequestCount); |
|
221 // Check that only spaces exist between aHangingChars and |
|
222 // aMaxBreakPos |
|
223 for (TInt i = aHangingChars; i != aBreakPosAfterSpaces; ++i) |
|
224 { |
|
225 TUint n; |
|
226 test(ESpLineBreakClass == LineBreakClass(aText[i], n, n)); |
|
227 } |
|
228 // Check that all the spaces were counted |
|
229 test(aBreakPosAfterSpaces == aText.Length() |
|
230 || aText[aBreakPosAfterSpaces] != ' '); |
|
231 } |
|
232 // Find out how many runs of two or more Sa there are, and check that |
|
233 // this matches the number of times that it was requested. |
|
234 TInt minChecked = aMinBreakPos - 1; |
|
235 TInt maxChecked = aMaxBreakPos + 2; |
|
236 if (r) |
|
237 { |
|
238 if (aForwards) |
|
239 maxChecked = aBreakPos + 1; |
|
240 else |
|
241 minChecked = aBreakPos - 1; |
|
242 } |
|
243 if (minChecked < 0) |
|
244 minChecked = 0; |
|
245 if (aText.Length() < maxChecked) |
|
246 maxChecked = aText.Length(); |
|
247 TInt runs = 0; |
|
248 TInt sasSoFar = 0; |
|
249 test (maxChecked - minChecked < 2 |
|
250 || aText[minChecked] != '@' |
|
251 || aText[minChecked + 1] != '@' |
|
252 || !aForwards |
|
253 || aHangingChars == iSaBreakpoint); |
|
254 for (; minChecked != maxChecked; ++minChecked) |
|
255 { |
|
256 if (aText[minChecked] == '@') |
|
257 ++sasSoFar; |
|
258 else |
|
259 { |
|
260 if (1 < sasSoFar) |
|
261 ++runs; |
|
262 sasSoFar = 0; |
|
263 } |
|
264 } |
|
265 if (1 < sasSoFar) |
|
266 ++runs; |
|
267 test(sasSoFar < 2 || aForwards || aHangingChars == iSaBreakpoint); |
|
268 test(runs == iSaRequestCount); |
|
269 return r; |
|
270 } |
|
271 |
|
272 TInt FindNextCustomClass() const |
|
273 { |
|
274 TInt end = iDirection < 0? -1 : iText->Length(); |
|
275 for (; iCurrentPos != end; iCurrentPos += iDirection) |
|
276 { |
|
277 TInt c = (*iText)[iCurrentPos]; |
|
278 if ('0' <= c && c <= '9') |
|
279 return c; |
|
280 } |
|
281 return -1; |
|
282 } |
|
283 TInt CountSpaces() const |
|
284 { |
|
285 TInt end = iDirection < 0? -1 : iText->Length(); |
|
286 TInt count = 0; |
|
287 if (iCurrentPos == end) |
|
288 return 0; |
|
289 iCurrentPos += iDirection; |
|
290 for (; iCurrentPos != end; iCurrentPos += iDirection, ++count) |
|
291 { |
|
292 TInt c = (*iText)[iCurrentPos]; |
|
293 if (' ' != c) |
|
294 return count; |
|
295 } |
|
296 return count; |
|
297 } |
|
298 |
|
299 private: |
|
300 mutable TTestGraphicsDeviceMap iGDM; |
|
301 mutable const TDesC* iText; |
|
302 mutable TInt iMaxBreakPos; |
|
303 mutable TInt iMinBreakPos; |
|
304 |
|
305 mutable TInt iDirection; |
|
306 mutable TInt iCurrentPos; |
|
307 mutable TInt iHangingCharRequestCount; |
|
308 mutable TInt iSaRequestCount; |
|
309 |
|
310 public: |
|
311 TInt iPicturePos; |
|
312 CPicture* iPicture; |
|
313 |
|
314 TBool iHangingChar; |
|
315 |
|
316 TInt iSaBreakpoint; |
|
317 }; |
|
318 |
|
319 TInt TestLineBreak(const TDesC& aText, TInt aSaBreak, TBool aHangingChar, |
|
320 TInt aMin, TInt aMax, TBool aForwards) |
|
321 { |
|
322 if (aMax == 0) |
|
323 aMax = aText.Length(); |
|
324 TTestSource t; |
|
325 t.iHangingChar = aHangingChar; |
|
326 t.iSaBreakpoint = aSaBreak; |
|
327 TInt b0, b1, b2; |
|
328 b0 = KMaxTInt; |
|
329 b1 = KMaxTInt; |
|
330 b2 = KMaxTInt; |
|
331 return t.GetLineBreakL(aText, 0, aMin, aMax, aForwards, b0, b1, b2)? |
|
332 b1 : -1; |
|
333 } |
|
334 |
|
335 void RunTests() |
|
336 { |
|
337 test.Title(); |
|
338 test.Start(_L(" @SYMTestCaseID:SYSLIB-FORM-LEGACY-TTMSOURCE-0001 Line-Break Tests: ")); |
|
339 |
|
340 test(-1 == TestLineBreak(_L(""), 0, 0, 0, 0, 0)); |
|
341 test(-1 == TestLineBreak(_L("5"), 0, 0, 0, 0, 0)); |
|
342 test(-1 == TestLineBreak(_L("5"), 0, 0, 0, 0, 1)); |
|
343 test(-1 == TestLineBreak(_L("@"), 1, 0, 0, 0, 0)); |
|
344 test(1 == TestLineBreak(_L("a b"), 0, 0, 0, 0, 0)); |
|
345 test(-1 == TestLineBreak(_L("0 0 0 9 9"), 0, 0, 0, 0, 0)); |
|
346 test(-1 == TestLineBreak(_L("0 0 0 9 9"), 0, 0, 0, 0, 1)); |
|
347 test(9 == TestLineBreak(_L("4242454445"), 0, 0, 0, 0, 0)); |
|
348 test(5 == TestLineBreak(_L("4242454445"), 0, 0, 0, 0, 1)); |
|
349 test(5 == TestLineBreak(_L("hello there"), 0, 0, 0, 0, 0)); |
|
350 test(5 == TestLineBreak(_L("hello there"), 0, 0, 0, 0, 1)); |
|
351 test(-1 == TestLineBreak(_L("hel the re"), 0, 0, 5, 7, 0)); |
|
352 test(-1 == TestLineBreak(_L("hel the re"), 0, 0, 5, 7, 1)); |
|
353 test(8 == TestLineBreak(_L("hel the re"), 0, 1, 5, 7, 0)); |
|
354 test(8 == TestLineBreak(_L("hel the re"), 0, 1, 6, 7, 1)); |
|
355 test(3 == TestLineBreak(_L("@@@@@"), 3, 0, 0, 0, 0)); |
|
356 test(3 == TestLineBreak(_L("@@@@@"), 3, 0, 0, 0, 1)); |
|
357 test(5 == TestLineBreak(_L("9999@@@@@00099@@@@gfra"), 5, 0, 5, 0, 0)); |
|
358 test(5 == TestLineBreak(_L("9999@@@@@00099@@@@gfra"), 5, 0, 5, 0, 1)); |
|
359 test(16 == TestLineBreak(_L("9999@@@@@00099@@@@gfra"), 16, 0, 0, 0, 0)); |
|
360 test(16 == TestLineBreak(_L("9999@@@@@00099@@@@gfra"), 16, 0, 0, 0, 1)); |
|
361 test(5 == TestLineBreak(_L("55@@@55"), 0, 0, 0, 0, 0)); |
|
362 test(2 == TestLineBreak(_L("55@@@55"), 0, 0, 0, 0, 1)); |
|
363 test(3 == TestLineBreak(_L("55@55"), 0, 0, 0, 0, 0)); |
|
364 test(2 == TestLineBreak(_L("55@55"), 0, 0, 0, 0, 1)); |
|
365 |
|
366 // Test for DEF046468, which was caused by the TLineBreakIterator constructor accessing past the end of a string |
|
367 test.Next(_L("Line-Break DEF046468 Test:")); |
|
368 // Create a string of 16 chars with a picture code at the 17th position |
|
369 _LIT(KLarsString, "dolor sit amet, \xFFFC"); |
|
370 // Create a TPtrC for the 16 character string ( with the picture code after the string in memory ) |
|
371 TBufC<20> KTestBuffer(KLarsString); |
|
372 TPtrC KTestString( reinterpret_cast<const TUint16*>(KTestBuffer.Ptr()), 16); |
|
373 // Test the iterator overrun. If iterator accesses past the end of the array, it'll get picture code and crash |
|
374 test(9 == TestLineBreak(KTestString,0,0,1,15,0)); |
|
375 |
|
376 test.End(); |
|
377 test.Close(); |
|
378 |
|
379 } |
|
380 |
|
381 TInt E32Main() |
|
382 { |
|
383 TrapCleanup = CTrapCleanup::New(); |
|
384 TRAPD(err, RunTests()); |
|
385 test(err == KErrNone); |
|
386 delete TrapCleanup; |
|
387 return 0; |
|
388 } |