/*
* Copyright (c) 2000-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: 
* TUndo.cpp test file for UndoSystem classes
*
*/


#include <e32test.h>

#include "UndoSystem.h"
#include "UndoSystemImpl.h"
#include "EditorUndo.h"
#include "EditorPlainTextUndo.h"
#include <txtetext.h>
#include <conpics.h>
#include <s32mem.h>
#include <s32ucmp.h>
#include "TGraphicsContext.h"
#include "form_and_etext_editor.h"

#ifdef SYMBIAN_ENABLE_SPLIT_HEADERS
#include <txtclipboard.h>
#include "txtfmlyr_internal.h"
#endif

#define UNUSED_VAR(a) a = a

using namespace UndoSystem;

namespace
{
CTrapCleanup* TrapCleanup;
RTest test(_L("TUndo - Undo system"));
}

//
//
//  logger
//
//

class CLogger : public CBase
	{
public:
	virtual void Output(const TDesC& aText) = 0;
	void Output(TInt a)
		{
		TBuf<16> num(_L("<"));
		num.AppendNum(a);
		num.Append(_L(">"));
		Output(num);
		}
	};

namespace
{
CLogger& operator<<(CLogger& aLog, const TDesC& aText)
	{
	aLog.Output(aText);
	return aLog;
	}
CLogger& operator<<(CLogger& aLog, TInt aVal)
	{
	aLog.Output(aVal);
	return aLog;
	}
}

class CCheckingLogger : public CLogger
	{
	const TDesC*	iCheck;
	TInt			iPos;
	TBool			iFailed;
public:
	void SetCheckString(const TDesC& aCheck) { iCheck = &aCheck; iPos = 0; iFailed = EFalse; }
	TBool Failed() { return iFailed; }
	TBool Passed() { return !iFailed && iCheck && iCheck->Length() == iPos; }
	void Output(const TDesC& aText)
		{
		if (!iCheck || iFailed)
			return;
		TInt length = aText.Length();
		TInt maxLength = iCheck->Length() - iPos;
		TPtrC mid = iCheck->Mid(iPos, length < maxLength? length : maxLength);
		if (aText.Compare(mid) != 0)
			iFailed = ETrue;
		else
			iPos += aText.Length();
		}
	};

class CStoringLogger : public CLogger
	{
	HBufC*	iStore;
	TInt	iMaxLength;
public:
	~CStoringLogger() { delete iStore; }
	HBufC* GetStore() { HBufC* r = iStore; iStore = 0; iMaxLength = 0; return r; }
	void Output(const TDesC& aText)
		{
		if (!iStore)
			{
			iStore = HBufC::NewL(50);
			iMaxLength = 50;
			}
		while (iMaxLength < aText.Length() + iStore->Length())
			{
			iMaxLength *= 2;
			iStore = iStore->ReAllocL(iMaxLength);
			}
		iStore->Des().Append(aText);
		};
	};

//
//
//  commands
//
//

// internal commands
class CCommandOffset;
class CCommandToggle;
class CTestCommand : public CSingleCommand
	{
public:
	TUid FamilyUid() const
		{
		return TUid::Uid(12345);
		}
	virtual CCommandOffset* CastToCCommandOffset() { return 0; }
	virtual CCommandToggle* CastToCCommandToggle() { return 0; }
	};

// Offset
class CCommandOffset : public CTestCommand
	{
	TInt	iOffset;
	TInt*	iTarget;
	CLogger* iLogger;
	CCommandOffset() {}
public:
	static CCommandOffset* NewL(TInt aOffset, TInt* aTarget, CLogger* aLogger = 0)
		{
		CCommandOffset* r = new(ELeave) CCommandOffset;
		r->iOffset = aOffset;
		r->iTarget = aTarget;
		r->iLogger = aLogger;
		return r;
		}
	void SetLogger(CLogger* aLogger) { iLogger = aLogger; }
	TInt ExecuteL() const
		{
		if (iLogger)
			(*iLogger) << _L("offset") << iOffset;
		*iTarget += iOffset;
		return KErrNone;
		}
	CCommand* CreateInverseL() const
		{
		return NewL(-iOffset, iTarget, iLogger);
		}
	void Add(TInt aOffset) { iOffset += aOffset; }
	CCommandOffset* CastToCCommandOffset() { return this; }
	};

// Negate (only if iTog is ETrue!)
class CCommandToggle : public CTestCommand
	{
	TBool iTog;
	TInt* iTarget;
	CLogger* iLogger;
	CCommandToggle() {}
public:
	static CCommandToggle* NewL(TBool aTog, TInt* aTarget, CLogger* aLogger = 0)
		{
		CCommandToggle* r = new(ELeave) CCommandToggle;
		r->iTog = aTog;
		r->iTarget = aTarget;
		r->iLogger = aLogger;
		return r;
		}
	void SetLogger(CLogger* aLogger) { iLogger = aLogger; }
	TInt ExecuteL() const
		{
		if (iLogger)
			{
			(*iLogger) << _L("negate") << (iTog? 1:0);
			}
		if (iTog)
			*iTarget = -*iTarget;
		return KErrNone;
		}
	CCommand* CreateInverseL() const
		{
		return NewL(iTog, iTarget, iLogger);
		}
	void Add(TBool aTog)
		{
		if (aTog)
			iTog = !iTog;
		}
	CCommandToggle* CastToCCommandToggle() { return this; }
	};

// command prototypes
class CCommandIncProto : public CTestCommand
	{
	TInt*		iTarget;
	CLogger*	iLogger;
	CCommandIncProto() {}
public:
	static CCommandIncProto* NewL(TInt* aTarget, CLogger* aLogger = 0)
		{
		CCommandIncProto* r = new(ELeave) CCommandIncProto;
		r->iTarget = aTarget;
		r->iLogger = aLogger;
		return r;
		}
	TInt ExecuteL() const
		{
		if (iLogger)
			(*iLogger) << _L("inc<>");
		++*iTarget;
		return KErrNone;
		}
	CCommand* CreateInverseL() const
		{
		return CCommandOffset::NewL(-1, iTarget, iLogger);
		}
	TBool PrepareToAddInverseToLastL(CSingleCommand& aLastCommand) const
		{
		if (aLastCommand.FamilyUid() != TUid::Uid(12345))
			return EFalse;
		return !!static_cast<CTestCommand&>(aLastCommand).CastToCCommandOffset();
		}
	void AddInverseToLast(CSingleCommand& aLastCommand) const
		{
		CCommandOffset* c =
			static_cast<CTestCommand&>(aLastCommand).CastToCCommandOffset();
		ASSERT(c);
		c->Add(-1);
		}
	};

class CCommandDecProto : public CTestCommand
	{
	TInt*		iTarget;
	CLogger*	iLogger;
	CCommandDecProto() {}
public:
	static CCommandDecProto* NewL(TInt* aTarget, CLogger* aLogger = 0)
		{
		CCommandDecProto* r = new(ELeave) CCommandDecProto;
		r->iTarget = aTarget;
		r->iLogger = aLogger;
		return r;
		}
	TInt ExecuteL() const
		{
		if (iLogger)
			(*iLogger) << _L("dec<>");
		--*iTarget;
		return KErrNone;
		}
	CCommand* CreateInverseL() const
		{
		return CCommandOffset::NewL(1, iTarget, iLogger);
		}
	TBool PrepareToAddInverseToLastL(CSingleCommand& aLastCommand) const
		{
		if (aLastCommand.FamilyUid() != TUid::Uid(12345))
			return EFalse;
		return !!static_cast<CTestCommand&>(aLastCommand).CastToCCommandOffset();
		}
	void AddInverseToLast(CSingleCommand& aLastCommand) const
		{
		CCommandOffset* c =
			static_cast<CTestCommand&>(aLastCommand).CastToCCommandOffset();
		ASSERT(c);
		c->Add(1);
		}
	};

class CCommandNegProto : public CTestCommand
	{
	TInt*		iTarget;
	CLogger*	iLogger;
	CCommandNegProto() {}
public:
	static CCommandNegProto* NewL(TInt* aTarget, CLogger* aLogger = 0)
		{
		CCommandNegProto* r = new(ELeave) CCommandNegProto;
		r->iTarget = aTarget;
		r->iLogger = aLogger;
		return r;
		}
	TInt ExecuteL() const
		{
		if (iLogger)
			(*iLogger) << _L("neg<>");
		*iTarget = -*iTarget;
		return KErrNone;
		}
	CCommand* CreateInverseL() const
		{
		return CCommandToggle::NewL(ETrue, iTarget, iLogger);
		}
	TBool PrepareToAddInverseToLastL(CSingleCommand& aLastCommand) const
		{
		if (aLastCommand.FamilyUid() != TUid::Uid(12345))
			return EFalse;
		return !!static_cast<CTestCommand&>(aLastCommand).CastToCCommandToggle();
		}
	void AddInverseToLast(CSingleCommand& aLastCommand) const
		{
		CCommandToggle* c =
			static_cast<CTestCommand&>(aLastCommand).CastToCCommandToggle();
		ASSERT(c);
		c->Add(ETrue);
		}
	};

// command whose inverse is a batch command
class CCommandDecThenNegProto : public CTestCommand
	{
	TInt*		iTarget;
	CLogger*	iLogger;
	CCommandDecThenNegProto() {}
public:
	static CCommandDecThenNegProto* NewL(TInt* aTarget, CLogger* aLogger = 0)
		{
		CCommandDecThenNegProto* r = new(ELeave) CCommandDecThenNegProto;
		r->iTarget = aTarget;
		r->iLogger = aLogger;
		return r;
		}
	TInt ExecuteL() const
		{
		if (iLogger)
			(*iLogger) << _L("decneg<>");
		*iTarget = -(*iTarget - 1);
		return KErrNone;
		}
	CCommand* CreateInverseL() const
		{
		CBatchCommand* batch = CBatchCommand::NewLC();
		batch->PushL(CCommandOffset::NewL(1, iTarget, iLogger));
		batch->PushL(CCommandToggle::NewL(ETrue, iTarget, iLogger));
		CleanupStack::Pop(batch);
		return batch;
		}
	};

class CCommandCannotDo : public CTestCommand
	{
	CLogger*	iLogger;
public:
	static CCommandCannotDo* NewL(CLogger* aLogger)
		{
		CCommandCannotDo* r = new(ELeave) CCommandCannotDo;
		r->iLogger = aLogger;
		return r;
		}
	TInt ExecuteL() const
		{
		if (iLogger)
			(*iLogger) << _L("nodo<>");
		return KErrNotSupported;
		}
	CCommand* CreateInverseL() const
		{
		return 0;
		}
	};

class CCommandCannotInvert : public CTestCommand
	{
	CLogger*	iLogger;
public:
	static CCommandCannotInvert* NewL(CLogger* aLogger)
		{
		CCommandCannotInvert* r = new(ELeave) CCommandCannotInvert;
		r->iLogger = aLogger;
		return r;
		}
	TInt ExecuteL() const
		{
		if (iLogger)
			(*iLogger) << _L("noinv<>");
		return KErrNone;
		}
	CCommand* CreateInverseL() const
		{
		if (iLogger)
			(*iLogger) << _L("noinvfail.");
		User::Leave(KErrNotSupported);
		return 0;
		}
	};

class CCommandLeavesInvert : public CTestCommand
	{
	CLogger*	iLogger;
public:
	mutable TBool iFail;
	static CCommandLeavesInvert* NewL(CLogger* aLogger)
		{
		CCommandLeavesInvert* r = new(ELeave) CCommandLeavesInvert;
		r->iLogger = aLogger;
		r->iFail = ETrue;
		return r;
		}
	TInt ExecuteL() const
		{
		if (iLogger)
			(*iLogger) << _L("leaveinv<>");
		return KErrNone;
		}
	CCommand* CreateInverseL() const
		{
		if (iFail)
			{
			iFail = EFalse;
			if (iLogger)
				(*iLogger) << _L("noinvfail.");
			User::Leave(KErrNotFound);
			}
		return 0;
		}
	};

class CCommandNoMemory : public CTestCommand
	{
	CLogger*	iLogger;
public:
	TBool iFailInvert;
	TBool iFailAddToLast;
	TBool iFailExecute;
	mutable TBool iLogExecuteFailed;

	static CCommandNoMemory* NewL(CLogger* aLogger)
		{
		CCommandNoMemory* r = new(ELeave) CCommandNoMemory;
		r->iLogger = aLogger;
		r->iFailInvert		= ETrue;
		r->iFailAddToLast	= ETrue;
		r->iFailExecute		= ETrue;
		r->iLogExecuteFailed= ETrue;
		return r;
		}
	TInt ExecuteL() const
		{
		if (iFailExecute)
			{
			if (iLogger && iLogExecuteFailed)
				(*iLogger) << _L("nomemfailexe.");
			iLogExecuteFailed = EFalse;
			User::Leave(KErrNoMemory);
			}
		if (iLogger)
			(*iLogger) << _L("nomem<>");
		return KErrNone;
		}
	CCommand* CreateInverseL() const
		{
		if (iFailInvert)
			{
			if (iLogger)
				(*iLogger) << _L("nomemfailinv.");
			User::Leave(KErrNoMemory);
			}
		return 0;
		}
	TBool PrepareToAddInverseToLastL(CSingleCommand&) const
		{
		if (iFailAddToLast)
			{
			if (iLogger)
				(*iLogger) << _L("nomemfailadd.");
			User::Leave(KErrNoMemory);
			}
		return EFalse;
		}
	};

// this gatekeeper refuses non-undoable requests
class CRefuserGatekeeper : public CBase, public MNotUndoableGatekeeper
	{
public:
	TBool RetryOutOfMemoryL(TInt)
		{
		return EFalse;
		}
	TBool AllowNotUndoableL(TInt)
		{
		return EFalse;
		}
	};

// this gatekeeper permits all non-undoable requests
// (not just KErrNotSupported and KErrNoMemory)
class CPermitterGatekeeper : public CBase, public MNotUndoableGatekeeper
	{
public:
	TBool RetryOutOfMemoryL(TInt)
		{
		return EFalse;
		}
	TBool AllowNotUndoableL(TInt)
		{
		return ETrue;
		}
	};

// this gatekeeper makes the CCommandNoMemory fail less the more times it is called
class CMemoryReclaimGatekeeper : public CRefuserGatekeeper
	{
public:
	CCommandNoMemory* iTarget;
	CMemoryReclaimGatekeeper(CCommandNoMemory* aTarget = 0) : iTarget(aTarget) {}
	TBool RetryOutOfMemoryL(TInt aNumRetries)
		{
		if (aNumRetries == 0)
			{
			iTarget->iFailAddToLast = EFalse;
			return ETrue;
			}
		if (aNumRetries == 1)
			{
			iTarget->iFailInvert = EFalse;
			return ETrue;
			}
		if (aNumRetries == 2)
			{
			iTarget->iFailExecute = EFalse;
			return ETrue;
			}
		return EFalse;
		}
	};


//
//
//	Editor
//
//

// a cut-down set of attributes for testing purposes
class CUndoTestPicture : public CPicture
	{
	TBuf<1> iDesc;
public:
	CUndoTestPicture(TInt aChar) : iDesc(1) { iDesc[0] = static_cast<TText>(aChar); }
	void Draw(CGraphicsContext&, const TPoint&, const TRect&, MGraphicsDeviceMap*) const {}
	void ExternalizeL(RWriteStream&) const {}
	void GetOriginalSizeInTwips(TSize&) const {}
	TPtrC Description() const { return iDesc; }
	};

struct TTestAttributes
	{
	TInt8 iCharFlags;
	TInt8 iParFlags;
	TInt8 iStyle;
	TTestAttributes() : iCharFlags(0), iParFlags(0), iStyle(-1) {}
	TBool operator==(TTestAttributes& a)
		{
		return iCharFlags == a.iCharFlags && iParFlags == a.iParFlags && iStyle == a.iStyle;
		}
	};
CLogger& operator<<(CLogger& log, TTestAttributes& at)
	{
	TBuf<3> buf(_L("Aa0"));
	buf[0] = (TText)(buf[0] + (at.iCharFlags & 15));
	buf[1] = (TText)(buf[1] + (at.iParFlags & 15));
	buf[2] = (TText)(buf[2] + at.iStyle);
	return log << buf;
	}
// Test editor, badly behaved if something leaves.
// The only formats supported are bold, italic, keep together and keep with next.
class CTestEditor : public CBase, public MUnifiedEditor,
	public MUnifiedEditor::MStyleSupport,
	public MUnifiedEditor::MPictureSupport,
	public MUnifiedEditor::MClipboardSupport
	{
	TTestAttributes iBase;
	TTestAttributes iStyles[10];
	TBuf<KMaxParagraphStyleName> iStyleNames[10];
	TInt iNumStyles;
	CArrayFix<TTestAttributes>* iAttrs;
	CBufSeg* iText;
	CArrayFix<CUndoTestPicture*>* iPics;

	CTestEditor* ConstructL()
		{
		iText = CBufSeg::NewL(5 * sizeof(TText));
		iAttrs = new(ELeave) CArrayFixFlat<TTestAttributes>(5);
		iPics = new(ELeave) CArrayFixFlat<CUndoTestPicture*>(5);
		return this;
		}
	TInt Style(const TDesC& aName) const
		{
		for (int i = 0; i != iNumStyles; ++i)
			if (aName == iStyleNames[i])
				return i;
		return -1;
		}
	TInt InsertStylePos(const TDesC& aName) const
		{
		int i;
		for (i = 0; i != iNumStyles; ++i)
			if (aName.Compare(iStyleNames[i]) <= 0)
				return i;
		return i;
		}
	void ReassignStyles(TInt aFrom, TInt aTo)
		{
		ASSERT(-1 <= aTo && aTo < iNumStyles);
		ASSERT(0 <= aFrom && aFrom < iNumStyles);
		if (aTo == aFrom)
			return;
		TInt setTo	= aTo;
		if (aTo == -1)
			aTo = iNumStyles - 1;
		TInt low	= aFrom;
		TInt high	= aTo;
		TInt delta	= -1;
		if (aTo < aFrom)
			{
			low		= aTo;
			high	= aFrom;
			delta	= 1;
			}
		TInt len = DocumentLength();

		int i;
		for (i = 0; i != len; ++i)
			{
			TTestAttributes* attr = &iAttrs->At(i);
			if (aFrom == attr->iStyle)
				attr->iStyle = static_cast<TInt8>(setTo);
			else if (low <= attr->iStyle && attr->iStyle <= high)
				attr->iStyle = static_cast<TInt8>(attr->iStyle + delta);
			}
		for (i = aFrom; i != aTo; i -= delta)
			{
			iStyles[i] = iStyles[i - delta];
			iStyleNames[i] = iStyleNames[i - delta];
			}
		}
	void DoDeleteStyle(TInt aForDeletion)
		{
		ASSERT(aForDeletion < iNumStyles);
		ReassignStyles(aForDeletion, -1);
		--iNumStyles;
		}
	void DoAddStyle(const TDesC& aNewName, const TTestAttributes& aAttr)
		{
		ASSERT(iNumStyles < 10);
		TInt pos = InsertStylePos(aNewName);
		++iNumStyles;
		ReassignStyles(iNumStyles - 1, pos);
		iStyles[pos] = aAttr;
		iStyleNames[pos] = aNewName;
		}
	static void BoldToAttr(TTestAttributes& aAttr, const TTmCharFormat& aCFormat)
		{
		if (aCFormat.iFontSpec.IsBold())
			aAttr.iCharFlags |= 1;
		else
			aAttr.iCharFlags &= ~1;
		}
	static void ItalicToAttr(TTestAttributes& aAttr, const TTmCharFormat& aCFormat)
		{
		if (aCFormat.iFontSpec.IsItalic())
			aAttr.iCharFlags |= 2;
		else
			aAttr.iCharFlags &= ~2;
		}
	static void CharFormatToAttr(TTestAttributes& aAttr, const TTmCharFormat& aCFormat)
		{
		aAttr.iCharFlags = 0;
		BoldToAttr(aAttr, aCFormat);
		ItalicToAttr(aAttr, aCFormat);
		}
	static void CharLayerToAttr(TTestAttributes& aAttr, const TTmCharFormatLayer& aCLayer)
		{
		if (aCLayer.iMask.iFlags & TTmCharFormatMask::EBold)
			{
			aAttr.iCharFlags |= 4;
			BoldToAttr(aAttr, aCLayer.iFormat);
			}
		if (aCLayer.iMask.iFlags & TTmCharFormatMask::EItalic)
			{
			aAttr.iCharFlags |= 8;
			ItalicToAttr(aAttr, aCLayer.iFormat);
			}
		}
	static void KeepTogetherToAttr(TTestAttributes& aAttr, const RTmParFormat& aPFormat)
		{
		if (aPFormat.iFlags & RTmParFormat::EKeepTogether)
			aAttr.iParFlags |= 1;
		else
			aAttr.iParFlags &= ~1;
		}
	static void KeepWithNextToAttr(TTestAttributes& aAttr, const RTmParFormat& aPFormat)
		{
		if (aPFormat.iFlags & RTmParFormat::EKeepWithNext)
			aAttr.iParFlags |= 2;
		else
			aAttr.iParFlags &= ~2;
		}
	static void ParFormatToAttr(TTestAttributes& aAttr, const RTmParFormat& aPFormat)
		{
		aAttr.iParFlags = 0;
		KeepTogetherToAttr(aAttr, aPFormat);
		KeepWithNextToAttr(aAttr, aPFormat);
		}
	static void ParLayerToAttr(TTestAttributes& aAttr, const RTmParFormatLayer& aPLayer)
		{
		if (aPLayer.iMask.iFlags & TTmParFormatMask::EKeepTogether)
			{
			aAttr.iParFlags |= 4;
			KeepTogetherToAttr(aAttr, aPLayer.iFormat);
			}
		if (aPLayer.iMask.iFlags & TTmParFormatMask::EKeepWithNext)
			{
			aAttr.iParFlags |= 8;
			KeepWithNextToAttr(aAttr, aPLayer.iFormat);
			}
		}
	static void BoldAttrToCharFormat(TTmCharFormat& aCFormat, const TTestAttributes& aAttr)
		{
		if (aAttr.iCharFlags & 1)
			aCFormat.iFontSpec.SetBold(ETrue);
		}
	static void ItalicAttrToCharFormat(TTmCharFormat& aCFormat, const TTestAttributes& aAttr)
		{
		if (aAttr.iCharFlags & 2)
			aCFormat.iFontSpec.SetItalic(ETrue);
		}
	static void ResetCharFormat(TTmCharFormat& aCFormat)
		{
		TTmCharFormat c;
		aCFormat = c;
		}
	static void AttrToCharFormat(TTmCharFormat& aCFormat, const TTestAttributes& aAttr)
		{
		ResetCharFormat(aCFormat);
		BoldAttrToCharFormat(aCFormat, aAttr);
		ItalicAttrToCharFormat(aCFormat, aAttr);
		}
	static void MergeAttrToCharLayer(TTmCharFormatLayer& aCLayer, const TTestAttributes& aAttr)
		{
		if (aAttr.iCharFlags & 4)
			{
			aCLayer.iMask.iFlags |= TTmCharFormatMask::EBold;
			BoldAttrToCharFormat(aCLayer.iFormat, aAttr);
			}
		if (aAttr.iCharFlags & 8)
			{
			aCLayer.iMask.iFlags |= TTmCharFormatMask::EItalic;
			ItalicAttrToCharFormat(aCLayer.iFormat, aAttr);
			}
		}
	static void AttrToCharLayer(TTmCharFormatLayer& aCLayer, const TTestAttributes& aAttr)
		{
		ResetCharFormat(aCLayer.iFormat);
		aCLayer.iMask.iFlags = 0;
		MergeAttrToCharLayer(aCLayer, aAttr);
		}
	static void ResetParFormat(RTmParFormat& aPFormat)
		{
		RTmParFormat p;
		CleanupClosePushL(p);
		aPFormat.CopyL(p);
		CleanupStack::PopAndDestroy();
		}
	static void KeepTogetherAttrToParFormat(RTmParFormat& aPFormat, const TTestAttributes& aAttr)
		{
		if (aAttr.iParFlags & 1)
			aPFormat.iFlags |= RTmParFormat::EKeepTogether;
		}
	static void KeepWithNextAttrToParFormat(RTmParFormat& aPFormat, const TTestAttributes& aAttr)
		{
		if (aAttr.iParFlags & 2)
			aPFormat.iFlags |= RTmParFormat::EKeepWithNext;
		}
	static void AttrToParFormat(RTmParFormat& aPFormat, const TTestAttributes& aAttr)
		{
		ResetParFormat(aPFormat);
		KeepTogetherAttrToParFormat(aPFormat, aAttr);
		KeepWithNextAttrToParFormat(aPFormat, aAttr);
		}
	static void MergeAttrToParLayer(RTmParFormatLayer& aPLayer, const TTestAttributes& aAttr)
		{
		if (aAttr.iParFlags & 4)
			{
			aPLayer.iMask.iFlags |= TTmParFormatMask::EKeepTogether;
			KeepTogetherAttrToParFormat(aPLayer.iFormat, aAttr);
			}
		if (aAttr.iParFlags & 8)
			{
			aPLayer.iMask.iFlags |= TTmParFormatMask::EKeepWithNext;
			KeepWithNextAttrToParFormat(aPLayer.iFormat, aAttr);
			}
		}
	static void AttrToParLayer(RTmParFormatLayer& aPLayer, const TTestAttributes& aAttr)
		{
		ResetParFormat(aPLayer.iFormat);
		aPLayer.iMask.iFlags = 0;
		MergeAttrToParLayer(aPLayer, aAttr);
		}
public:
	~CTestEditor()
		{
		delete iAttrs;
		// workaround for CBufSeg bug
		if (0 < iText->Size())
			iText->Delete(iText->Size() - 1, 1);
		delete iText;
		delete iPics;
		}
	static CTestEditor* NewL()	{ return (new(ELeave) CTestEditor)->ConstructL(); }
	void Reset()
		{
		iAttrs->Reset();
		iText->Reset();
		iPics->Reset();
		iNumStyles = 0;
		}
	void AlterGranularityL(TInt aNewGranularity)
		{
		CBufSeg* newIText = CBufSeg::NewL(aNewGranularity * sizeof(TText));
		CleanupStack::PushL(newIText);
		TBuf8<32> transfer;
		TInt pos = 0;
		while (pos < iText->Size())
			{
			TInt length = transfer.MaxLength();
			if (iText->Size() - pos < length)
				length = iText->Size() - pos;
			iText->Read(pos, transfer, length);
			newIText->InsertL(pos, transfer, length);
			pos += transfer.Length();
			}
		CleanupStack::Pop(newIText);
		// workaround for CBufSeg bug
		if (0 < iText->Size())
			iText->Delete(iText->Size() - 1, 1);
		delete iText;
		iText = newIText;
		}
	void Print(CLogger& log)
		{
		TInt length = DocumentLength();
		int i;
		log << _L("text{");
		for (i = 0; i < length;)
			{
			TPtrC seg;
			GetText(i, seg);
			TInt picPos = seg.Locate(CEditableText::EPictureCharacter);
			if (0 < picPos)
				{
				// shorten seg to just before the picture character
				TPtrC temp(seg.Ptr(), picPos);
				seg.Set(temp);
				}
			if (0 == picPos)
				{
				CUndoTestPicture* pic = iPics->At(i);
				if (pic)
					log << _L("{pic:") << pic->Description() << _L("}");
				else
					log << _L("{nopic}");
				++i;
				}
			else
				{
				log << seg;
				i += seg.Length();
				}
			}
		log << _L("} styles{");
		for(i = 0; i != iNumStyles; ++i)
			{
			if (i)
				log << _L(", ");
			log << iStyleNames[i] << _L(":") << iStyles[i];
			}
		log << _L("} attr{");
		for (i = 0; i != length; ++i)
			log << iAttrs->At(i);
		log << _L("} ");
		}
	MTmOptionalInterface* Interface(TUint aId)
		{
		if (aId == KUidMUnifiedEditorStyleSupport)
			return static_cast<MUnifiedEditor::MStyleSupport*>(this);
		if (aId == KUidMUnifiedEditorPictureSupport)
			return static_cast<MUnifiedEditor::MPictureSupport*>(this);
		if (aId == KUidMUnifiedEditorClipboardSupport)
			return static_cast<MUnifiedEditor::MClipboardSupport*>(this);
		return 0;
		}

	void InsertTextL(TInt aPos, const TDesC& aText,
					 const TDesC* aStyle,
					 const TTmCharFormatLayer* aCharFormat,
					 const RTmParFormatLayer* aParFormat)
		{
		TTestAttributes attr;
		attr.iStyle = aStyle? (TInt8)Style(*aStyle) : (TInt8)-1;
		if (aCharFormat)
			CharLayerToAttr(attr, *aCharFormat);
		if (aParFormat)
			ParLayerToAttr(attr, *aParFormat);
		iText->InsertL(aPos * sizeof(TText), aText.Ptr(), aText.Length() * sizeof(TText));
		iAttrs->InsertL(aPos, attr, aText.Length());
		CUndoTestPicture* nullPic = 0;
		iPics->InsertL(aPos, nullPic, aText.Length());
		}
	void DeleteTextL(TInt aPos, TInt aLength)
		{
		iText->Delete(aPos * sizeof(TText), aLength * sizeof(TText));
		iAttrs->Delete(aPos, aLength);
		for (int i = aPos; i != aPos + aLength; ++i)
			delete iPics->At(i);
		iPics->Delete(aPos, aLength);
		}
	void SetBaseFormatL(const TTmCharFormat& aCharFormat,const RTmParFormat& aParFormat)
		{
		CharFormatToAttr(iBase, aCharFormat);
		ParFormatToAttr(iBase, aParFormat);
		}
	void SetCharFormatL(TInt aPos,TInt aLength,const TTmCharFormatLayer& aFormat)
		{
		TInt end = aPos + aLength;
		if (DocumentLength() < end)
			end = DocumentLength();
		for (; aPos < end; ++aPos)
			CharLayerToAttr(iAttrs->At(aPos), aFormat);
		}
	void SetParFormatL(TInt aPos,TInt aLength,const RTmParFormatLayer& aFormat)
		{
		TInt end = aPos + aLength;
		if (DocumentLength() < end)
			end = DocumentLength();
		for (; aPos < end; ++aPos)
			ParLayerToAttr(iAttrs->At(aPos), aFormat);
		}
	void DeleteCharFormatL(TInt aPos,TInt aLength)
		{
		TInt end = aPos + aLength;
		if (DocumentLength() < end)
			end = DocumentLength();
		for (; aPos < end; ++aPos)
			iAttrs->At(aPos).iCharFlags = 0;
		}
	void DeleteParFormatL(TInt aPos,TInt aLength)
		{
		TInt end = aPos + aLength;
		if (DocumentLength() < end)
			end = DocumentLength();
		for (; aPos < end; ++aPos)
			iAttrs->At(aPos).iParFlags = 0;
		}
	TInt CreateStyleL(const RTmStyle& aStyle)
		{
		TInt styleNo = Style(aStyle.iName);
		if (0 <= styleNo)
			return KErrAlreadyExists;
		TTestAttributes newAttr;
		CharLayerToAttr(newAttr, aStyle.iCharFormat);
		ParLayerToAttr(newAttr, aStyle.iParFormat);
		DoAddStyle(aStyle.iName, newAttr);
		return KErrNone;
		}
	TInt ChangeStyleL(const RTmStyle& aStyle)
		{
		TInt styleNo = Style(aStyle.iName);
		if (styleNo < 0)
			return KErrNotFound;
		iStyles[styleNo] = TTestAttributes();
		CharLayerToAttr(iStyles[styleNo], aStyle.iCharFormat);
		ParLayerToAttr(iStyles[styleNo], aStyle.iParFormat);
		return KErrNone;
		}
	TInt SetStyleL(TInt aPos, TInt aLength, const TDesC& aName)
		{
		TInt styleNo(-1);
		if (aName.Length())
			{
			styleNo = Style(aName);
			if (styleNo < 0)
				return KErrNotFound;
			}
		TInt end = aPos + aLength;
		for (; aPos < end; ++aPos)
			iAttrs->At(aPos).iStyle = (TInt8)styleNo;
		return KErrNone;
		}
	TInt RenameStyleL(const TDesC& aOldName, const TDesC& aNewName)
		{
		TInt oldNo = Style(aOldName);
		if (oldNo < 0)
			return KErrNotFound;
		TTestAttributes temp = iStyles[oldNo];
		TInt newNo = InsertStylePos(aNewName);
		if (oldNo < newNo)
			--newNo;
		ReassignStyles(oldNo, newNo);
		iStyles[newNo] = temp;
		iStyleNames[newNo] = aNewName;
		return KErrNone;
		}
	TInt DeleteStyleL(const TDesC& aName)
		{
		TInt n = Style(aName);
		if (n < 0)
			return KErrNotFound;
		DoDeleteStyle(n);
		return KErrNone;
		}
	void InsertPictureL(TInt aPos, const TPictureHeader& aPictureIn)
		{
		TBuf<1> picChar(1);
		picChar[0] = CEditableText::EPictureCharacter;
		InsertTextL(aPos, picChar, 0, 0, 0);
		iPics->At(aPos) = static_cast<CUndoTestPicture*>(aPictureIn.iPicture.AsPtr());
		}
	void DropPictureL(TInt aPos)
		{
		TPtrC ptr;
		GetText(aPos, ptr);
		if (ptr[0] == CEditableText::EPictureCharacter)
			{
			iPics->At(aPos) = 0;
			DeleteTextL(aPos, 1);
			}
		}
	void Picture(TInt aPos, TPictureHeader& aPictureOut) const
		{
		CPicture* pic = iPics->At(aPos);
		aPictureOut.iPictureType = KUidXzePictureType;
		aPictureOut.iPicture = pic;
		}
	TInt DocumentLength() const
		{
		return iText->Size() / sizeof(TText);
		}
	void GetText(TInt aPos, TPtrC& aText) const
		{
		iText->Compress();
		if (DocumentLength() <= aPos)
			aPos = DocumentLength();
		TPtr8 ptr = iText->Ptr(aPos * sizeof(TText));
		aText.Set((TText*)ptr.Ptr(), ptr.Length()/sizeof(TText));
		}
	void GetBaseFormatL(TTmCharFormat& aCharFormat, RTmParFormat& aParFormat) const
		{
		AttrToCharFormat(aCharFormat, iBase);
		AttrToParFormat(aParFormat, iBase);
		}
	void GetCharFormat(TInt aPos, TFormatLevel aLevel,
					   TTmCharFormatLayer& aFormat, TInt& aRunLength) const
		{
		TInt length = DocumentLength();
		if (length <= aPos)
			{
			aRunLength = 0;
			return;
			}
		TTestAttributes attr = iAttrs->At(aPos);
		if (aLevel == ESpecific)
			{
			AttrToCharLayer(aFormat, attr);
			}
		else
			{
			AttrToCharLayer(aFormat, iBase);
			MergeAttrToCharLayer(aFormat, attr);
			}
		TInt pos = aPos + 1;
		while (pos < length && attr == iAttrs->At(pos))
			++pos;
		aRunLength = pos - aPos;
		}
	void GetParFormatL(TInt aPos, TFormatLevel aLevel,
					   RTmParFormatLayer& aFormat, TInt& aRunLength) const
		{
		TInt length = DocumentLength();
		if (length <= aPos)
			{
			aRunLength = 0;
			return;
			}
		TTestAttributes attr = iAttrs->At(aPos);
		if (aLevel == ESpecific)
			{
			AttrToParLayer(aFormat, attr);
			}
		else
			{
			AttrToParLayer(aFormat, iBase);
			MergeAttrToParLayer(aFormat, attr);
			}
		TInt pos = aPos + 1;
		while (pos < length && attr == iAttrs->At(pos))
			++pos;
		aRunLength = pos - aPos;
		}
	TInt StyleCount() const { return iNumStyles; }
	void GetStyle(TInt aPos, TPtrC& aName, TInt& aRunLength) const
		{
		TInt length = DocumentLength();
		if (aPos < 0 || length <= aPos)
			{
			aName.Set(iStyleNames[0].Ptr(), 0);
			aRunLength = 0;
			return;
			}
		TInt styleNo = iAttrs->At(aPos).iStyle;
		if (styleNo < 0)
			aName.Set(iStyleNames[0].Ptr(), 0);
		else
			aName.Set(iStyleNames[styleNo]);
		TInt pos = aPos + 1;
		while (pos < length && iAttrs->At(pos).iStyle == styleNo)
			++pos;
		aRunLength = pos - aPos;
		return;
		}
	TInt GetStyleByNameL(const TDesC& aName, RTmStyle& aStyle) const
		{
		return GetStyleByIndexL(Style(aName), aStyle);
		}
	TInt GetStyleByIndexL(TInt aIndex, RTmStyle& aStyle) const
		{
		if (aIndex < 0 || iNumStyles <= aIndex)
			return KErrNotFound;
		aStyle.iName = iStyleNames[aIndex];
		AttrToParLayer(aStyle.iParFormat, iStyles[aIndex]);
		AttrToCharLayer(aStyle.iCharFormat, iStyles[aIndex]);
		return KErrNone;
		}
	void CopyToStoreL(CStreamStore& aStore, CStreamDictionary& aDictionary,
		TInt aPos, TInt aLength) const
		{
		ASSERT(aPos + aLength <= DocumentLength());
		if (aLength <= 0)
			return;
		RStoreWriteStream stream;
		TStreamId id = stream.CreateLC(aStore);
		stream.WriteInt32L(aLength);
		RBufReadStream input_stream(*iText, aPos * sizeof(TText));
		TMemoryStreamUnicodeSource source(input_stream);
		TUnicodeCompressor c;
		c.CompressL(stream, source, KMaxTInt, aLength);
		input_stream.Close();
		stream.CommitL();
		aDictionary.AssignL(KClipboardUidTypePlainText, id);
		CleanupStack::PopAndDestroy();		// close stream

		// now write out formatting in our own bizarre format
		//...
		// in actual fact this probably wouldn't test that much, so I won't
		// bother right now.
		}
	void PasteFromStoreL(const CStreamStore& aStore,
		const CStreamDictionary& aDictionary, TInt aPos)
		{
		ASSERT(aPos <= DocumentLength());
		TStreamId id = aDictionary.At(KClipboardUidTypePlainText);
		RStoreReadStream stream;
		stream.OpenLC(aStore, id);
		TInt length = stream.ReadInt32L();
		RBufWriteStream bufferStream;
		bufferStream.Insert(*iText, aPos * sizeof(TText));
		TMemoryStreamUnicodeSink sink(bufferStream);
		TUnicodeExpander e;
		e.ExpandL(sink, stream, length);
		bufferStream.CommitL();
		bufferStream.Close();
		CleanupStack::PopAndDestroy();	// close stream

		// and if we get round to adding some formatting to the copy method,
		// then we should deal with it here also
		//...
		// but not today. Just add the appropriate spaces into all the structures.
		TTestAttributes attr;
		iAttrs->InsertL(aPos, attr, length);
		CUndoTestPicture* nullPic = 0;
		iPics->InsertL(aPos, nullPic, length);
		}
	};

CLogger& operator<<(CLogger& log, CTestEditor& ed) { ed.Print(log); return log; }

// 1 - CCommandStack test
TInt ExecuteStackL(CCommandStack& a)
	{
	while (a.Top())
		{
		CSingleCommand* single = a.Top()->Single();
		if (!single)
			{
			test.Printf(_L("CCommandStack : stack unexpectedly contained batches"));
			a.Reset();
			return 1;
			}
		single->ExecuteL();
		delete single;
		a.Pop();
		}
	return 0;
	}
TInt CheckLog(CCheckingLogger& a)
	{
	if (a.Passed())
		return 0;
	test.Printf(_L("CCommandStack... : log failed"));
	return 1;
	}
TInt CheckTop(CCommandStack& aStack, CCommand* aTop)
	{
	if (aStack.Top() != aTop)
		{
		test.Printf(_L("CCommandStack : unexpected item at top of stack"));
		return 1;
		}
	return 0;
	}
TInt CheckCount(CCommandStack& aStack, TInt aExpectedCount)
	{
	if (aStack.Count() != aExpectedCount)
		{
		test.Printf(_L("CCommandStack : stack an unexpected size"));
		return 1;
		}
	return 0;
	}
TInt CheckPop(CCommandStack& aStack)
	{
	CCommand* check = aStack.Top();
	if (aStack.Pop() != check)
		{
		test.Printf(_L("CCommandStack : Pop() does not match Top()"));
		return 1;
		}
	return 0;
	}
void AddStuffL(CCommandStack& aStack, TInt* aTarget, CLogger* aLog)
	{
	TInt startCount = aStack.Count();
	CheckTop(aStack, 0);
	aStack.PrepareToPushL(1);
	CheckCount(aStack, startCount);
	CheckTop(aStack, 0);
	CheckCount(aStack, startCount);
	CCommand* temp = CCommandIncProto::NewL(aTarget, aLog);
	aStack.Push(temp);
	CheckCount(aStack, startCount + 1);
	CheckTop(aStack, temp);
	aStack.PrepareToPushL(2);
	CheckCount(aStack, startCount + 1);
	CheckTop(aStack, temp);
	CheckCount(aStack, startCount + 1);
	CheckTop(aStack, temp);
	aStack.PrepareToPushL(1);
	aStack.PrepareToPushL(3);
	CheckCount(aStack, startCount + 1);
	CheckTop(aStack, temp);
	temp = CCommandDecProto::NewL(aTarget, aLog);
	CheckCount(aStack, startCount + 1);
	aStack.Push(temp);
	CheckCount(aStack, startCount + 2);
	CheckTop(aStack, temp);
	CheckTop(aStack, temp);
	CheckCount(aStack, startCount + 2);
	CheckTop(aStack, temp);
	temp = CCommandIncProto::NewL(aTarget, aLog);
	aStack.Push(temp);
	CheckTop(aStack, temp);
	CheckCount(aStack, startCount + 3);
	aStack.PrepareToPushL(1);
	CheckTop(aStack, temp);
	aStack.PrepareToPushL(2);
	CheckTop(aStack, temp);
	temp = CCommandNegProto::NewL(aTarget, aLog);
	CheckCount(aStack, startCount + 3);
	aStack.Push(temp);
	CheckCount(aStack, startCount + 4);
	CheckTop(aStack, temp);
	CheckCount(aStack, startCount + 4);
	CheckTop(aStack, temp);
	temp = CCommandIncProto::NewL(aTarget, aLog);
	CheckCount(aStack, startCount + 4);
	aStack.Push(temp);
	CheckTop(aStack, temp);
	CheckCount(aStack, startCount + 5);
	}
void TestCCommandStackL()
	{
	__UHEAP_MARK;
	TInt target;
	CCheckingLogger* log = new(ELeave) CCheckingLogger;

	CCommandStack* stack = CCommandStack::NewL();

	AddStuffL(*stack, &target, log);

	log->SetCheckString(_L("inc<>neg<>inc<>dec<>inc<>"));
	ExecuteStackL(*stack);
	CheckLog(*log);

	CheckCount(*stack, 0);
	CCommand* temp = CCommandIncProto::NewL(&target, log);
	CheckTop(*stack, 0);
	stack->PrepareToPushL(1);
	CheckCount(*stack, 0);
	CheckTop(*stack, 0);
	CheckCount(*stack, 0);
	stack->Push(temp);
	CheckCount(*stack, 1);
	stack->PrepareToPushL(1);
	CheckCount(*stack, 1);
	CCommand* next = CCommandDecProto::NewL(&target, log);
	stack->Push(next);
	CheckCount(*stack, 2);
	stack->PrepareToPushL(1);
	CheckPop(*stack);
	stack->PrepareToPushL(1);
	CheckCount(*stack, 1);
	CheckTop(*stack, temp);
	CheckCount(*stack, 1);
	stack->Push(next);
	stack->PrepareToPushL(1);
	CheckCount(*stack, 2);
	CheckCount(*stack, 2);
	CheckPop(*stack);
	CheckCount(*stack, 1);
	CheckTop(*stack, temp);
	delete next;

	stack->Reset();
	CheckCount(*stack, 0);

	AddStuffL(*stack, &target, log);
	stack->PruneTo(3);
	CheckCount(*stack, 3);
	log->SetCheckString(_L("inc<>neg<>inc<>"));
	ExecuteStackL(*stack);
	CheckLog(*log);

	AddStuffL(*stack, &target, log);
	stack->PrepareToPushL(1);
	CheckCount(*stack, 5);
	stack->PruneTo(2);
	CheckCount(*stack, 2);
	log->SetCheckString(_L("inc<>neg<>"));
	ExecuteStackL(*stack);
	CheckLog(*log);

	delete stack;
	delete log;

	__UHEAP_MARKENDC(0);
	}

// 2 - CBatchCommand test
void ExecuteBatchL(CBatchCommand& a)
	{
	while (a.Top())
		{
		CSingleCommand* single = a.Top();
		single->ExecuteL();
		if (a.Pop() != single)
			{
			test.Printf(_L("CBatchCommand : Pop() didn't match Top()"));
			test(0);
			}
		delete single;
		}
	test(1);
	}
void CheckTop(CBatchCommand& aBatch, CCommand* aTop)
	{
	if (aBatch.Top() != aTop)
		{
		test.Printf(_L("CCommandBatch : unexpected item at top of stack"));
		test(0);
		}
	test(1);
	}
void TestCBatchCommandL()
	{
	__UHEAP_MARK;
	TInt target = 0;
	CCheckingLogger* log = new(ELeave) CCheckingLogger;

	CBatchCommand* batch = CBatchCommand::NewL();

	CBatchCommand* b1 = CBatchCommand::NewL();
	CBatchCommand* b2 = CBatchCommand::NewL();
	CBatchCommand* b3 = CBatchCommand::NewL();

	CCommand* s1 = CCommandIncProto::NewL(&target, log);
	CCommand* s2 = CCommandDecProto::NewL(&target, log);
	CCommand* s3 = CCommandNegProto::NewL(&target, log);
	CCommand* s4 = CCommandIncProto::NewL(&target, log);
	CCommand* s5 = CCommandDecProto::NewL(&target, log);
	CCommand* s6 = CCommandNegProto::NewL(&target, log);
	CCommand* s7 = CCommandIncProto::NewL(&target, log);
	CCommand* s8 = CCommandDecProto::NewL(&target, log);
	CCommand* s9 = CCommandNegProto::NewL(&target, log);

	b2->PrepareToPushL(s4);
	b2->Push(s4);
	b2->PrepareToPushL(s8);
	b2->Push(s8);
	b2->PrepareToPushL(s2);
	b2->PrepareToPushL(s2);
	b2->Push(s2);

	b3->PrepareToPushL(s3);
	b3->PrepareToPushL(s9);
	b3->Push(s9);
	b3->PrepareToPushL(s3);
	b3->Push(s3);
	b3->PrepareToPushL(s7);
	b3->Push(s7);

	b1->PrepareToPushL(s6);
	b1->Push(s6);
	b1->PrepareToPushL(s5);
	b1->Push(s5);
	b1->PrepareToPushL(b3);
	b1->Push(b3);
	b1->PrepareToPushL(b1);
	b1->PrepareToPushL(s1);
	b1->PrepareToPushL(s1);
	b1->PrepareToPushL(b2);

	batch->PrepareToPushL(b2);
	batch->Push(b2);
	batch->PrepareToPushL(s1);
	batch->PrepareToPushL(s1);
	batch->PrepareToPushL(b1);
	batch->Push(b1);
	batch->PrepareToPushL(s1);
	batch->Push(s1);

	CheckTop(*batch, s1);
	batch->Pop();
	CheckTop(*batch, s7);
	batch->PrepareToPushL(s1);
	CheckTop(*batch, s7);
	batch->Push(s1);
	CheckTop(*batch, s1);
	batch->PrepareToPushL(s1);
	CheckTop(*batch, s1);
	batch->Pop();
	CheckTop(*batch, s7);
	batch->Pop();
	CheckTop(*batch, s3);
	batch->Pop();
	CheckTop(*batch, s9);
	batch->Pop();
	CheckTop(*batch, s5);
	batch->Pop();
	CheckTop(*batch, s6);
	batch->PrepareToPushL(s5);
	CheckTop(*batch, s6);
	batch->Push(s5);
	CheckTop(*batch, s5);
	b3 = CBatchCommand::NewL();
	b3->PrepareToPushL(s9);
	CheckTop(*b3, 0);
	b3->Push(s9);
	CheckTop(*b3, s9);
	b3->PrepareToPushL(s3);
	CheckTop(*b3, s9);
	b3->Push(s3);
	CheckTop(*b3, s3);
	b3->PrepareToPushL(s7);
	CheckTop(*b3, s3);
	b3->Push(s7);
	CheckTop(*b3, s7);
	batch->PrepareToPushL(b3);
	CheckTop(*batch, s5);
	batch->Push(b3);
	CheckTop(*batch, s7);
	batch->PrepareToPushL(s1);
	batch->Push(s1);

	log->SetCheckString(_L("inc<>inc<>neg<>neg<>dec<>neg<>dec<>dec<>inc<>"));
	ExecuteBatchL(*batch);
	CheckLog(*log);

	delete log;
	delete batch;

	__UHEAP_MARKENDC(0);
	}

// 3 - CCommandHistory test
void ExecuteHistoryL(CCommandHistory& aHistory, CLogger& aLog)
	{
	while (aHistory.Top())
		{
		if (aHistory.Top()->Single())
			{
			aHistory.Top()->Single()->ExecuteL();
			}
		else
			{
			CBatchCommand* batch = aHistory.Top()->Batch();
			test(batch != 0);
			aLog << _L("batch{");
			ExecuteBatchL(*batch);
			aLog << _L("}");
			}
		delete aHistory.Pop();
		}
	}
void TestCCommandHistoryL()
	{
	__UHEAP_MARK;

	CCommandHistory* history = CCommandHistory::NewL();
	CCheckingLogger* log = new(ELeave) CCheckingLogger;
	TInt target;

	CCommand* p;
	history->SetMaxItems(5);
	p = CCommandDecProto::NewL(&target, log);
	history->PrepareToAddCommandL(p);
	history->AddCommand(p);
	p = CCommandIncProto::NewL(&target, log);
	history->PrepareToAddCommandL(p);
	history->AddCommand(p);
	p = CCommandDecProto::NewL(&target, log);
	history->PrepareToAddCommandL(p);
	history->AddCommand(p);
	p = CCommandNegProto::NewL(&target, log);
	history->PrepareToAddCommandL(p);
	history->AddCommand(p);
	history->BeginBatchLC();
	p = CCommandIncProto::NewL(&target, log);
	history->PrepareToAddCommandL(p);
	history->AddCommand(p);
	p = CCommandDecProto::NewL(&target, log);
	history->PrepareToAddCommandL(p);
	history->AddCommand(p);
	p = CCommandNegProto::NewL(&target, log);
	history->PrepareToAddCommandL(p);
	history->AddCommand(p);
	CleanupStack::PopAndDestroy();
	p = CCommandDecProto::NewL(&target, log);
	history->PrepareToAddCommandL(p);
	history->AddCommand(p);
	CBatchCommand* batch = CBatchCommand::NewL();
	p = CCommandDecProto::NewL(&target, log);
	batch->PrepareToPushL(p);
	batch->Push(p);
	p = CCommandNegProto::NewL(&target, log);
	batch->PrepareToPushL(p);
	batch->Push(p);
	p = CCommandIncProto::NewL(&target, log);
	batch->PrepareToPushL(p);
	batch->Push(p);
	history->PrepareToAddCommandL(batch);
	history->AddCommand(batch);
	p = CCommandNegProto::NewL(&target, log);
	history->PrepareToAddCommandL(p);
	history->AddCommand(p);

	log->SetCheckString(_L("neg<>batch{inc<>neg<>dec<>}dec<>batch{neg<>dec<>inc<>}neg<>"));
	ExecuteHistoryL(*history, *log);
	CheckLog(*log);

	delete log;
	delete history;

	__UHEAP_MARKENDC(0);
	}

// 4 - CCommandManager test
void TestCanUndo(const CCommandManager& aMan)
	{
	if (aMan.CanUndo())
		{
		test(1);
		return;
		}
	test.Printf(_L("CCommandManager : unexpectedly could not undo"));
	test(0);
	}
void TestCanRedo(const CCommandManager& aMan)
	{
	if (aMan.CanRedo())
		{
		test(1);
		return;
		}
	test.Printf(_L("CCommandManager : unexpectedly could not redo"));
	test(0);
	}
void TestCannotUndo(const CCommandManager& aMan)
	{
	if (!aMan.CanUndo())
		{
		test(1);
		return;
		}
	test.Printf(_L("CCommandManager : unexpectedly could undo"));
	test(0);
	}
void TestCannotRedo(const CCommandManager& aMan)
	{
	if (!aMan.CanRedo())
		{
		test(1);
		return;
		}
	test.Printf(_L("CCommandManager : unexpectedly could undo"));
	test(0);
	}
void SetUpTestL(CCommandManager& aMan, CSingleCommand& aCommand, TInt* aTarget, CLogger* aLogger)
	{
	CCommandIncProto* inc = CCommandIncProto::NewL(aTarget, aLogger);
	CleanupStack::PushL(inc);
	CCommandNegProto* neg = CCommandNegProto::NewL(aTarget, aLogger);
	CleanupStack::PushL(neg);
	CCommandDecProto* dec = CCommandDecProto::NewL(aTarget, aLogger);
	CleanupStack::PushL(dec);

	aMan.ExecuteL(*inc);
	aMan.BeginBatchLC();
	aMan.ExecuteL(*neg);
	aMan.ExecuteL(aCommand);
	aMan.ExecuteL(*dec);
	CleanupStack::PopAndDestroy();		// close batch
	aMan.ExecuteL(*neg);
	aMan.UndoL();

	CleanupStack::PopAndDestroy(dec);
	CleanupStack::PopAndDestroy(neg);
	CleanupStack::PopAndDestroy(inc);
	}
TInt CheckErrorCode(TInt aErr, TInt aExpected)
	{
	if (aErr == aExpected)
		return 0;
	if (aErr == KErrNone)
		test.Printf(_L("CCommandManager : no leave where one was expected"));
	else
		test.Printf(_L("CCommandManager : unexpected leave code"));
	return 1;
	}
void TestCCommandManagerL()
	{
	__UHEAP_MARK;

	CCommandManager* manager = CCommandManager::NewL();
	CCheckingLogger* log = new(ELeave) CCheckingLogger;
	TInt target = 0;
	CRefuserGatekeeper* refuser = new(ELeave) CRefuserGatekeeper;
	CPermitterGatekeeper* permitter = new(ELeave) CPermitterGatekeeper;
	CMemoryReclaimGatekeeper* reclaimer = new(ELeave) CMemoryReclaimGatekeeper;

	TestCannotUndo(*manager);
	TestCannotRedo(*manager);

	CCommandIncProto* inc = CCommandIncProto::NewL(&target, log);
	CCommandDecProto* dec = CCommandDecProto::NewL(&target, log);
	CCommandNegProto* neg = CCommandNegProto::NewL(&target, log);

	log->SetCheckString(_L("inc<>neg<>inc<>dec<>neg<>negate<1>"));
	SetUpTestL(*manager, *inc, &target, log);
	CheckLog(*log);
	TestCanUndo(*manager);
	TestCanRedo(*manager);
	log->SetCheckString(_L("offset<0>negate<1>"));
	manager->UndoL();
	CheckLog(*log);
	TestCanUndo(*manager);
	TestCanRedo(*manager);
	log->SetCheckString(_L("offset<-1>"));
	manager->UndoL();
	CheckLog(*log);
	TestCannotUndo(*manager);
	TestCanRedo(*manager);
	log->SetCheckString(_L("offset<1>"));
	manager->RedoL();
	CheckLog(*log);
	TestCanUndo(*manager);
	TestCanRedo(*manager);
	log->SetCheckString(_L("negate<1>offset<0>"));
	manager->RedoL();
	CheckLog(*log);
	TestCanUndo(*manager);
	TestCanRedo(*manager);
	log->SetCheckString(_L("negate<1>"));
	manager->RedoL();
	CheckLog(*log);
	TestCanUndo(*manager);
	TestCannotRedo(*manager);
	log->SetCheckString(_L("negate<1>"));
	manager->UndoL();
	CheckLog(*log);
	TestCanUndo(*manager);
	TestCanRedo(*manager);
	log->SetCheckString(_L("inc<>"));
	manager->ExecuteL(*inc);
	CheckLog(*log);
	TestCanUndo(*manager);
	TestCannotRedo(*manager);
	log->SetCheckString(_L("offset<-1>negate<1>"));
	manager->UndoL();
	CheckLog(*log);
	TestCanUndo(*manager);
	TestCanRedo(*manager);
	log->SetCheckString(_L("negate<1>offset<1>"));
	manager->RedoL();
	CheckLog(*log);
	TestCanUndo(*manager);
	TestCannotRedo(*manager);

	manager->ResetUndo();
	TestCannotUndo(*manager);
	TestCannotRedo(*manager);

	// test coalescence

	log->SetCheckString(_L("inc<>inc<>inc<>inc<>inc<>inc<>"));

	manager->ExecuteL(*inc);
	manager->ExecuteL(*inc);
	manager->BeginBatchLC();
	manager->ExecuteL(*inc);
	manager->ExecuteL(*inc);
	CleanupStack::PopAndDestroy();		// close batch
	manager->ExecuteL(*inc);
	manager->ExecuteL(*inc);
	CheckLog(*log);

	log->SetCheckString(_L("offset<-4>offset<-2>"));
	manager->UndoL();
	manager->UndoL();
	CheckLog(*log);

	log->SetCheckString(_L("offset<2>offset<4>"));
	manager->RedoL();
	manager->RedoL();
	TestCannotRedo(*manager);
	CheckLog(*log);

	manager->ResetUndo();
	TestCannotUndo(*manager);
	TestCannotRedo(*manager);

	// test command with batch inverse
	log->SetCheckString(_L("inc<>decneg<>inc<>"));
	CCommandDecThenNegProto* dnp = CCommandDecThenNegProto::NewL(&target, log);
	manager->ExecuteL(*inc);
	manager->ExecuteL(*dnp);
	manager->ExecuteL(*inc);
	CheckLog(*log);
	delete dnp;
	log->SetCheckString(_L("offset<-1>"));
	manager->UndoL();
	CheckLog(*log);
	log->SetCheckString(_L("negate<1>offset<1>"));
	manager->UndoL();
	CheckLog(*log);
	log->SetCheckString(_L("offset<-1>"));
	manager->UndoL();
	CheckLog(*log);
	manager->ResetUndo();

	// Test case when undo is not supported
	// 1. execution is permitted
	log->SetCheckString(_L("inc<>neg<>noinvfail.noinv<>dec<>neg<>negate<1>"));
	CCommandCannotInvert* noInv = CCommandCannotInvert::NewL(log);
	SetUpTestL(*manager, *noInv, &target, log);
	CheckLog(*log);
	TestCannotUndo(*manager);
	log->SetCheckString(_L("negate<1>"));
	manager->RedoL();
	CheckLog(*log);
	TestCannotRedo(*manager);
	manager->ResetUndo();

	//2. execution is supressed
	manager->SetGatekeeper(refuser);
	log->SetCheckString(_L("inc<>neg<>noinvfail.dec<>neg<>negate<1>"));
	SetUpTestL(*manager, *noInv, &target, log);
	CheckLog(*log);
	delete noInv;
	log->SetCheckString(_L("offset<1>negate<1>"));
	manager->UndoL();
	CheckLog(*log);
	log->SetCheckString(_L("offset<-1>"));
	manager->UndoL();
	CheckLog(*log);
	TestCannotUndo(*manager);
	manager->ResetUndo();
	manager->SetGatekeeper(0);

	// Test case when execution fails (with returned error code)
	CCommandCannotDo* noDo = CCommandCannotDo::NewL(log);
	log->SetCheckString(_L("inc<>neg<>nodo<>dec<>neg<>negate<1>"));
	SetUpTestL(*manager, *noDo, &target, log);
	delete noDo;
	log->SetCheckString(_L("offset<1>negate<1>"));
	manager->UndoL();
	CheckLog(*log);
	log->SetCheckString(_L("offset<-1>"));
	manager->UndoL();
	CheckLog(*log);
	TestCannotUndo(*manager);
	manager->ResetUndo();

	// Test case when inversion fails (not inversion is reported as impossible)
	// 1. when execution is permitted
	manager->SetGatekeeper(permitter);
	log->SetCheckString(_L("inc<>neg<>noinvfail.leaveinv<>dec<>neg<>negate<1>"));
	CCommandLeavesInvert* leaveInv = CCommandLeavesInvert::NewL(log);
	SetUpTestL(*manager, *leaveInv, &target, log);
	CheckLog(*log);
	TestCannotUndo(*manager);
	log->SetCheckString(_L("negate<1>"));
	manager->RedoL();
	CheckLog(*log);
	TestCannotRedo(*manager);
	manager->ResetUndo();

	// 2. when execution is supressed
	manager->SetGatekeeper(refuser);
	log->SetCheckString(_L("inc<>neg<>noinvfail.dec<>neg<>negate<1>"));
	leaveInv->iFail = ETrue;
	TRAPD(err, SetUpTestL(*manager, *leaveInv, &target, log));
	CheckErrorCode(err, KErrNone);
	CheckLog(*log);
	log->SetCheckString(_L("offset<1>negate<1>"));
	manager->UndoL();
	CheckLog(*log);
	log->SetCheckString(_L("offset<-1>"));
	manager->UndoL();
	CheckLog(*log);
	TestCannotUndo(*manager);
	manager->ResetUndo();

	// 3. when execution is terminated by leaving
	manager->SetGatekeeper(0);
	log->SetCheckString(_L("inc<>neg<>noinvfail."));
	leaveInv->iFail = ETrue;
	TRAP(err, SetUpTestL(*manager, *leaveInv, &target, log));
	CheckErrorCode(err, KErrNotFound);
	CheckLog(*log);
	delete leaveInv;
	log->SetCheckString(_L("negate<1>"));
	manager->UndoL();
	CheckLog(*log);
	log->SetCheckString(_L("offset<-1>"));
	manager->UndoL();
	CheckLog(*log);
	TestCannotUndo(*manager);
	manager->ResetUndo();

	// Test case when inversion runs out of memory
	// 1. when execution is permitted with no undo
	manager->SetGatekeeper(permitter);
	log->SetCheckString(_L("inc<>neg<>nomemfailadd.nomem<>dec<>neg<>negate<1>"));
	CCommandNoMemory* noMem = CCommandNoMemory::NewL(log);
	noMem->iFailExecute = EFalse;
	SetUpTestL(*manager, *noMem, &target, log);
	CheckLog(*log);
	TestCannotUndo(*manager);
	log->SetCheckString(_L("negate<1>"));
	manager->RedoL();
	CheckLog(*log);
	TestCannotRedo(*manager);
	manager->ResetUndo();

	// 2. when execution is supressed
	manager->SetGatekeeper(refuser);
	log->SetCheckString(_L("inc<>neg<>nomemfailadd.dec<>neg<>negate<1>"));
	TRAP(err, SetUpTestL(*manager, *noMem, &target, log));
	CheckErrorCode(err, KErrNone);
	CheckLog(*log);
	log->SetCheckString(_L("offset<1>negate<1>"));
	manager->UndoL();
	CheckLog(*log);
	log->SetCheckString(_L("offset<-1>"));
	manager->UndoL();
	CheckLog(*log);
	TestCannotUndo(*manager);
	manager->ResetUndo();
	manager->SetGatekeeper(0);

	// 3. when memory is reclaimed
	reclaimer->iTarget = noMem;
	manager->SetGatekeeper(reclaimer);
	log->SetCheckString(_L("inc<>neg<>nomemfailadd.nomemfailinv.nomem<>dec<>neg<>negate<1>"));
	TRAP(err, SetUpTestL(*manager, *noMem, &target, log));
	CheckErrorCode(err, KErrNone);
	CheckLog(*log);
	log->SetCheckString(_L("offset<1>negate<1>"));
	manager->UndoL();
	CheckLog(*log);
	log->SetCheckString(_L("offset<-1>"));
	manager->UndoL();
	CheckLog(*log);
	TestCannotUndo(*manager);
	manager->ResetUndo();
	manager->SetGatekeeper(0);

	// Test when execution runs out of memory
	// 1. with no reclaimation
	noMem->iFailAddToLast	= EFalse;
	noMem->iFailInvert		= EFalse;
	noMem->iFailExecute		= ETrue;
	noMem->iLogExecuteFailed= ETrue;
	log->SetCheckString(_L("inc<>neg<>nomemfailexe."));
	TRAP(err, SetUpTestL(*manager, *noMem, &target, log));
	CheckErrorCode(err, KErrNoMemory);
	CheckLog(*log);
	TestCannotRedo(*manager);
	log->SetCheckString(_L("negate<1>"));
	manager->UndoL();
	CheckLog(*log);
	log->SetCheckString(_L("offset<-1>"));
	manager->UndoL();
	CheckLog(*log);
	TestCannotUndo(*manager);
	manager->ResetUndo();
	// 2. with reclaimation
	noMem->iFailAddToLast	= EFalse;
	noMem->iFailInvert		= EFalse;
	noMem->iFailExecute		= ETrue;
	noMem->iLogExecuteFailed= ETrue;
	reclaimer->iTarget = noMem;
	manager->SetGatekeeper(reclaimer);
	log->SetCheckString(_L("inc<>neg<>nomemfailexe.nomem<>dec<>neg<>negate<1>"));
	TRAP(err, SetUpTestL(*manager, *noMem, &target, log));
	CheckErrorCode(err, KErrNone);
	CheckLog(*log);
	log->SetCheckString(_L("offset<1>negate<1>"));
	manager->UndoL();
	CheckLog(*log);
	log->SetCheckString(_L("offset<-1>"));
	manager->UndoL();
	CheckLog(*log);
	TestCannotUndo(*manager);
	manager->ResetUndo();
	manager->SetGatekeeper(0);
	delete noMem;

	delete inc;
	delete dec;
	delete neg;
	delete reclaimer;
	delete refuser;
	delete permitter;
	delete log;
	manager->Release();

	__UHEAP_MARKENDC(0);
	}

//
//
//	Tests involving CTestEditor
//
//

void CheckEditorLog(CCheckingLogger& a)
	{
	if (a.Passed())
		{
		test(1);
		return;
		}
	test.Printf(_L("EditorUndo : log failed"));
	test(0);
	}

void TestPlainText(CTestEditor& aTestEditor, MUnifiedEditor& aUndoEditor,
				   CCommandManager& aCommandManager)
	{
	CCheckingLogger* check = new(ELeave) CCheckingLogger;
	CStoringLogger* log = new(ELeave) CStoringLogger;

	//
	// general inserting and deleting text
	//
	aUndoEditor.InsertTextL(0, _L("Hello world!"), 0, 0, 0);

	aCommandManager.ResetUndo();

	aTestEditor.Print(*log);
	HBufC* helloWorldLog = log->GetStore();

	check->SetCheckString(*helloWorldLog);
	aTestEditor.Print(*check);
	CheckEditorLog(*check);
	aUndoEditor.InsertTextL(5, _L(" lovely"), 0, 0, 0);
	aTestEditor.Print(*log);
	HBufC* helloLovelyWorldLog = log->GetStore();
	aUndoEditor.DeleteTextL(10, 8);
	aTestEditor.Print(*log);
	HBufC* helloLoveLog = log->GetStore();
	aCommandManager.UndoL();
	check->SetCheckString(*helloLovelyWorldLog);
	aTestEditor.Print(*check);
	CheckEditorLog(*check);
	aCommandManager.UndoL();
	check->SetCheckString(*helloWorldLog);
	aTestEditor.Print(*check);
	CheckEditorLog(*check);
	aCommandManager.RedoL();
	check->SetCheckString(*helloLovelyWorldLog);
	aTestEditor.Print(*check);
	CheckEditorLog(*check);
	aCommandManager.RedoL();
	check->SetCheckString(*helloLoveLog);
	aTestEditor.Print(*check);
	CheckEditorLog(*check);
	aCommandManager.UndoL();
	check->SetCheckString(*helloLovelyWorldLog);
	aTestEditor.Print(*check);
	CheckEditorLog(*check);
	aCommandManager.UndoL();
	check->SetCheckString(*helloWorldLog);
	aTestEditor.Print(*check);
	CheckEditorLog(*check);

	aUndoEditor.InsertTextL(6, _L("w"), 0, 0, 0);
	aUndoEditor.InsertTextL(7, _L("h"), 0, 0, 0);
	aUndoEditor.InsertTextL(8, _L("at's"), 0, 0, 0);
	aUndoEditor.InsertTextL(12, _L(" "), 0, 0, 0);
	aUndoEditor.InsertTextL(13, _L("w"), 0, 0, 0);
	aUndoEditor.InsertTextL(14, _L("i"), 0, 0, 0);
	aUndoEditor.InsertTextL(6, _L("there "), 0, 0, 0);
	aUndoEditor.InsertTextL(21, _L("t"), 0, 0, 0);
	aUndoEditor.InsertTextL(22, _L("h"), 0, 0, 0);
	aUndoEditor.InsertTextL(23, _L(" "), 0, 0, 0);
	aTestEditor.Print(*log);
	HBufC* textLog0 = log->GetStore();
	aUndoEditor.InsertTextL(24, _L("the"), 0, 0, 0);	// first of next
	aUndoEditor.InsertTextL(27, _L(" "), 0, 0, 0);
	aUndoEditor.InsertTextL(28, _L("d "), 0, 0, 0);
	aUndoEditor.InsertTextL(28, _L("ol"), 0, 0, 0);
	aTestEditor.Print(*log);
	HBufC* textLog1 = log->GetStore();

	aCommandManager.UndoL();
	check->SetCheckString(*textLog0);
	aTestEditor.Print(*check);
	CheckEditorLog(*check);

	aCommandManager.UndoL();
	check->SetCheckString(*helloWorldLog);
	aTestEditor.Print(*check);
	CheckEditorLog(*check);

	aCommandManager.RedoL();
	check->SetCheckString(*textLog0);
	aTestEditor.Print(*check);
	CheckEditorLog(*check);

	aCommandManager.RedoL();
	check->SetCheckString(*textLog1);
	aTestEditor.Print(*check);
	CheckEditorLog(*check);

	// check coalescence of insertions
	aTestEditor.AlterGranularityL(5);
	aUndoEditor.DeleteTextL(22, 1);
	aUndoEditor.DeleteTextL(21, 1);
	aUndoEditor.DeleteTextL(20, 1);
	aUndoEditor.DeleteTextL(19, 1);
	aUndoEditor.DeleteTextL(18, 1);
	aUndoEditor.DeleteTextL(18, 1);
	aUndoEditor.DeleteTextL(15, 3);		// this will coalesce
	aUndoEditor.DeleteTextL(6, 9);		// this won't, as it does not fit in one command
	aTestEditor.Print(*log);
	HBufC* delLog0 = log->GetStore();
	aUndoEditor.DeleteTextL(4, 1);
	aTestEditor.Print(*log);
	HBufC* delLog1 = log->GetStore();
	aUndoEditor.DeleteTextL(8, 2);
	aUndoEditor.DeleteTextL(8, 1);	// should coalesce
	aUndoEditor.DeleteTextL(8, 1);	// should coalesce
	aTestEditor.Print(*log);
	HBufC* delLog3 = log->GetStore();

	aCommandManager.UndoL();
	check->SetCheckString(*delLog1);
	aTestEditor.Print(*check);
	CheckEditorLog(*check);

	aCommandManager.UndoL();
	check->SetCheckString(*delLog0);
	aTestEditor.Print(*check);
	CheckEditorLog(*check);

	aCommandManager.UndoL();
	aCommandManager.UndoL();
	check->SetCheckString(*textLog1);
	aTestEditor.Print(*check);
	CheckEditorLog(*check);

	aCommandManager.RedoL();
	aCommandManager.RedoL();
	check->SetCheckString(*delLog0);
	aTestEditor.Print(*check);
	CheckEditorLog(*check);

	aCommandManager.RedoL();
	check->SetCheckString(*delLog1);
	aTestEditor.Print(*check);
	CheckEditorLog(*check);

	aCommandManager.RedoL();
	check->SetCheckString(*delLog3);
	aTestEditor.Print(*check);
	CheckEditorLog(*check);

	aCommandManager.UndoL();
	check->SetCheckString(*delLog1);
	aTestEditor.Print(*check);
	CheckEditorLog(*check);

	aCommandManager.UndoL();
	check->SetCheckString(*delLog0);
	aTestEditor.Print(*check);
	CheckEditorLog(*check);

	aCommandManager.UndoL();
	aCommandManager.UndoL();
	check->SetCheckString(*textLog1);
	aTestEditor.Print(*check);
	CheckEditorLog(*check);

	aCommandManager.UndoL();
	aCommandManager.UndoL();
	check->SetCheckString(*helloWorldLog);
	aTestEditor.Print(*check);
	CheckEditorLog(*check);

	aCommandManager.ResetUndo();
	delete delLog0;
	delete delLog1;
	delete delLog3;

	// Check adding large amounts of text
	aTestEditor.AlterGranularityL(32);
	aUndoEditor.InsertTextL(0, _L("123456789"), 0, 0, 0);
	aUndoEditor.InsertTextL(0, _L("223456789"), 0, 0, 0);
	aTestEditor.Print(*log);
	HBufC* largeLog0 = log->GetStore();
	aUndoEditor.InsertTextL(0, _L("3234567890"), 0, 0, 0);
	aUndoEditor.InsertTextL(0, _L("4234567890"), 0, 0, 0);
	aTestEditor.Print(*log);
	HBufC* largeLog1 = log->GetStore();
	aUndoEditor.InsertTextL(0, _L("523456789"), 0, 0, 0);
	aTestEditor.Print(*log);
	HBufC* largeLog2 = log->GetStore();

	aCommandManager.UndoL();
	check->SetCheckString(*largeLog1);
	aTestEditor.Print(*check);
	CheckEditorLog(*check);

	aCommandManager.UndoL();
	check->SetCheckString(*largeLog0);
	aTestEditor.Print(*check);
	CheckEditorLog(*check);

	aCommandManager.UndoL();
	check->SetCheckString(*helloWorldLog);
	aTestEditor.Print(*check);
	CheckEditorLog(*check);

	aCommandManager.RedoL();
	check->SetCheckString(*largeLog0);
	aTestEditor.Print(*check);
	CheckEditorLog(*check);

	aCommandManager.RedoL();
	check->SetCheckString(*largeLog1);
	aTestEditor.Print(*check);
	CheckEditorLog(*check);

	aCommandManager.RedoL();
	check->SetCheckString(*largeLog2);
	aTestEditor.Print(*check);
	CheckEditorLog(*check);

	aCommandManager.UndoL();
	check->SetCheckString(*largeLog1);
	aTestEditor.Print(*check);
	CheckEditorLog(*check);

	aCommandManager.UndoL();
	check->SetCheckString(*largeLog0);
	aTestEditor.Print(*check);
	CheckEditorLog(*check);

	aCommandManager.UndoL();
	check->SetCheckString(*helloWorldLog);
 	aTestEditor.Print(*check);
	CheckEditorLog(*check);

	aCommandManager.RedoL();
	aCommandManager.RedoL();
	aCommandManager.RedoL();

	// test copy and paste
	MUnifiedEditor::MClipboardSupport* ci = aUndoEditor.ClipboardSupport();
	ASSERT(ci);

	CBufStore* clipboardBuffer = CBufStore::NewL(100);
	CStreamDictionary* clipboardDictionary = CStreamDictionary::NewL();

	ci->CopyToStoreL(*clipboardBuffer, *clipboardDictionary, 5, 40);
	aTestEditor.Print(*log);
	HBufC* clipLog0 = log->GetStore();
	ci->PasteFromStoreL(*clipboardBuffer, *clipboardDictionary, 2);
	aTestEditor.Print(*log);
	HBufC* clipLog1 = log->GetStore();
	ci->PasteFromStoreL(*clipboardBuffer, *clipboardDictionary, 55);
	aTestEditor.Print(*log);
	HBufC* clipLog2 = log->GetStore();
	ci->PasteFromStoreL(*clipboardBuffer, *clipboardDictionary, 23);
	aTestEditor.Print(*log);
	HBufC* clipLog3 = log->GetStore();

	aCommandManager.UndoL();
	check->SetCheckString(*clipLog2);
	aTestEditor.Print(*check);
	CheckEditorLog(*check);

	aCommandManager.UndoL();
	check->SetCheckString(*clipLog1);
	aTestEditor.Print(*check);
	CheckEditorLog(*check);

	aCommandManager.UndoL();
	check->SetCheckString(*clipLog0);
	aTestEditor.Print(*check);
	CheckEditorLog(*check);

	aCommandManager.RedoL();
	check->SetCheckString(*clipLog1);
	aTestEditor.Print(*check);
	CheckEditorLog(*check);

	aCommandManager.RedoL();
	check->SetCheckString(*clipLog2);
	aTestEditor.Print(*check);
	CheckEditorLog(*check);

	aCommandManager.RedoL();
	check->SetCheckString(*clipLog3);
	aTestEditor.Print(*check);
	CheckEditorLog(*check);

	aCommandManager.UndoL();
	check->SetCheckString(*clipLog2);
	aTestEditor.Print(*check);
	CheckEditorLog(*check);

	aCommandManager.UndoL();
	check->SetCheckString(*clipLog1);
	aTestEditor.Print(*check);
	CheckEditorLog(*check);

	aCommandManager.UndoL();
	check->SetCheckString(*clipLog0);
	aTestEditor.Print(*check);
	CheckEditorLog(*check);

	delete clipLog0;
	delete clipLog1;
	delete clipLog2;
	delete clipLog3;
	delete clipboardDictionary;
	delete clipboardBuffer;

	delete textLog0;
	delete textLog1;

	delete largeLog0;
	delete largeLog1;
	delete largeLog2;

	delete helloWorldLog;
	delete helloLovelyWorldLog;
	delete helloLoveLog;
	delete log;
	delete check;
	}

// This class merely splits the test function into little functions
// to help out the MW compiler
class TestEditorUndo
	{
	CCheckingLogger* check;
	CStoringLogger* log;
	CTestEditor* testEd;
	CCommandManager* manager;
	CEditorPlainTextWithUndo* plainEd;
	CEditorWithUndo* ed;
	TTmCharFormatMask charBMask;
	TTmCharFormatMask charIMask;
	TTmCharFormatMask charBIMask;
	TOpenFontFaceAttribBase attrib;
	TTmCharFormat charB;
	TTmCharFormat charIB;
	TTmCharFormat charI;
	TTmParFormatMask parTMask;
	TTmParFormatMask parNMask;
	TTmParFormatMask parTNMask;
	RTmParFormat par0;
	RTmParFormat parT;
	RTmParFormat parN;
	RTmParFormat parTN;
	TTmCharFormatLayer charLayer;
	RTmParFormatLayer parLayer;
	RTmStyle style1;
	RTmStyle style2;
	HBufC* charLog0;
	HBufC* charLog1;
	HBufC* charLog2;
	HBufC* charLog3;
	HBufC* textLog0;
	HBufC* textLog1;
	HBufC* textLog2;
	HBufC* textLog3;
	HBufC* parLog0;
	HBufC* parLog1;
	HBufC* parLog2;
	HBufC* parLog3;
	HBufC* delLog0;
	HBufC* delLog1;
	HBufC* delLog2;
	HBufC* delLog3;
	HBufC* styleLog1;
	HBufC* styleLog2;
	HBufC* styleLog3;
	HBufC* styleLog4;
	HBufC* styleLog5;
	HBufC* styleLog6;
	HBufC* styleLog7;
	HBufC* styleLog8;
	HBufC* styleLog9;
	HBufC* styleLog10;
	HBufC* styleLog11;
	HBufC* styleLog12;
	HBufC* styleLog13;
	HBufC* picLog0;
	HBufC* picLog1;
	HBufC* picLog2;
	HBufC* picLog3;
	HBufC* picLog4;
	HBufC* picLog5;
	HBufC* bookMarkLog0;
	HBufC* bookMarkLog1;
	HBufC* bookMarkLog2;
	HBufC* bookMarkLog3;
	HBufC* bookMarkLog4;
	HBufC* bookMarkLog5;
public:
	void Test1L();
	void Test2L();
	void Test3L();
	void Test4L();
	void Test5L();
	void Test6L();
	void Test7L();
	void Test8L();
	};

void TestEditorUndo::Test1L()
	{
	check = new(ELeave) CCheckingLogger;
	log = new(ELeave) CStoringLogger;
	testEd = CTestEditor::NewL();
	manager = CCommandManager::NewL();
	plainEd = CEditorPlainTextWithUndo::NewL(*testEd, manager);

	TestPlainText(*testEd, *plainEd, *manager);
	ed = CEditorWithUndo::NewL(*testEd, manager);
	testEd->DeleteTextL(0, testEd->DocumentLength());
	manager->ResetUndo();

	delete plainEd;
	plainEd = 0;

	TestPlainText(*testEd, *ed, *manager);
	manager->Release();
	}

void TestEditorUndo::Test2L()
	{
	// char and par formats
	charBMask.iFlags = TTmCharFormatMask::EBold;
	charIMask.iFlags = TTmCharFormatMask::EItalic;
	charBIMask.iFlags = TTmCharFormatMask::EItalic | TTmCharFormatMask::EBold;
	attrib.SetBold(ETrue);
	charB.iFontSpec.SetAttrib(attrib);
	attrib.SetItalic(ETrue);
	charIB.iFontSpec.SetAttrib(attrib);
	attrib.SetBold(EFalse);
	charI.iFontSpec.SetAttrib(attrib);

	parTMask.iFlags = TTmParFormatMask::EKeepTogether;
	parNMask.iFlags = TTmParFormatMask::EKeepWithNext;
	parTNMask.iFlags = TTmParFormatMask::EKeepTogether | TTmParFormatMask::EKeepWithNext;
	parT.iFlags = RTmParFormat::EKeepTogether;
	parN.iFlags = RTmParFormat::EKeepWithNext;
	parTN.iFlags = RTmParFormat::EKeepWithNext | RTmParFormat::EKeepTogether;

	charLayer.iFormat = charB;
	charLayer.iMask = charBMask;
	ed->SetCharFormatL(0, 5, charLayer);
	testEd->Print(*log);
	charLog0 = log->GetStore();

	charLayer.iFormat = charI;
	charLayer.iMask = charIMask;
	ed->SetCharFormatL(3, 9, charLayer);
	testEd->Print(*log);
	charLog1 = log->GetStore();

	charLayer.iFormat = charB;
	charLayer.iMask = charBIMask;
	ed->SetCharFormatL(2, 5, charLayer);
	testEd->Print(*log);
	charLog2 = log->GetStore();

	ed->DeleteCharFormatL(1, 10);
	testEd->Print(*log);
	charLog3 = log->GetStore();

	ed->UndoL();
	check->SetCheckString(*charLog2);
	testEd->Print(*check);
	CheckEditorLog(*check);

	ed->UndoL();
	check->SetCheckString(*charLog1);
	testEd->Print(*check);
	CheckEditorLog(*check);

	ed->UndoL();
	check->SetCheckString(*charLog0);
	testEd->Print(*check);
	CheckEditorLog(*check);

	ed->RedoL();
	check->SetCheckString(*charLog1);
	testEd->Print(*check);
	CheckEditorLog(*check);

	ed->RedoL();
	check->SetCheckString(*charLog2);
	testEd->Print(*check);
	CheckEditorLog(*check);

	ed->RedoL();
	check->SetCheckString(*charLog3);
	testEd->Print(*check);
	CheckEditorLog(*check);

	parLayer.iMask = parTMask;
	parLayer.iFormat.CopyL(parT);
	ed->SetParFormatL(5, 7, parLayer);
	testEd->Print(*log);
	parLog0 = log->GetStore();

	parLayer.iMask = parTNMask;
	parLayer.iFormat.CopyL(parN);
	ed->SetParFormatL(0, 7, parLayer);
	testEd->Print(*log);
	parLog1 = log->GetStore();

	ed->DeleteParFormatL(4, 4);
	testEd->Print(*log);
	parLog2 = log->GetStore();

	parLayer.iMask = parNMask;
	parLayer.iFormat.CopyL(parN);
	ed->SetParFormatL(3, 6, parLayer);
	testEd->Print(*log);
	parLog3 = log->GetStore();

	ed->UndoL();
	check->SetCheckString(*parLog2);
	testEd->Print(*check);
	CheckEditorLog(*check);

	ed->UndoL();
	check->SetCheckString(*parLog1);
	testEd->Print(*check);
	CheckEditorLog(*check);

	ed->UndoL();
	check->SetCheckString(*parLog0);
	testEd->Print(*check);
	CheckEditorLog(*check);

	ed->UndoL();
	check->SetCheckString(*charLog3);
	testEd->Print(*check);
	CheckEditorLog(*check);

	ed->RedoL();
	check->SetCheckString(*parLog0);
	testEd->Print(*check);
	CheckEditorLog(*check);

	ed->RedoL();
	check->SetCheckString(*parLog1);
	testEd->Print(*check);
	CheckEditorLog(*check);

	ed->RedoL();
	check->SetCheckString(*parLog2);
	testEd->Print(*check);
	CheckEditorLog(*check);

	ed->RedoL();
	check->SetCheckString(*parLog3);
	testEd->Print(*check);
	CheckEditorLog(*check);
	}

void TestEditorUndo::Test3L()
	{
	// check coalescence of deletions
	charLayer.iMask = charIMask;
	charLayer.iFormat = charI;
	parLayer.iMask = parNMask;
	parLayer.iFormat.CopyL(parN);
	ed->InsertTextL(6, _L("w"), 0, &charLayer, &parLayer);
	ed->InsertTextL(7, _L("h"), 0, &charLayer, &parLayer);
	ed->InsertTextL(8, _L("at's"), 0, &charLayer, &parLayer);
	ed->InsertTextL(12, _L(" "), 0, &charLayer, &parLayer);
	ed->InsertTextL(13, _L("w"), 0, &charLayer, &parLayer);
	ed->InsertTextL(14, _L("i"), 0, &charLayer, &parLayer);
	ed->InsertTextL(6, _L("there "), 0, &charLayer, &parLayer);
	ed->InsertTextL(21, _L("t"), 0, &charLayer, &parLayer);
	ed->InsertTextL(22, _L("h"), 0, &charLayer, &parLayer);
	ed->InsertTextL(23, _L(" "), 0, &charLayer, &parLayer);
	testEd->Print(*log);
	textLog0 = log->GetStore();
	ed->InsertTextL(24, _L("the"), 0, &charLayer, &parLayer);	// first of next?
	ed->InsertTextL(27, _L(" "), 0, &charLayer, &parLayer);
	testEd->Print(*log);
	textLog1 = log->GetStore();
	charLayer.iMask = charBIMask;
	ed->InsertTextL(28, _L("ol"), 0, &charLayer, &parLayer);
	testEd->Print(*log);
	textLog2 = log->GetStore();
	parLayer.iMask = parTNMask;
	ed->InsertTextL(30, _L("d "), 0, &charLayer, &parLayer);
	testEd->Print(*log);
	textLog3 = log->GetStore();

	ed->UndoL();
	check->SetCheckString(*textLog0);
	testEd->Print(*check);
	CheckEditorLog(*check);

	ed->UndoL();
	check->SetCheckString(*parLog3);
	testEd->Print(*check);
	CheckEditorLog(*check);

	ed->RedoL();
	check->SetCheckString(*textLog0);
	testEd->Print(*check);
	CheckEditorLog(*check);

	ed->RedoL();
	check->SetCheckString(*textLog3);
	testEd->Print(*check);
	CheckEditorLog(*check);
	ed->ResetUndo();
	}

void TestEditorUndo::Test4L()
	{
	// check coalescence of insertions
	testEd->AlterGranularityL(5);
	ed->DeleteTextL(22, 1);
	ed->DeleteTextL(21, 1);
	ed->DeleteTextL(20, 1);
	ed->DeleteTextL(19, 1);
	ed->DeleteTextL(18, 1);
	ed->DeleteTextL(18, 1);
	ed->DeleteTextL(15, 3);		// this will coalesce
	ed->DeleteTextL(6, 9);		// this won't, as it does not fit in one command
	testEd->Print(*log);
	delLog0 = log->GetStore();
	ed->DeleteTextL(4, 1);
	testEd->Print(*log);
	delLog1 = log->GetStore();
	ed->DeleteTextL(8, 2);
	ed->DeleteTextL(8, 1);	// should coalesce
	testEd->Print(*log);
	delLog2 = log->GetStore();
	ed->DeleteTextL(8, 1);	// should fail to coalesce
	testEd->Print(*log);
	delLog3 = log->GetStore();

	ed->UndoL();
	check->SetCheckString(*delLog2);
	testEd->Print(*check);
	CheckEditorLog(*check);

	ed->UndoL();
	check->SetCheckString(*delLog1);
	testEd->Print(*check);
	CheckEditorLog(*check);

	ed->UndoL();
	check->SetCheckString(*delLog0);
	testEd->Print(*check);
	CheckEditorLog(*check);

	ed->UndoL();
	ed->UndoL();
	check->SetCheckString(*textLog3);
	testEd->Print(*check);
	CheckEditorLog(*check);

	ed->RedoL();
	ed->RedoL();
	check->SetCheckString(*delLog0);
	testEd->Print(*check);
	CheckEditorLog(*check);

	ed->RedoL();
	check->SetCheckString(*delLog1);
	testEd->Print(*check);
	CheckEditorLog(*check);

	ed->RedoL();
	check->SetCheckString(*delLog2);
	testEd->Print(*check);
	CheckEditorLog(*check);

	ed->RedoL();
	check->SetCheckString(*delLog3);
	testEd->Print(*check);
	CheckEditorLog(*check);

	ed->UndoL();
	check->SetCheckString(*delLog2);
	testEd->Print(*check);
	CheckEditorLog(*check);

	ed->UndoL();
	check->SetCheckString(*delLog1);
	testEd->Print(*check);
	CheckEditorLog(*check);

	ed->UndoL();
	check->SetCheckString(*delLog0);
	testEd->Print(*check);
	CheckEditorLog(*check);

	ed->UndoL();
	ed->UndoL();
	check->SetCheckString(*textLog3);
	testEd->Print(*check);
	CheckEditorLog(*check);
	ed->ResetUndo();
	delete delLog0;
	delete delLog1;
	delete delLog2;
	delete delLog3;
	}

void TestEditorUndo::Test5L()
	{
	// Check adding large amounts of text
	testEd->AlterGranularityL(32);
	ed->InsertTextL(0, _L("123456789"), 0, 0, 0);
	ed->InsertTextL(0, _L("223456789"), 0, 0, 0);
	testEd->Print(*log);
	delete textLog0;
	textLog0 = log->GetStore();
	ed->InsertTextL(0, _L("3234567890"), 0, 0, 0);
	ed->InsertTextL(0, _L("4234567890"), 0, 0, 0);
	testEd->Print(*log);
	delete textLog1;
	textLog1 = log->GetStore();
	ed->InsertTextL(0, _L("523456789"), 0, 0, 0);
	testEd->Print(*log);
	delete textLog2;
	textLog2 = log->GetStore();

	ed->UndoL();
	check->SetCheckString(*textLog1);
	testEd->Print(*check);
	CheckEditorLog(*check);

	ed->UndoL();
	check->SetCheckString(*textLog0);
	testEd->Print(*check);
	CheckEditorLog(*check);

	ed->UndoL();
	check->SetCheckString(*textLog3);
	testEd->Print(*check);
	CheckEditorLog(*check);

	ed->RedoL();
	check->SetCheckString(*textLog0);
	testEd->Print(*check);
	CheckEditorLog(*check);

	ed->RedoL();
	check->SetCheckString(*textLog1);
	testEd->Print(*check);
	CheckEditorLog(*check);

	ed->RedoL();
	check->SetCheckString(*textLog2);
	testEd->Print(*check);
	CheckEditorLog(*check);

	ed->UndoL();
	check->SetCheckString(*textLog1);
	testEd->Print(*check);
	CheckEditorLog(*check);

	ed->UndoL();
	check->SetCheckString(*textLog0);
	testEd->Print(*check);
	CheckEditorLog(*check);

	ed->UndoL();
	check->SetCheckString(*textLog3);
	testEd->Print(*check);
	CheckEditorLog(*check);
	}

void TestEditorUndo::Test6L()
	{
	// test style manipulation
	style1.iName = _L("author");
	style2.iName = _L("title");
	style2.iNextStyleName = _L("author");
	style1.iCharFormat.iFormat = charI;
	style1.iCharFormat.iMask = charIMask;
	style1.iParFormat.iFormat.CopyL(parT);
	style1.iParFormat.iMask = parTNMask;
	style2.iCharFormat.iFormat = charB;
	style2.iCharFormat.iMask = charBIMask;
	style2.iParFormat.iFormat.CopyL(parN);
	style2.iParFormat.iMask = parNMask;

	ed->StyleSupport()->CreateStyleL(style1);
	testEd->Print(*log);
	styleLog1 = log->GetStore();
	TInt retval = ed->StyleSupport()->SetStyleL(1, 3, _L("author"));
	testEd->Print(*log);
	styleLog2 = log->GetStore();
	if (retval != KErrNone)
		{
		test.Printf(_L("EditorUndo : apply style failed"));
		test(0);
		}
	TPtrC testStyleName;
	TInt testStyleRunLength;
	ed->StyleSupport()->GetStyle(1, testStyleName, testStyleRunLength);
	if (testStyleRunLength != 3 || testStyleName != style1.iName)
		{
		test.Printf(_L("EditorUndo : apply style failed"));
		test(0);
		}
	ed->InsertTextL(5, _L(","), &style1.iName, 0, 0);
	testEd->Print(*log);
	styleLog3 = log->GetStore();
	ed->StyleSupport()->CreateStyleL(style2);
	testEd->Print(*log);
	styleLog4 = log->GetStore();
	ed->StyleSupport()->SetStyleL(2, 7, _L("title"));
	testEd->Print(*log);
	styleLog5 = log->GetStore();
	ed->StyleSupport()->SetStyleL(10, 4, _L("title"));
	testEd->Print(*log);
	styleLog6 = log->GetStore();
	ed->StyleSupport()->SetStyleL(8, 4, _L("author"));
	testEd->Print(*log);
	styleLog7 = log->GetStore();
	style1.iCharFormat.iFormat = charB;
	style1.iCharFormat.iMask = charBMask;
	ed->StyleSupport()->ChangeStyleL(style1);
	testEd->Print(*log);
	styleLog8 = log->GetStore();
 	ed->StyleSupport()->RenameStyleL(_L("author"), _L("version"));
	style1.iName = _L("version");
	testEd->Print(*log);
	styleLog9 = log->GetStore();
	retval = ed->StyleSupport()->SetStyleL(10, 1, _L("version"));
	testEd->Print(*log);
	styleLog10 = log->GetStore();
	if (retval != KErrNone)
		{
		test.Printf(_L("EditorUndo : rename style failed"));
		test(0);
		}
	ed->StyleSupport()->GetStyle(1, testStyleName, testStyleRunLength);
	if (testStyleRunLength != 1 || testStyleName != style1.iName)
		{
		test.Printf(_L("EditorUndo : rename or apply style failed"));
		test(0);
		}
	ed->StyleSupport()->RenameStyleL(_L("title"), _L("zip"));
	style2.iName = _L("zip");
	testEd->Print(*log);
	styleLog11 = log->GetStore();
	ed->StyleSupport()->SetStyleL(0, 6, _L("zip"));
	testEd->Print(*log);
	styleLog12 = log->GetStore();
	ed->StyleSupport()->DeleteStyleL(_L("zip"));
	testEd->Print(*log);
	styleLog13 = log->GetStore();
	ed->InsertTextL(0, _L("Well "), &style1.iName, 0, 0);

	ed->UndoL();
	check->SetCheckString(*styleLog13);
	testEd->Print(*check);
	CheckEditorLog(*check);

	ed->UndoL();
	check->SetCheckString(*styleLog12);
	testEd->Print(*check);
	CheckEditorLog(*check);

	ed->UndoL();
	check->SetCheckString(*styleLog11);
	testEd->Print(*check);
	CheckEditorLog(*check);

	ed->UndoL();
	check->SetCheckString(*styleLog10);
	testEd->Print(*check);
	CheckEditorLog(*check);

	ed->UndoL();
	check->SetCheckString(*styleLog9);
	testEd->Print(*check);
	CheckEditorLog(*check);

	ed->UndoL();
	check->SetCheckString(*styleLog8);
	testEd->Print(*check);
	CheckEditorLog(*check);

	ed->UndoL();
	check->SetCheckString(*styleLog7);
	testEd->Print(*check);
	CheckEditorLog(*check);

	ed->UndoL();
	check->SetCheckString(*styleLog6);
	testEd->Print(*check);
	CheckEditorLog(*check);

	ed->UndoL();
	check->SetCheckString(*styleLog5);
	testEd->Print(*check);
	CheckEditorLog(*check);

	ed->UndoL();
	check->SetCheckString(*styleLog4);
	testEd->Print(*check);
	CheckEditorLog(*check);

	ed->UndoL();
	check->SetCheckString(*styleLog3);
	testEd->Print(*check);
	CheckEditorLog(*check);

	ed->UndoL();
	check->SetCheckString(*styleLog2);
	testEd->Print(*check);
	CheckEditorLog(*check);

	ed->UndoL();
	check->SetCheckString(*styleLog1);
	testEd->Print(*check);
	CheckEditorLog(*check);

	ed->UndoL();
	check->SetCheckString(*textLog3);
	testEd->Print(*check);
	CheckEditorLog(*check);

	ed->RedoL();
	check->SetCheckString(*styleLog1);
	testEd->Print(*check);
	CheckEditorLog(*check);

	ed->RedoL();
	check->SetCheckString(*styleLog2);
	testEd->Print(*check);
	CheckEditorLog(*check);

	ed->RedoL();
	check->SetCheckString(*styleLog3);
	testEd->Print(*check);
	CheckEditorLog(*check);

	ed->RedoL();
	check->SetCheckString(*styleLog4);
	testEd->Print(*check);
	CheckEditorLog(*check);

	ed->RedoL();
	check->SetCheckString(*styleLog5);
	testEd->Print(*check);
	CheckEditorLog(*check);

	ed->RedoL();
	check->SetCheckString(*styleLog6);
	testEd->Print(*check);
	CheckEditorLog(*check);

	ed->RedoL();
	check->SetCheckString(*styleLog7);
	testEd->Print(*check);
	CheckEditorLog(*check);

	ed->RedoL();
	check->SetCheckString(*styleLog8);
	testEd->Print(*check);
	CheckEditorLog(*check);

	ed->RedoL();
	check->SetCheckString(*styleLog9);
	testEd->Print(*check);
	CheckEditorLog(*check);

	ed->RedoL();
	check->SetCheckString(*styleLog10);
	testEd->Print(*check);
	CheckEditorLog(*check);

	ed->RedoL();
	check->SetCheckString(*styleLog11);
	testEd->Print(*check);
	CheckEditorLog(*check);

	ed->RedoL();
	check->SetCheckString(*styleLog12);
	testEd->Print(*check);
	CheckEditorLog(*check);

	ed->RedoL();
	check->SetCheckString(*styleLog13);
	testEd->Print(*check);
	CheckEditorLog(*check);

	// probably need some more style tests that test the full range of
	// attributes that a style may have.
	//...

	delete textLog0;
	delete textLog1;
	delete textLog2;
	delete parLog0;
	delete parLog1;
	delete parLog2;
	delete parLog3;
	delete charLog0;
	delete charLog1;
	delete charLog2;
	delete charLog3;
	delete styleLog1;
	delete styleLog2;
	delete styleLog3;
	delete styleLog4;
	delete styleLog5;
	delete styleLog6;
	delete styleLog7;
	delete styleLog8;
	delete styleLog9;
	delete styleLog10;
	delete styleLog11;
	delete styleLog12;
	delete styleLog13;

	delete textLog3;
	}

void TestEditorUndo::Test7L()
	{
	// test picture manipulation
	TPictureHeader pic;
	pic.iPictureType = KUidXzePictureType;
	testEd->Print(*log);
	picLog0 = log->GetStore();
	pic.iPicture = new (ELeave) CUndoTestPicture('A');
	ed->PictureSupport()->InsertPictureL(5, pic);
	testEd->Print(*log);
	picLog1 = log->GetStore();
	pic.iPicture = new (ELeave) CUndoTestPicture('B');
	ed->PictureSupport()->InsertPictureL(8, pic);
	testEd->Print(*log);
	picLog2 = log->GetStore();
	pic.iPicture = new (ELeave) CUndoTestPicture('C');
	ed->PictureSupport()->InsertPictureL(9, pic);
	testEd->Print(*log);
	picLog3 = log->GetStore();
	pic.iPicture = new (ELeave) CUndoTestPicture('D');
	ed->PictureSupport()->InsertPictureL(12, pic);
	ed->StyleSupport()->SetStyleL(6, 2, style1.iName);
	ed->SetCharFormatL(8, 3, charLayer);
	ed->SetParFormatL(7, 7, parLayer);
	testEd->Print(*log);
	picLog4 = log->GetStore();
	ed->DeleteTextL(5, 8);
	testEd->Print(*log);
	picLog5 = log->GetStore();

	ed->UndoL();
	check->SetCheckString(*picLog4);
	testEd->Print(*check);
	CheckEditorLog(*check);

	ed->UndoL();
	ed->UndoL();
	ed->UndoL();
	ed->UndoL();
	check->SetCheckString(*picLog3);
	testEd->Print(*check);
	CheckEditorLog(*check);

	ed->UndoL();
	check->SetCheckString(*picLog2);
	testEd->Print(*check);
	CheckEditorLog(*check);

	ed->UndoL();
	check->SetCheckString(*picLog1);
	testEd->Print(*check);
	CheckEditorLog(*check);

	ed->UndoL();
	check->SetCheckString(*picLog0);
	testEd->Print(*check);
	CheckEditorLog(*check);

	ed->RedoL();
	check->SetCheckString(*picLog1);
	testEd->Print(*check);
	CheckEditorLog(*check);

	ed->RedoL();
	check->SetCheckString(*picLog2);
	testEd->Print(*check);
	CheckEditorLog(*check);

	ed->RedoL();
	check->SetCheckString(*picLog3);
	testEd->Print(*check);
	CheckEditorLog(*check);

	ed->RedoL();
	ed->RedoL();
	ed->RedoL();
	ed->RedoL();
	check->SetCheckString(*picLog4);
	testEd->Print(*check);
	CheckEditorLog(*check);

	ed->RedoL();
	check->SetCheckString(*picLog5);
	testEd->Print(*check);
	CheckEditorLog(*check);

	style1.Close();
	style2.Close();
	parLayer.Close();
	par0.Close();
	parT.Close();
	parN.Close();
	parTN.Close();
	delete picLog0;
	delete picLog1;
	delete picLog2;
	delete picLog3;
	delete picLog4;
	delete picLog5;
	}

void TestEditorUndo::Test8L()
	{
	// test bookmarking
	for (TInt i = 0; i != 7; ++i)
		{
		testEd->Reset();
		ed->ResetUndo();
		if (i == 0)
			manager->SetBookmark();
		testEd->Print(*log);
		bookMarkLog0 = log->GetStore();
		ed->InsertTextL(0, _L("Hallo"), 0, 0, 0);	// hallo
		if (i == 1)
			manager->SetBookmark();
		testEd->Print(*log);
		bookMarkLog1 = log->GetStore();
		ed->DeleteTextL(2, 1);	// halo
		if (i == 2)
			manager->SetBookmark();
		testEd->Print(*log);
		bookMarkLog2 = log->GetStore();
		manager->BeginBatchLC();
		ed->DeleteTextL(3, 1);	// hal
		ed->InsertTextL(3, _L("t, who goes there?"), 0, 0, 0);	// halt, who goes there?
		if (i == 3)
			manager->SetBookmark();		// should not get set
		ed->DeleteTextL(9, 5);		// halt, who there?
		CleanupStack::PopAndDestroy();
		if (i == 4)
			manager->SetBookmark();
		testEd->Print(*log);
		bookMarkLog3 = log->GetStore();
		ed->InsertTextL(0, _L("Oi"), 0, 0, 0);
		if (i == 5)
			manager->SetBookmark();
		testEd->Print(*log);
		bookMarkLog4 = log->GetStore();
		ed->InsertTextL(2, _L("! "), 0, 0, 0);
		testEd->Print(*log);
		bookMarkLog5 = log->GetStore();
		if (i == 6)
			manager->SetBookmark();

		ed->UndoL();
		// coalescence should have happenned unless there is a bookmark
		// in the way.
		if (i == 5)
			{
			test(manager->IsAtBookmark());
			check->SetCheckString(*bookMarkLog4);
			testEd->Print(*check);
			CheckEditorLog(*check);
			ed->UndoL();
			}
		if (i == 4)
			test(manager->IsAtBookmark());
		else
			test(!manager->IsAtBookmark());
		check->SetCheckString(*bookMarkLog3);
		testEd->Print(*check);
		CheckEditorLog(*check);
		ed->UndoL();
		if (i == 2)
			test(manager->IsAtBookmark());
		else
			test(!manager->IsAtBookmark());
		check->SetCheckString(*bookMarkLog2);
		testEd->Print(*check);
		CheckEditorLog(*check);
		ed->UndoL();
		if (i == 1)
			test(manager->IsAtBookmark());
		else
			test(!manager->IsAtBookmark());
		check->SetCheckString(*bookMarkLog1);
		testEd->Print(*check);
		CheckEditorLog(*check);
		ed->UndoL();
		if (i == 0)
			test(manager->IsAtBookmark());
		else
			test(!manager->IsAtBookmark());
		check->SetCheckString(*bookMarkLog0);
		testEd->Print(*check);
		CheckEditorLog(*check);
		test(!ed->CanUndo());
		ed->RedoL();
		if (i == 1)
			test(manager->IsAtBookmark());
		else
			test(!manager->IsAtBookmark());
		check->SetCheckString(*bookMarkLog1);
		testEd->Print(*check);
		CheckEditorLog(*check);
		ed->RedoL();
		if (i == 2)
			test(manager->IsAtBookmark());
		else
			test(!manager->IsAtBookmark());
		check->SetCheckString(*bookMarkLog2);
		testEd->Print(*check);
		CheckEditorLog(*check);
		ed->RedoL();
		if (i == 4)
			test(manager->IsAtBookmark());
		else
			test(!manager->IsAtBookmark());
		check->SetCheckString(*bookMarkLog3);
		testEd->Print(*check);
		CheckEditorLog(*check);
		ed->RedoL();
		if (i == 5)
			{
			test(manager->IsAtBookmark());
			check->SetCheckString(*bookMarkLog4);
			testEd->Print(*check);
			CheckEditorLog(*check);
			ed->RedoL();
			}
		test(!ed->CanRedo());
		if (i == 6)
			test(manager->IsAtBookmark());
		else
			test(!manager->IsAtBookmark());

		delete bookMarkLog0;
		delete bookMarkLog1;
		delete bookMarkLog2;
		delete bookMarkLog3;
		delete bookMarkLog4;
		delete bookMarkLog5;
		}

	delete ed;
	delete testEd;
	delete log;
	delete check;
	}

void TestEditorUndoL()
	{
	__UHEAP_MARK;

	TestEditorUndo t;
	t.Test1L();
	t.Test2L();
	t.Test3L();
	t.Test4L();
	t.Test5L();
	t.Test6L();
	t.Test7L();
	t.Test8L();

	__UHEAP_MARKENDC(0);
	}
// integration of command manager with multiple editors
void TestMultipleEditorsL()
	{
	__UHEAP_MARK;

	CCheckingLogger* check = new(ELeave) CCheckingLogger;
	CStoringLogger* log = new(ELeave) CStoringLogger;

	CTestEditor* testEd0 = CTestEditor::NewL();
	CTestEditor* testEd1 = CTestEditor::NewL();
	CTestEditor* testEd2 = CTestEditor::NewL();
	CTestEditor* testEd3 = CTestEditor::NewL();
	CCommandManager* manager = CCommandManager::NewL();

	CEditorPlainTextWithUndo* ed0 =
		CEditorPlainTextWithUndo::NewL(*testEd0, manager);
	CEditorWithUndo* ed1 = CEditorWithUndo::NewL(*testEd1, manager);
	CEditorPlainTextWithUndo* ed2 =
		CEditorPlainTextWithUndo::NewL(*testEd2, manager);
	CEditorWithUndo* ed3 = CEditorWithUndo::NewL(*testEd3, manager);
	manager->Release();

	// Testing the API's of CEditorPlainTextWithUndo
	TTmCharFormatMask charBIMask;
	charBIMask.iFlags = TTmCharFormatMask::EItalic | TTmCharFormatMask::EBold;
	TOpenFontFaceAttribBase attrib;
	TTmCharFormat charB;
	attrib.SetBold(ETrue);
	charB.iFontSpec.SetAttrib(attrib);
	TTmCharFormatLayer charLayer;
	charLayer.iFormat = charB;
	charLayer.iMask = charBIMask;
	TTmParFormatMask parTMask;
	parTMask.iFlags = TTmParFormatMask::EKeepTogether;
	RTmParFormat parT;
	parT.iFlags = RTmParFormat::EKeepTogether;
	RTmParFormatLayer parLayer;
	parLayer.iMask = parTMask;

	//Setting the base, character and paragraph format and then inserting the text
	ed0->SetBaseFormatL(charB,parT);
	ed0->SetCharFormatL(0, 1, charLayer);
	ed0->SetParFormatL(0, 1, parLayer);

	// The main thing to check is that no commands coalesce that have
	// different targets which would if their targets matched. The
	// commands that can coalesce are Delete Text, Delete Plain Text,
	// Insert Text, Insert Plain Text, Delete Character Format, Delete
	// Paragraph Format.
	ed0->InsertTextL(0, _L("ab"), 0, 0, 0);
	testEd0->Print(*log);
	HBufC* log00 = log->GetStore();
	ed2->InsertTextL(0, _L("cd"), 0, 0, 0);
	testEd2->Print(*log);
	HBufC* log20 = log->GetStore();
	ed1->InsertTextL(0, _L("ef"), 0, 0, 0);
	testEd1->Print(*log);
	HBufC* log10 = log->GetStore();
	ed3->InsertTextL(0, _L("gh"), 0, 0, 0);
	testEd3->Print(*log);
	HBufC* log30 = log->GetStore();

	manager->UndoL();
	check->SetCheckString(*log10);
	testEd1->Print(*check);
	CheckEditorLog(*check);

	manager->UndoL();
	check->SetCheckString(*log20);
	testEd2->Print(*check);
	CheckEditorLog(*check);

	manager->UndoL();
	check->SetCheckString(*log00);
	testEd0->Print(*check);
	CheckEditorLog(*check);

	manager->UndoL();
	TestCannotUndo(*manager);

	manager->RedoL();
	check->SetCheckString(*log00);
	testEd0->Print(*check);
	CheckEditorLog(*check);

	manager->RedoL();
	check->SetCheckString(*log20);
	testEd2->Print(*check);
	CheckEditorLog(*check);

	manager->RedoL();
	check->SetCheckString(*log10);
	testEd1->Print(*check);
	CheckEditorLog(*check);

	manager->RedoL();
	check->SetCheckString(*log30);
	testEd3->Print(*check);
	CheckEditorLog(*check);
	TestCannotRedo(*manager);

	ed0->DeleteTextL(1, 1);
	testEd0->Print(*log);
	HBufC* log01 = log->GetStore();
	ed2->DeleteTextL(0, 1);
	testEd2->Print(*log);
	HBufC* log21 = log->GetStore();
	ed3->DeleteTextL(0, 1);
	testEd3->Print(*log);
	HBufC* log31 = log->GetStore();
	ed1->DeleteTextL(0, 1);
	testEd1->Print(*log);
	HBufC* log11 = log->GetStore();

	manager->UndoL();
	check->SetCheckString(*log31);
	testEd3->Print(*check);
	CheckEditorLog(*check);

	manager->UndoL();
	check->SetCheckString(*log21);
	testEd2->Print(*check);
	CheckEditorLog(*check);

	manager->UndoL();
	check->SetCheckString(*log01);
	testEd0->Print(*check);
	CheckEditorLog(*check);

	manager->UndoL();
	check->SetCheckString(*log30);
	testEd3->Print(*check);
	CheckEditorLog(*check);

	manager->RedoL();
	check->SetCheckString(*log01);
	testEd0->Print(*check);
	CheckEditorLog(*check);

	manager->RedoL();
	check->SetCheckString(*log21);
	testEd2->Print(*check);
	CheckEditorLog(*check);

	manager->RedoL();
	check->SetCheckString(*log31);
	testEd3->Print(*check);
	CheckEditorLog(*check);

	manager->RedoL();
	check->SetCheckString(*log11);
	testEd1->Print(*check);
	CheckEditorLog(*check);
	TestCannotRedo(*manager);

	parLayer.iFormat.CopyL(parT);

	//Getting the base format to check if it is set accordingly
	TTmCharFormat charB1;
	RTmParFormat parT1;
	ed0->GetBaseFormatL(charB1,parT1);
	test(charB1==charB);
	test(parT1==parT);

	//Getting the character format
	TTmCharFormatLayer charLayer1;
	MUnifiedEditor::TFormatLevel level=MUnifiedEditor::EEffective;
	TInt runLen=10;
	ed0->GetCharFormat(0,level,charLayer1,runLen);

	//Getting the paragraph format
	RTmParFormatLayer parLayer1;
	ed0->GetParFormatL(0,level,parLayer1,runLen);

	//Getting the text
	TPtrC text;
	ed0->GetText(0,text);
	test(text==_L("a"));

	//Deleting the formating
	ed0->DeleteCharFormatL(0,1);
	ed0->DeleteParFormatL(0,1);

	// To test CEditorCommandSetBaseFormat class
	// SetBaseFormatL calls CEditorCommandSetBaseFormatProto::CreateInverseL() which in turn calls CEditorCommandSetBaseFormat::NewL().
	ed1->SetBaseFormatL(charB,parT);
	ed1->SetCharFormatL(0, 1, charLayer);

	testEd1->Print(*log);
	HBufC* log12 = log->GetStore();
	ed3->SetCharFormatL(0, 1 ,charLayer);
	testEd3->Print(*log);
	HBufC* log32 = log->GetStore();
	ed3->SetParFormatL(0, 1, parLayer);
	testEd3->Print(*log);
	HBufC* log33 = log->GetStore();
	ed1->SetParFormatL(0, 1, parLayer);
	testEd1->Print(*log);
	HBufC* log13 = log->GetStore();

	manager->UndoL();
	check->SetCheckString(*log33);
	testEd3->Print(*check);
	CheckEditorLog(*check);

	manager->UndoL();
	check->SetCheckString(*log32);
	testEd3->Print(*check);
	CheckEditorLog(*check);

	manager->UndoL();
	check->SetCheckString(*log12);
	testEd1->Print(*check);
	CheckEditorLog(*check);

	manager->RedoL();
	check->SetCheckString(*log32);
	testEd3->Print(*check);
	CheckEditorLog(*check);

	manager->RedoL();
	check->SetCheckString(*log33);
	testEd3->Print(*check);
	CheckEditorLog(*check);

	manager->RedoL();
	check->SetCheckString(*log13);
	testEd1->Print(*check);
	CheckEditorLog(*check);

	parLayer.Close();
	parT.Close();
	delete log00;
	delete log10;
	delete log20;
	delete log30;
	delete log01;
	delete log11;
	delete log21;
	delete log31;
	delete log12;
	delete log13;
	delete log32;
	delete log33;
	manager->ResetUndo();
	delete ed0;
	delete ed1;
	delete ed2;
	delete ed3;
	delete testEd0;
	delete testEd1;
	delete testEd2;
	delete testEd3;
	delete log;
	delete check;

	__UHEAP_MARKENDC(0);
	}

//
//
//  Main
//
//

void RunTests()
	{
	__UHEAP_MARK;

	test.Title();
	test.Start(_L("@SYMTestCaseID:SYSLIB-FORM-LEGACY-UNDO-0001 Undo System Tests: "));

	// test of general undo system components
	TestCCommandStackL();
	TestCBatchCommandL();
	TestCCommandHistoryL();
	TestCCommandManagerL();

	// test of editor undo components
	TestEditorUndoL();

	// test that command manager and multiple editors integrate correctly
	TestMultipleEditorsL();

	test.End();
	test.Close();

	__UHEAP_MARKENDC(0);
	}

TInt E32Main()
	{
	TrapCleanup = CTrapCleanup::New();
	TRAPD(err, RunTests());
    test(err == KErrNone);
	delete TrapCleanup;
	return 0;
	}
