textrendering/textformatting/test/src/TTmSource.cpp
changeset 0 1fb32624e06b
child 51 a7c938434754
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/textrendering/textformatting/test/src/TTmSource.cpp	Tue Feb 02 02:02:46 2010 +0200
@@ -0,0 +1,388 @@
+/*
+* Copyright (c) 2002-2009 Nokia Corporation and/or its subsidiary(-ies).
+* All rights reserved.
+* This component and the accompanying materials are made available
+* under the terms of "Eclipse Public License v1.0"
+* which accompanies this distribution, and is available
+* at the URL "http://www.eclipse.org/legal/epl-v10.html".
+*
+* Initial Contributors:
+* Nokia Corporation - initial contribution.
+*
+* Contributors:
+*
+* Description: 
+* Test code for MTmSource functionality
+*
+*/
+
+
+#include "TAGMA.H"
+#include <e32test.h>
+
+#ifdef SYMBIAN_ENABLE_SPLIT_HEADERS
+#include "TAGMA_INTERNAL.H"
+#endif
+
+#define UNUSED_VAR(a) a = a
+
+CTrapCleanup* TrapCleanup;
+RTest test(_L("TTmSource - MTmSource tests"));
+
+class TTestGraphicsDeviceMap : public MGraphicsDeviceMap
+	{
+public:
+	TInt HorizontalTwipsToPixels(TInt a) const { return a; }
+	TInt VerticalTwipsToPixels(TInt a) const { return a; }
+	TInt HorizontalPixelsToTwips(TInt a) const { return a; }
+	TInt VerticalPixelsToTwips(TInt a) const { return a; }
+	TInt GetNearestFontInTwips(CFont*&,const TFontSpec&)
+		{
+		return KErrGeneral;
+		}
+	void ReleaseFont(CFont*) {}
+	};
+
+class CTestPicture : public CPicture
+	{
+public:
+	CTestPicture() {}
+	virtual void Draw(CGraphicsContext&, const TPoint&, const TRect&, MGraphicsDeviceMap*) const {}
+	virtual void ExternalizeL(RWriteStream&) const {}
+	virtual void GetOriginalSizeInTwips(TSize&) const {}
+	virtual TBool LineBreakPossible(TUint aClass,TBool aBeforePicture,TBool aHaveSpaces) const
+		{
+		++iRequestCount;
+		if (aBeforePicture)
+			{
+			test(aClass == iClassBefore);
+			test(aHaveSpaces == iSpacesBefore);
+			return iResultBefore;
+			}
+		test(aClass == iClassAfter);
+		test(aHaveSpaces == iSpacesAfter);
+		return iResultAfter;
+		}
+	// expected parameters for LineBreakPossible
+	TUint iClassBefore;
+	TUint iClassAfter;
+	TBool iSpacesBefore;
+	TBool iSpacesAfter;
+	// Results for breaking before/breaking after
+	TBool iResultBefore;
+	TBool iResultAfter;
+	mutable TInt iRequestCount;
+	};
+
+class TTestSource : public MTmSource
+	{
+public:
+	TTestSource() : iPicturePos(-1) {}
+	virtual ~TTestSource() {}
+	MGraphicsDeviceMap& FormatDevice() const { return iGDM; }
+	MGraphicsDeviceMap& InterpretDevice() const { return iGDM; }
+	TInt DocumentLength() const
+		{
+		return iText->Length();
+		}
+	void GetText(TInt aPos,TPtrC& aText, TTmCharFormat& aFormat) const
+		{
+		TTmCharFormat f;
+		aFormat = f;
+		aText.Set(iText->Mid(aPos));
+		}
+	void GetParagraphFormatL(TInt, RTmParFormat& aFormat) const
+		{
+		RTmParFormat p;
+		aFormat.CopyL(p);
+		}
+	CPicture* PictureL(TInt aPos) const
+		{
+		return aPos == iPicturePos? iPicture : 0;
+		}
+	TInt ParagraphStart(TInt) const { return 0; }
+
+	virtual TUint LineBreakClass(TUint aCode, TUint& aRangeStart,
+		TUint& aRangeEnd) const
+		{
+		if ('@' == aCode)
+			{
+			aRangeStart = aRangeEnd = aCode;
+			return ESaLineBreakClass;
+			}
+		if ('0' <= aCode && aCode <= '9')
+			{
+			aRangeStart = aRangeEnd = aCode;
+			return ELineBreakClasses + aCode - '0';
+			}
+		return MTmSource::LineBreakClass(aCode, aRangeStart, aRangeEnd);
+		}
+
+	virtual TBool LineBreakPossible(TUint aPrevClass, TUint aNextClass,
+		TBool aHaveSpaces) const
+		{
+		TInt first = static_cast<TInt>(aPrevClass);
+		TInt second = static_cast<TInt>(aNextClass);
+		TInt customCount = 0;
+		if (iDirection < 0)
+			{
+			first = aNextClass;
+			second = aPrevClass;
+			}
+		if (ELineBreakClasses <= first && first < ELineBreakClasses + 10)
+			{
+			++customCount;
+			test(first - ELineBreakClasses + '0' == FindNextCustomClass());
+			TInt countSpaces = CountSpaces();
+			test(!aHaveSpaces == !countSpaces);
+			}
+		if (ELineBreakClasses <= second && second < ELineBreakClasses + 10)
+			{
+			++customCount;
+			TInt c = FindNextCustomClass();
+			test(second - ELineBreakClasses + '0' == c);
+			}
+		if (0 == customCount)
+			return MTmSource::LineBreakPossible(aPrevClass, aNextClass, aHaveSpaces);
+		// Between custom and non-custom classes, allow a break only with spaces
+		// or between @ and 5
+		if (1 == customCount)
+			return aHaveSpaces
+				|| (first == ESaLineBreakClass && second == ELineBreakClasses + 5)
+				|| (second == ESaLineBreakClass && first == ELineBreakClasses + 5);
+		// Allow a break with spaces except after '0' or before '9'
+		if (aHaveSpaces)
+			return aPrevClass != ELineBreakClasses && aNextClass != ELineBreakClasses + 9;
+		// Allow a break only between a class and the class one more than it.
+		return aPrevClass + 1 == aNextClass;
+		}
+
+	virtual TBool GetLineBreakInContext(
+		const TDesC& aText, TInt aMinBreakPos, TInt aMaxBreakPos,
+		TBool aForwards,TInt& aBreakPos) const
+		{
+		test (iDirection == (aForwards? 1 : -1));
+		// The allowable break-points should not include the first
+		// and last characters of the run.
+		test (aMinBreakPos != 0);
+		for (TInt i = aMinBreakPos - 1; i <= aMaxBreakPos; ++i)
+			test('@' == aText[i]);
+		++iSaRequestCount;
+		aBreakPos = iText->Ptr() + iSaBreakpoint - aText.Ptr();
+		return aMinBreakPos <= aBreakPos && aBreakPos <= aMaxBreakPos;
+		}
+
+	virtual TBool IsHangingCharacter(TUint aChar) const
+		{
+		++iHangingCharRequestCount;
+		test(aChar == (*iText)[iMaxBreakPos]);
+		if (!iHangingChar)
+			return EFalse;
+		if (iDirection < 0)
+			++iCurrentPos;
+		return ETrue;
+		}
+
+	// non-virtual
+	TBool GetLineBreakL(const TDesC& aText, TInt aDocPos,
+		TInt aMinBreakPos, TInt aMaxBreakPos, TBool aForwards,
+		TInt& aBreakPos, TInt& aHangingChars, TInt& aBreakPosAfterSpaces) const
+		{
+		iText = &aText;
+		iMaxBreakPos = aMaxBreakPos;
+		iMinBreakPos = aMinBreakPos;
+		iHangingCharRequestCount = 0;
+		iSaRequestCount = 0;
+		iDirection = aForwards? 1 : -1;
+		iCurrentPos = aForwards? aMinBreakPos : aMaxBreakPos - 1;
+		TBool r = MTmSource::GetLineBreakL(aText, aDocPos,
+			aMinBreakPos, aMaxBreakPos, aForwards,
+			aBreakPos, aHangingChars, aBreakPosAfterSpaces);
+		if (r)
+			{
+			test(aMinBreakPos <= aBreakPos);
+			test(0 < aBreakPos);
+			test(aBreakPos <= aHangingChars);
+			test(aHangingChars <= aBreakPosAfterSpaces);
+			test(aBreakPos <= aMaxBreakPos);
+			test(aHangingChars == aBreakPos || iHangingChar);
+			// If the direction was backwards, the algorithm should have
+			// checked if a hanging character was allowed.
+			// This condition could be relaxed to allow it not to be checked
+			// if there is no break allowed between the possible hanging
+			// character and the previous character.
+			test(!aForwards || aText.Length() == aMaxBreakPos
+				|| 0 < iHangingCharRequestCount);
+			// If the maximum break point was chosen or exceeded, the algorithm
+			// should have checked to find out whether a hanging character is
+			// allowed.
+			test(aHangingChars < aMaxBreakPos
+				|| 0 < iHangingCharRequestCount);
+			// Check that only spaces exist between aHangingChars and
+			// aMaxBreakPos
+			for (TInt i = aHangingChars; i != aBreakPosAfterSpaces; ++i)
+				{
+				TUint n;
+				test(ESpLineBreakClass == LineBreakClass(aText[i], n, n));
+				}
+			// Check that all the spaces were counted
+			test(aBreakPosAfterSpaces == aText.Length()
+				|| aText[aBreakPosAfterSpaces] != ' ');
+			}
+		// Find out how many runs of two or more Sa there are, and check that
+		// this matches the number of times that it was requested.
+		TInt minChecked = aMinBreakPos - 1;
+		TInt maxChecked = aMaxBreakPos + 2;
+		if (r)
+			{
+			if (aForwards)
+				maxChecked = aBreakPos + 1;
+			else
+				minChecked = aBreakPos - 1;
+			}
+		if (minChecked < 0)
+			minChecked = 0;
+		if (aText.Length() < maxChecked)
+			maxChecked = aText.Length();
+		TInt runs = 0;
+		TInt sasSoFar = 0;
+		test (maxChecked - minChecked < 2
+			|| aText[minChecked] != '@'
+			|| aText[minChecked + 1] != '@'
+			|| !aForwards
+			|| aHangingChars == iSaBreakpoint);
+		for (; minChecked != maxChecked; ++minChecked)
+			{
+			if (aText[minChecked] == '@')
+				++sasSoFar;
+			else
+				{
+				if (1 < sasSoFar)
+					++runs;
+				sasSoFar = 0;
+				}
+			}
+		if (1 < sasSoFar)
+			++runs;
+		test(sasSoFar < 2 || aForwards || aHangingChars == iSaBreakpoint);
+		test(runs == iSaRequestCount);
+		return r;
+		}
+
+	TInt FindNextCustomClass() const
+		{
+		TInt end = iDirection < 0? -1 : iText->Length();
+		for (; iCurrentPos != end; iCurrentPos += iDirection)
+			{
+			TInt c = (*iText)[iCurrentPos];
+			if ('0' <= c && c <= '9')
+				return c;
+			}
+		return -1;
+		}
+	TInt CountSpaces() const
+		{
+		TInt end = iDirection < 0? -1 : iText->Length();
+		TInt count = 0;
+		if (iCurrentPos == end)
+			return 0;
+		iCurrentPos += iDirection;
+		for (; iCurrentPos != end; iCurrentPos += iDirection, ++count)
+			{
+			TInt c = (*iText)[iCurrentPos];
+			if (' ' != c)
+				return count;
+			}
+		return count;
+		}
+
+private:
+	mutable TTestGraphicsDeviceMap iGDM;
+	mutable const TDesC* iText;
+	mutable TInt iMaxBreakPos;
+	mutable TInt iMinBreakPos;
+
+	mutable TInt iDirection;
+	mutable TInt iCurrentPos;
+	mutable TInt iHangingCharRequestCount;
+	mutable TInt iSaRequestCount;
+
+public:
+	TInt iPicturePos;
+	CPicture* iPicture;
+
+	TBool iHangingChar;
+
+	TInt iSaBreakpoint;
+	};
+
+TInt TestLineBreak(const TDesC& aText, TInt aSaBreak, TBool aHangingChar,
+	TInt aMin, TInt aMax, TBool aForwards)
+	{
+	if (aMax == 0)
+		aMax = aText.Length();
+	TTestSource t;
+	t.iHangingChar = aHangingChar;
+	t.iSaBreakpoint = aSaBreak;
+	TInt b0, b1, b2;
+	b0 = KMaxTInt;
+	b1 = KMaxTInt;
+	b2 = KMaxTInt;
+	return t.GetLineBreakL(aText, 0, aMin, aMax, aForwards, b0, b1, b2)?
+		b1 : -1;
+	}
+
+void RunTests()
+	{
+	test.Title();
+	test.Start(_L(" @SYMTestCaseID:SYSLIB-FORM-LEGACY-TTMSOURCE-0001 Line-Break Tests: "));
+
+	test(-1 == TestLineBreak(_L(""), 0, 0, 0, 0, 0));
+	test(-1 == TestLineBreak(_L("5"), 0, 0, 0, 0, 0));
+	test(-1 == TestLineBreak(_L("5"), 0, 0, 0, 0, 1));
+	test(-1 == TestLineBreak(_L("@"), 1, 0, 0, 0, 0));
+	test(1 == TestLineBreak(_L("a   b"), 0, 0, 0, 0, 0));
+	test(-1 == TestLineBreak(_L("0 0 0 9    9"), 0, 0, 0, 0, 0));
+	test(-1 == TestLineBreak(_L("0 0 0 9    9"), 0, 0, 0, 0, 1));
+	test(9 == TestLineBreak(_L("4242454445"), 0, 0, 0, 0, 0));
+	test(5 == TestLineBreak(_L("4242454445"), 0, 0, 0, 0, 1));
+	test(5 == TestLineBreak(_L("hello there"), 0, 0, 0, 0, 0));
+	test(5 == TestLineBreak(_L("hello there"), 0, 0, 0, 0, 1));
+	test(-1 == TestLineBreak(_L("hel  the re"), 0, 0, 5, 7, 0));
+	test(-1 == TestLineBreak(_L("hel  the re"), 0, 0, 5, 7, 1));
+	test(8 == TestLineBreak(_L("hel  the re"), 0, 1, 5, 7, 0));
+	test(8 == TestLineBreak(_L("hel  the re"), 0, 1, 6, 7, 1));
+	test(3 == TestLineBreak(_L("@@@@@"), 3, 0, 0, 0, 0));
+	test(3 == TestLineBreak(_L("@@@@@"), 3, 0, 0, 0, 1));
+	test(5 == TestLineBreak(_L("9999@@@@@00099@@@@gfra"), 5, 0, 5, 0, 0));
+	test(5 == TestLineBreak(_L("9999@@@@@00099@@@@gfra"), 5, 0, 5, 0, 1));
+	test(16 == TestLineBreak(_L("9999@@@@@00099@@@@gfra"), 16, 0, 0, 0, 0));
+	test(16 == TestLineBreak(_L("9999@@@@@00099@@@@gfra"), 16, 0, 0, 0, 1));
+	test(5 == TestLineBreak(_L("55@@@55"), 0, 0, 0, 0, 0));
+	test(2 == TestLineBreak(_L("55@@@55"), 0, 0, 0, 0, 1));
+	test(3 == TestLineBreak(_L("55@55"), 0, 0, 0, 0, 0));
+	test(2 == TestLineBreak(_L("55@55"), 0, 0, 0, 0, 1));
+
+	// Test for DEF046468, which was caused by the TLineBreakIterator constructor accessing past the end of a string
+	test.Next(_L("Line-Break DEF046468 Test:"));
+	// Create a string of 16 chars with a picture code at the 17th position
+	_LIT(KLarsString, "dolor sit amet, \xFFFC");
+	// Create a TPtrC for the 16 character string ( with the picture code after the string in memory )
+	TBufC<20> KTestBuffer(KLarsString);
+	TPtrC KTestString( reinterpret_cast<const TUint16*>(KTestBuffer.Ptr()), 16);
+	// Test the iterator overrun. If iterator accesses past the end of the array, it'll get picture code and crash
+	test(9 == TestLineBreak(KTestString,0,0,1,15,0));
+
+	test.End();
+	test.Close();
+
+	}
+
+TInt E32Main()
+	{
+	TrapCleanup = CTrapCleanup::New();
+	TRAPD(err, RunTests());
+    test(err == KErrNone);
+	delete TrapCleanup;
+	return 0;
+	}