textrendering/textformatting/test/src/TUndo.cpp
changeset 0 1fb32624e06b
child 16 748ec5531811
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/textrendering/textformatting/test/src/TUndo.cpp	Tue Feb 02 02:02:46 2010 +0200
@@ -0,0 +1,3576 @@
+/*
+* 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;
+	}