textrendering/texthandling/ttext/T_parse.cpp
author Pat Downey <patd@symbian.org>
Wed, 01 Sep 2010 12:39:40 +0100
branchRCL_3
changeset 55 336bee5c2d35
parent 54 748ec5531811
permissions -rw-r--r--
Revert incorrect RCL_3 drop: Revision: 201021 Kit: 201035

/*
* 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;
	}