diff -r 000000000000 -r 1fb32624e06b textrendering/textformatting/test/src/TUndo.cpp --- /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 + +#include "UndoSystem.h" +#include "UndoSystemImpl.h" +#include "EditorUndo.h" +#include "EditorPlainTextUndo.h" +#include +#include +#include +#include +#include "TGraphicsContext.h" +#include "form_and_etext_editor.h" + +#ifdef SYMBIAN_ENABLE_SPLIT_HEADERS +#include +#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(aLastCommand).CastToCCommandOffset(); + } + void AddInverseToLast(CSingleCommand& aLastCommand) const + { + CCommandOffset* c = + static_cast(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(aLastCommand).CastToCCommandOffset(); + } + void AddInverseToLast(CSingleCommand& aLastCommand) const + { + CCommandOffset* c = + static_cast(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(aLastCommand).CastToCCommandToggle(); + } + void AddInverseToLast(CSingleCommand& aLastCommand) const + { + CCommandToggle* c = + static_cast(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(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 iStyleNames[10]; + TInt iNumStyles; + CArrayFix* iAttrs; + CBufSeg* iText; + CArrayFix* iPics; + + CTestEditor* ConstructL() + { + iText = CBufSeg::NewL(5 * sizeof(TText)); + iAttrs = new(ELeave) CArrayFixFlat(5); + iPics = new(ELeave) CArrayFixFlat(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(setTo); + else if (low <= attr->iStyle && attr->iStyle <= high) + attr->iStyle = static_cast(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(this); + if (aId == KUidMUnifiedEditorPictureSupport) + return static_cast(this); + if (aId == KUidMUnifiedEditorClipboardSupport) + return static_cast(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(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; + }