textrendering/texthandling/ttext/T_parse.cpp
changeset 0 1fb32624e06b
child 51 a7c938434754
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/textrendering/texthandling/ttext/T_parse.cpp	Tue Feb 02 02:02:46 2010 +0200
@@ -0,0 +1,835 @@
+/*
+* Copyright (c) 1999-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: 
+* TPARSE.CPP
+*
+*/
+
+
+#include <e32test.h>
+#include <txtrich.h>
+#include <e32math.h>
+#include "T_parse.h"
+
+LOCAL_D CTrapCleanup* TrapCleanup;
+LOCAL_D RTest test(_L("Testing EText parser system"));
+LOCAL_D const TInt KTestCleanupStack=0x200;
+
+
+CTestParser* CTestParser::NewL()
+	{
+	CTestParser* self = new (ELeave) CTestParser;
+	CleanupStack::PushL(self);
+	self->iDoItText = HBufC::NewL(128);
+	*(self->iDoItText) = _L("Or is it just undead?");
+	CleanupStack::Pop();
+	return self;
+	}
+
+
+CTestParser::CTestParser()
+	{
+	}
+
+
+CTestParser::~CTestParser()
+	{
+	delete iDoItText;
+	}
+
+
+TBool CTestParser::ParseThisText(const CRichText& aTextObj, TBool aAllowBack,
+									TInt aStartScan, TInt aScanLength,
+									TInt& aStartTag, TInt& aTagLength)
+	{
+	// Very simple - looking for the phrase "the world"
+	_LIT(targetText, "TARGET");
+	TInt length = 6;
+	// Consider that the start of the scan may be part way through
+	// the item that we are looking for, so see if we need to move back
+	TInt start = aStartScan;
+	if (aAllowBack)
+		{
+		if (start >= length - 1)
+			start -= length - 1;
+		else
+			start = 0;
+		}
+	// And it might go beyond the end of the scan
+	TInt end = aStartScan + aScanLength;
+	if (end + length - 1 <= aTextObj.DocumentLength())
+		end += length - 1;
+	else
+		end = aTextObj.DocumentLength();
+	while (end - start >= length)
+		{
+		TPtrC buffer = aTextObj.Read(start, end - start);
+		TInt segmentLength= buffer.Length();
+
+		if (segmentLength >= length)	// There's enough text to bother searching
+			{
+			TInt result = buffer.FindF(targetText);
+			if (result != KErrNotFound)	// We found it
+				{
+				aStartTag = start + result;
+				aTagLength = length;
+				return ETrue;
+				}
+			}
+		if (end == start + segmentLength)	// There's no more text at all
+			break;
+		// The buffer is segmented and there's another segment 
+
+		// It could be over the segment boundary
+		TInt start2 = start + segmentLength;
+		TInt end2 = start2;
+		if (segmentLength >= length - 1)
+			start2 -= length - 1;
+		else start2 = start;
+		if (end >= end2 + length - 1)
+			end2 += length -1;
+		else
+			end2 = end;
+		if (end2 - start2 >= length)
+			{
+			// Create a buffer with the end of one and the start of the other
+			TBuf<10> bridgeBuffer;
+			aTextObj.Extract(bridgeBuffer, start2, end2 - start2);
+			TInt result = bridgeBuffer.FindF(targetText);
+			if (result != KErrNotFound)	// We found it
+				{
+				aStartTag = start2 + result;
+				aTagLength = length;
+				return ETrue;
+				}
+			}
+
+		// Move start up for next segment
+		start += segmentLength;
+		}
+		return EFalse;	// Not enough text left in buffer
+	}
+
+
+const TDesC& CTestParser::CreateDoItText(const CRichText& /* aTextObj */,
+							TInt /* aStartText */, TInt /* aLength */)
+	{
+	return *iDoItText;
+	}
+
+
+void CTestParser::ActivateThisTextL(const CRichText& /* aTextObj */,
+					  TInt /* aStartText */, TInt /* aLength */)
+	{
+	// Do something?
+
+	}
+
+
+void CTestParser::GetRecogniseFormat(TCharFormat& aFormat)
+	{
+	aFormat.iFontPresentation.iTextColor = KRgbRed;
+	aFormat.iFontPresentation.iUnderline = EUnderlineOn;
+	}
+
+
+void CTestParser::GetRolloverFormat(TCharFormat& aFormat)
+	{
+	aFormat.iFontPresentation.iTextColor = KRgbRed;
+	aFormat.iFontPresentation.iUnderline = EUnderlineOn;
+	aFormat.iFontPresentation.iHighlightColor = KRgbDarkRed;
+	aFormat.iFontPresentation.iHighlightStyle = TFontPresentation::EFontHighlightRounded;
+	}
+
+
+void CTestParser::Release()
+	{
+	delete this;
+	}
+
+//--------------------------------------------
+
+CTestParser2* CTestParser2::NewL()
+	{
+	CTestParser2* self = new (ELeave) CTestParser2;
+	CleanupStack::PushL(self);
+	self->iDoItText = HBufC::NewL(128);
+	*(self->iDoItText) = _L("Or is it just undead?");
+	CleanupStack::Pop();
+	return self;
+	}
+
+
+CTestParser2::CTestParser2()
+	{
+	}
+
+
+CTestParser2::~CTestParser2()
+	{
+	delete iDoItText;
+	}
+
+
+TBool CTestParser2::ParseThisText(const CRichText& aTextObj, TBool aAllowBack,
+									TInt aStartScan, TInt aScanLength,
+									TInt& aStartTag, TInt& aTagLength)
+	{
+	// Very simple - looking for the phrase "ARG"
+	_LIT(targetText, "ARG");
+	TInt length = 3;
+	// Consider that the start of the scan may be part way through
+	// the item that we are looking for, so see if we need to move back
+	TInt start = aStartScan;
+	if (aAllowBack)
+		{
+		if (start >= length - 1)
+			start -= length - 1;
+		else
+			start = 0;
+		}
+	// And it might go beyond the end of the scan
+	TInt end = aStartScan + aScanLength;
+	if (end + length - 1 <= aTextObj.DocumentLength())
+		end += length - 1;
+	else
+		end = aTextObj.DocumentLength();
+	while (end - start >= length)
+		{
+		TPtrC buffer = aTextObj.Read(start, end - start);
+		TInt segmentLength= buffer.Length();
+
+		if (segmentLength >= length)	// There's enough text to bother searching
+			{
+			TInt result = buffer.FindF(targetText);
+			if (result != KErrNotFound)	// We found it
+				{
+				aStartTag = start + result;
+				aTagLength = length;
+				return ETrue;
+				}
+			}
+		if (end == start + segmentLength)	// There's no more text at all
+			break;
+		// The buffer is segmented and there's another segment 
+
+		// It could be over the segment boundary
+		TInt start2 = start + segmentLength;
+		TInt end2 = start2;
+		if (segmentLength >= length - 1)
+			start2 -= length - 1;
+		else start2 = start;
+		if (end >= end2 + length - 1)
+			end2 += length -1;
+		else
+			end2 = end;
+		if (end2 - start2 >= length)
+			{
+			// Create a buffer with the end of one and the start of the other
+			TBuf<10> bridgeBuffer;
+			aTextObj.Extract(bridgeBuffer, start2, end2 - start2);
+			TInt result = bridgeBuffer.FindF(targetText);
+			if (result != KErrNotFound)	// We found it
+				{
+				aStartTag = start2 + result;
+				aTagLength = length;
+				return ETrue;
+				}
+			}
+
+		// Move start up for next segment
+		start += segmentLength;
+		}
+		return EFalse;	// Not enough text left in buffer
+	}
+
+
+const TDesC& CTestParser2::CreateDoItText(const CRichText& /* aTextObj */,
+							TInt /* aStartText */, TInt /* aLength */)
+	{
+	return *iDoItText;
+	}
+
+
+void CTestParser2::ActivateThisTextL(const CRichText& /* aTextObj */,
+					  TInt /* aStartText */, TInt /* aLength */)
+	{
+	// Do something?
+
+	}
+
+
+void CTestParser2::GetRecogniseFormat(TCharFormat& aFormat)
+	{
+	aFormat.iFontPresentation.iTextColor = KRgbRed;
+	aFormat.iFontPresentation.iUnderline = EUnderlineOn;
+	}
+
+
+void CTestParser2::GetRolloverFormat(TCharFormat& aFormat)
+	{
+	aFormat.iFontPresentation.iTextColor = KRgbRed;
+	aFormat.iFontPresentation.iUnderline = EUnderlineOn;
+	aFormat.iFontPresentation.iHighlightColor = KRgbDarkRed;
+	aFormat.iFontPresentation.iHighlightStyle = TFontPresentation::EFontHighlightRounded;
+	}
+
+
+void CTestParser2::Release()
+	{
+	delete this;
+	}
+
+
+//--------------------------------------------
+
+
+CEditObserver::CEditObserver()
+	{
+	iStart = 0;
+	iExtent = 0;
+	}
+
+
+CEditObserver::~CEditObserver()
+	{
+	}
+
+
+void CEditObserver::EditObserver(TInt aStart, TInt aExtent)
+	{
+	iStart = aStart;
+	iExtent = aExtent;
+	}
+
+
+void Test1()
+	{
+	test.Next(_L("Install 5, deinstall in reverse order"));
+	CTestParser* parser1 = CTestParser::NewL();
+	CTestParser* parser2 = CTestParser::NewL();
+	CTestParser* parser3 = CTestParser::NewL();
+	CTestParser* parser4 = CTestParser::NewL();
+	CTestParser* parser5 = CTestParser::NewL();
+	CRichText::ActivateParserL(parser1);	// List 1
+	CRichText::ActivateParserL(parser2);	// List 1, 2
+	CRichText::ActivateParserL(parser3);	// List 1, 2, 3
+	CRichText::ActivateParserL(parser4);	// List 1, 2, 3, 4
+	CRichText::ActivateParserL(parser5);	// List 1, 2, 3, 4, 5
+	CRichText::DeactivateParser(parser5);	// List 1, 2, 3, 4
+	CRichText::DeactivateParser(parser4);	// List 1, 2, 3
+	CRichText::DeactivateParser(parser3);	// List 1, 2
+	CRichText::DeactivateParser(parser2);	// List 1
+	CRichText::DeactivateParser(parser1);	// List empty
+	delete parser5;
+	delete parser4;
+	delete parser3;
+	delete parser2;
+	delete parser1;
+	CRichText::DeactivateParserDefaults();
+	}
+
+
+void Test2()
+	{
+	test.Next(_L("Install, deinstall in interleaved order"));
+	CTestParser* parser1 = CTestParser::NewL();
+	CRichText::ActivateParserL(parser1);	// List 1
+	CTestParser* parser2 = CTestParser::NewL();
+	CRichText::ActivateParserL(parser2);	// List 1, 2
+	// Remove first in list
+	CRichText::DeactivateParser(parser1);	// List 2
+	delete parser1;
+	CTestParser* parser3 = CTestParser::NewL();
+	CRichText::ActivateParserL(parser3);	// List 2, 3
+	// Remove last in list
+	CRichText::DeactivateParser(parser3);	// List 2
+	delete parser3;
+	CTestParser* parser4 = CTestParser::NewL();
+	CRichText::ActivateParserL(parser4);	// List 2, 4
+	CTestParser* parser5 = CTestParser::NewL();
+	CRichText::ActivateParserL(parser5);	// List 2, 4, 5
+	// Remove middle in list
+	CRichText::DeactivateParser(parser4);	// List 2, 5
+	delete parser4;
+	// Empty list
+	CRichText::DeactivateParser(parser5);	// List 2
+	delete parser5;
+	CRichText::DeactivateParser(parser2);	// List empty
+	delete parser2;
+	CRichText::DeactivateParserDefaults();
+	}
+
+
+void Test3()
+	{
+	test.Next(_L("Testing memory with OOM"));
+
+	TInt i;
+	TInt ret;
+	TInt count = 0;
+	TInt success = 0;
+
+	for (i = 0; i < 20; i++)
+		{
+		CTestParser* parser1 = NULL;
+		CTestParser* parser2 = NULL;
+		CTestParser* parser3 = NULL;
+		CTestParser* parser4 = NULL;
+		CTestParser* parser5 = NULL;
+		// Switch on memory problems, varying fail rate from 20 to 1
+		__UHEAP_SETFAIL(RHeap::EDeterministic, 20 - i);
+		__UHEAP_MARK;
+		count++;
+		TRAP(ret, parser1 = CTestParser::NewL());
+		if (ret != KErrNone)
+			{
+			parser1 = NULL;
+			}
+		else
+			{
+			TRAP(ret, CRichText::ActivateParserL(parser1));
+			if (ret != KErrNone)
+				{
+				delete parser1;
+				parser1 = NULL;
+				}
+			}
+		TRAP(ret, parser2 = CTestParser::NewL());
+		if (ret != KErrNone)
+			{
+			parser2 = NULL;
+			}
+		else
+			{
+			TRAP(ret, CRichText::ActivateParserL(parser2));
+			if (ret != KErrNone)
+				{
+				delete parser2;
+				parser2 = NULL;
+				}
+			}
+		TRAP(ret, parser3 = CTestParser::NewL());
+		if (ret != KErrNone)
+			{
+			parser3 = NULL;
+			}
+		else
+			{
+			TRAP(ret, CRichText::ActivateParserL(parser3));
+			if (ret != KErrNone)
+				{
+				delete parser3;
+				parser3 = NULL;
+				}
+			}
+		TRAP(ret, parser4 = CTestParser::NewL());
+		if (ret != KErrNone)
+			{
+			parser4 = NULL;
+			}
+		else
+			{
+			TRAP(ret, CRichText::ActivateParserL(parser4));
+			if (ret != KErrNone)
+				{
+				delete parser4;
+				parser4 = NULL;
+				}
+			}
+		TRAP(ret, parser5 = CTestParser::NewL());
+		if (ret != KErrNone)
+			{
+			parser5 = NULL;
+			}
+		else
+			{
+			TRAP(ret, CRichText::ActivateParserL(parser5));
+			if (ret != KErrNone)
+				{
+				delete parser5;
+				parser5 = NULL;
+				}
+			}
+		if (parser1)
+			{
+			success++;
+			CRichText::DeactivateParser(parser1);
+			delete parser1;
+			}
+		if (parser2)
+			{
+			success++;
+			CRichText::DeactivateParser(parser2);
+			delete parser2;
+			}
+		if (parser3)
+			{
+			success++;
+			CRichText::DeactivateParser(parser3);
+			delete parser3;
+			}
+		if (parser4)
+			{
+			success++;
+			CRichText::DeactivateParser(parser4);
+			delete parser4;
+			}
+		if (parser5)
+			{
+			success++;
+			CRichText::DeactivateParser(parser5);
+			delete parser5;
+			}
+		CRichText::DeactivateParserDefaults();
+		// Switch off memory problems
+		__UHEAP_MARKEND;
+		__UHEAP_RESET;
+		}
+
+	test.Printf(_L("%d attempted activations, %d successful\n"), 5 * count, success);
+	}
+
+
+void Test4()
+	{
+	// Create a block of 1000 chars
+	// Randomly insert a target string and check:
+	// - Can't find target that is not complete
+	// - Does find complete target in right place
+	// - Once target is removed, can't find it
+	// repeat x 100
+	test.Next(_L("Testing EText behaviour with active parsers and single target"));
+	// Create and activate a parser
+	CTestParser* parser1 = CTestParser::NewL();
+	CRichText::ActivateParserL(parser1);
+	// Create a CRichText
+	CParaFormatLayer* GlobalParaFormatLayer = CParaFormatLayer::NewL();
+	CCharFormatLayer* GlobalCharFormatLayer = CCharFormatLayer::NewL();
+	CRichText* richText = CRichText::NewL(GlobalParaFormatLayer, GlobalCharFormatLayer, CEditableText::ESegmentedStorage, CEditableText::EDefaultTextGranularity);
+	richText->SetPictureFactory(NULL, NULL);  // forces index generation
+	// Switch on parsers for this CRichText
+	CEditObserver* editObserver = new (ELeave) CEditObserver;
+	richText->SetEditObserver(editObserver);
+
+	// Insert 1000 chars (repeated string)
+	TInt i;
+	for (i = 0; i < 100; i++)
+		richText->InsertL(richText->DocumentLength(), _L("abcdefghij"));
+	TInt64 seed = 314159;
+	TInt startTags;
+	TInt lengthTags;
+	for (i = 0; i < 100; i++)
+		{
+		// Get a random in range 0-999
+		TInt random = Math::Rand(seed) % 1000;
+		// "Randomly" insert target string that is not complete
+		richText->InsertL(random, _L("TARGE"));
+		test(!richText->ParseText(startTags, lengthTags, EFalse));
+		test(!richText->ParseText(startTags, lengthTags, ETrue));
+		// Complete target string and check we find single target where we expect
+		richText->InsertL(random + 5, 'T');
+		test(richText->ParseText(startTags, lengthTags, EFalse));
+		test((startTags == random) && (lengthTags == 6));
+		test(richText->ParseText(startTags, lengthTags, ETrue));
+		test((startTags == random) && (lengthTags == 6));
+		// Completely remove target string
+		richText->DeleteL(random, 6);
+		test(!richText->ParseText(startTags, lengthTags, EFalse));
+		test(!richText->ParseText(startTags, lengthTags, ETrue));
+		}
+
+	// Clean up
+
+	delete richText;
+	delete GlobalCharFormatLayer;
+	delete GlobalParaFormatLayer;
+	CRichText::DeactivateParser(parser1);
+	delete parser1;
+	CRichText::DeactivateParserDefaults();
+	delete editObserver;
+	}
+
+
+void Test5()
+	{
+	// Create a block of 1000 chars
+	// Randomly insert two target strings and check:
+	// - Can't find targets that are not complete
+	// - Does find complete targets with exact range covered
+	// - Once targets are removed, can't find it
+	// repeat x 100
+	test.Next(_L("Testing EText behaviour with active parsers and double target"));
+	// Create and activate a parser
+	CTestParser* parser1 = CTestParser::NewL();
+	CRichText::ActivateParserL(parser1);
+	// Create a CRichText
+	CParaFormatLayer* GlobalParaFormatLayer = CParaFormatLayer::NewL();
+	CCharFormatLayer* GlobalCharFormatLayer = CCharFormatLayer::NewL();
+	CRichText* richText = CRichText::NewL(GlobalParaFormatLayer, GlobalCharFormatLayer, CEditableText::ESegmentedStorage, CEditableText::EDefaultTextGranularity);
+	richText->SetPictureFactory(NULL, NULL);  // forces index generation
+	// Switch on parsers for this CRichText
+	CEditObserver* editObserver = new (ELeave) CEditObserver;
+	richText->SetEditObserver(editObserver);
+
+	// Insert 1000 chars (repeated string)
+	TInt i;
+	for (i = 0; i < 100; i++)
+		richText->InsertL(richText->DocumentLength(), _L("abcdefghij"));
+	TInt64 seed1 = 314159;
+	TInt startTags;
+	TInt lengthTags;
+	for (i = 0; i < 100; i++)
+		{
+		// Get a random in range 0-999
+		TInt random1 = Math::Rand(seed1) % 1000;
+		TInt random2 = Math::Rand(seed1) % 1000;
+		TInt rlow = (random1 < random2) ? random1 : random2;
+		TInt rhigh = (random1 > random2) ? random1 : random2;
+		if (rlow + 7 > rhigh)
+			{ // Too close, spread them out
+			if (rhigh + 7 <= richText->DocumentLength())
+				rhigh += 7;
+			else
+				rlow -= 7;
+			}
+		// "Randomly" insert target strings that are not complete
+		richText->InsertL(rlow, _L("TARGE"));
+		richText->InsertL(rhigh - 1, _L("TARGE"));
+		test(!richText->ParseText(startTags, lengthTags, EFalse));
+		test(!richText->ParseText(startTags, lengthTags, ETrue));
+		// Complete target string and check we find single target where we expect
+		richText->InsertL(rlow + 5, 'T');
+		richText->InsertL(rhigh + 5, 'T');
+		test(richText->ParseText(startTags, lengthTags, EFalse));
+		test((startTags == rlow) && (lengthTags == rhigh + 6 - rlow));
+		test(richText->ParseText(startTags, lengthTags, ETrue));
+		test((startTags == rlow) && (lengthTags == rhigh + 6 - rlow));
+		// Completely remove target string
+		richText->DeleteL(rhigh, 6);
+		richText->DeleteL(rlow, 6);
+		test(!richText->ParseText(startTags, lengthTags, EFalse));
+		test(!richText->ParseText(startTags, lengthTags, ETrue));
+		}
+
+	// Clean up
+
+	delete richText;
+	delete GlobalCharFormatLayer;
+	delete GlobalParaFormatLayer;
+	CRichText::DeactivateParser(parser1);
+	delete parser1;
+	CRichText::DeactivateParserDefaults();
+	delete editObserver;
+	}
+
+/**
+@SYMTestCaseID          SYSLIB-ETEXT-UT-3405
+@SYMTestCaseDesc	    Test for the fix for when CRichText crashes when parsers are active
+@SYMTestPriority 	    Medium
+@SYMTestActions  	    Calls PositionOfNextTag on all available document positions to validate fix.
+@SYMTestExpectedResults Test must be able to call PositionOfNextTag on all document positions
+@SYMDEF                 PDEF102494
+*/	
+
+void Test6()
+	{
+	test.Next(_L(" @SYMTestCaseID:SYSLIB-ETEXT-UT-3405 Calling PositionOfNextTag on an empty document "));
+	
+	// Create and activate a parser
+	CTestParser* parser1 = CTestParser::NewL();
+	CRichText::ActivateParserL(parser1);
+	// Create a CRichText
+	CParaFormatLayer* GlobalParaFormatLayer = CParaFormatLayer::NewL();
+	CCharFormatLayer* GlobalCharFormatLayer = CCharFormatLayer::NewL();
+	CRichText* richText = CRichText::NewL(GlobalParaFormatLayer, GlobalCharFormatLayer, CEditableText::ESegmentedStorage, CEditableText::EDefaultTextGranularity);
+	richText->SetPictureFactory(NULL, NULL);  // forces index generation
+	// Switch on parsers for this CRichText
+	CEditObserver* editObserver = new (ELeave) CEditObserver;
+	richText->SetEditObserver(editObserver);
+
+	//insert and format some text
+	richText->InsertL(richText->DocumentLength(), _L("abcdTARGET"));
+
+	TCharFormat charFormat;
+	TCharFormatMask charFormatMask;
+
+	richText->ApplyCharFormatL(charFormat, charFormatMask, 0, richText->DocumentLength());
+
+	TInt startTags;
+	TInt lengthTags;
+	richText->ParseText(startTags, lengthTags, ETrue);
+
+    // Call position of next tag on all document positions
+	for (TInt i = 0; i <= richText->DocumentLength(); i++)
+		{
+		richText->PositionOfNextTag(i, parser1);
+		}
+	
+	// Clean up
+
+	delete richText;
+	delete GlobalCharFormatLayer;
+	delete GlobalParaFormatLayer;
+	CRichText::DeactivateParser(parser1);
+	delete parser1;
+	CRichText::DeactivateParserDefaults();
+	delete editObserver;
+	}
+
+/**
+@SYMTestCaseID          SYSLIB-ETEXT-UT-3406
+@SYMTestCaseDesc	    Test for the fix for when CRichText crashes when parsers are active
+@SYMTestPriority 	    Medium
+@SYMTestActions  	    Initialize two parsers. First parser has a target string of "TARGET",
+                        and the second parser has a target string of "ARG". By parsing the
+                        two overlapping (ARG is contained in TARGET) target strings, the changes
+						for this defect have full coverage.
+@SYMTestExpectedResults Test must be able to call PositionOfNextTag on the end of document positions
+@SYMDEF                 PDEF102494
+*/
+void Test7()
+	{
+	test.Next(_L(" @SYMTestCaseID:SYSLIB-ETEXT-UT-3406 Testing EText behaviour with active parsers and single target "));
+	
+	// Create and activate a parser
+	CTestParser* parser1 = CTestParser::NewL();
+	CRichText::ActivateParserL(parser1);
+	CTestParser2* parser2 = CTestParser2::NewL();
+	CRichText::ActivateParserL(parser2);
+	// Create a CRichText
+	CParaFormatLayer* GlobalParaFormatLayer = CParaFormatLayer::NewL();
+	CCharFormatLayer* GlobalCharFormatLayer = CCharFormatLayer::NewL();
+	CRichText* richText = CRichText::NewL(GlobalParaFormatLayer, GlobalCharFormatLayer, CEditableText::ESegmentedStorage, CEditableText::EDefaultTextGranularity);
+	richText->SetPictureFactory(NULL, NULL);  // forces index generation
+	// Switch on parsers for this CRichText
+	CEditObserver* editObserver = new (ELeave) CEditObserver;
+	richText->SetEditObserver(editObserver);
+
+	// Insert 1000 chars (repeated string)
+	TInt i;
+	for (i = 0; i < 100; i++)
+		richText->InsertL(richText->DocumentLength(), _L("abcdefghij"));
+
+	TInt startTags;
+	TInt lengthTags;
+	TInt startPosition = 0;
+	
+	// Run test twice, once at start of string, once at end.
+	for (TInt i=0;i < 2;i++)
+		{
+		//Insert target string that is not complete (for code coverage purposes).
+		richText->InsertL(startPosition, _L("TARGE"));
+		// Parse range provided only. Parser two will set it's parser tags on "ARG".
+		test(richText->ParseText(startTags, lengthTags, EFalse));
+		// Ensure this call does not fail at end of document (anymore). This gets
+		// the position of the next tag at the end of document position. Should
+		// be none found at end of document.
+		test((richText->PositionOfNextTag(richText->DocumentLength(), parser1)) == -1);
+		
+		// Parse all text (for coverage purposes). This will first clear all tags,
+		// and then re-applies the tags again on "ARG"
+		test(richText->ParseText(startTags, lengthTags, ETrue));
+		test((richText->PositionOfNextTag(richText->DocumentLength(), parser1)) == -1);
+		
+		// Complete target string. The purpose of completing the target string
+		// here is for code coverage. The text has changed and the code path in 
+		// parselst.cpp will change.
+		richText->InsertL(startPosition + 5, 'T');
+
+		// Parse range provided only. The tags on "ARG" are considered
+		// lower precendence, so this will clear the parser tags on "ARG" and 
+		// will apply the parser tags on "TARGET". 
+		test(richText->ParseText(startTags, lengthTags, EFalse));
+		test((richText->PositionOfNextTag(richText->DocumentLength(), parser1)) == -1);
+				
+		// Completely remove target string
+		richText->DeleteL(startPosition, 6);
+		test(!richText->ParseText(startTags, lengthTags, EFalse));
+		test(!richText->ParseText(startTags, lengthTags, ETrue));
+		
+		startPosition = richText->DocumentLength();
+		}
+
+	// Clean up
+
+	delete richText;
+	delete GlobalCharFormatLayer;
+	delete GlobalParaFormatLayer;
+	CRichText::DeactivateParser(parser1);
+	delete parser1;
+	CRichText::DeactivateParser(parser2);
+	delete parser2;
+	CRichText::DeactivateParserDefaults();
+	delete editObserver;
+	}
+
+
+TInt E32Main()
+	{
+	TrapCleanup = CTrapCleanup::New();
+	TRAPD(r,\
+		{\
+		for (TInt i=KTestCleanupStack;i>0;i--)\
+			CleanupStack::PushL((TAny*)1);\
+		test(r==KErrNone);\
+		CleanupStack::Pop(KTestCleanupStack);\
+		});
+    test(r == KErrNone);
+	test.Title();
+
+	test.Start(_L("Testing memory under normal conditions"));
+	TInt ret;
+
+	__UHEAP_MARK;
+	TRAP(ret, Test1());
+    test(ret == KErrNone);
+	__UHEAP_MARKEND;
+
+	__UHEAP_MARK;
+	TRAP(ret, Test2());
+    test(ret == KErrNone);
+	__UHEAP_MARKEND;
+
+	__UHEAP_MARK;
+	TRAP(ret, Test3());
+    test(ret == KErrNone);
+	__UHEAP_MARKEND;
+
+	__UHEAP_MARK;
+	TRAP(ret, Test4());
+    test(ret == KErrNone);
+	__UHEAP_MARKEND;
+
+	__UHEAP_MARK;
+	TRAP(ret, Test5());
+    test(ret == KErrNone);
+	__UHEAP_MARKEND;
+
+	__UHEAP_MARK;
+	TRAP(ret, Test6());
+    test(ret == KErrNone);
+	__UHEAP_MARKEND;
+	
+	__UHEAP_MARK;
+	TRAP(ret, Test7());
+    test(ret == KErrNone);
+	__UHEAP_MARKEND;
+	
+	delete TrapCleanup;
+
+	test.End();
+	test.Close();
+	return 0;
+	}