diff -r cd501b96611d -r ece3df019add authenticationservices/authenticationserver/test/tauthcliserv/step_authexpr_eval.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/authenticationservices/authenticationserver/test/tauthcliserv/step_authexpr_eval.cpp Tue Nov 24 09:06:03 2009 +0200 @@ -0,0 +1,697 @@ +/* +* Copyright (c) 2005-2009 Nokia Corporation and/or its subsidiary(-ies). +* All rights reserved. +* This component and the accompanying materials are made available +* under the terms of the License "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: +* This file contains functions which are used to +* test evaluating authentication expressions. +* +*/ + + +#include "tauthcliservstep.h" + +using namespace AuthServer; + +typedef TTestPluginInterface::TCallEntry TCE; + +#define elemCount(___x) (sizeof(___x) / sizeof(___x[0])) + +static TAuthExpressionWrapper BuildLeftAnd(TInt aRemainingLevels); +static TAuthExpressionWrapper BuildRightAnd(TInt aRemainingLevels); +static TAuthExpressionWrapper BuildBalancedAnd(TInt aRemainingLevels); +static TAuthExpressionWrapper BuildFailedAnd(TInt aRemainingLevels); +static TAuthExpressionWrapper BuildSuccessfulOr(TInt aRemainingLevels); + + +// -------- CTStepActSch -------- + + +// -------- CTStepAuthExprEval -------- + + +void TTestPluginInterface::Evaluate(TPluginId aPluginId, TIdentityId& aIdentity, + CAuthExpressionImpl::TType /*aType*/, TRequestStatus& aStatus) +/** + Implement MEvaluatorPluginInterface by completing + the request with an identity equal to the plugin id. + */ + { + const TCallEntry ce(aPluginId); + TInt r = iCallLog.Append(ce); + + // this can be KErrNoMemory in OOM tests + if (r == KErrNone) + { + if (aPluginId == KTestPluginUnknown) + aIdentity = KUnknownIdentity; + else + aIdentity = static_cast(aPluginId); + } + + aStatus = KRequestPending; + TRequestStatus* rs = &aStatus; + User::RequestComplete(rs, r); + } + + +void TTestPluginInterface::Evaluate(TAuthPluginType aPluginType, TIdentityId& aIdentity, + CAuthExpressionImpl::TType /*aType*/, TRequestStatus& aStatus) +/** + Implement MEvaluatorPluginInterface by completing + the request with an identity equal to the plugin type. + */ + { + const TCallEntry ce(aPluginType); + TInt r = iCallLog.Append(ce); + + // this can be KerrNoMemory in OOM tests + if (r == KErrNone) + aIdentity = static_cast(aPluginType); + + aStatus = KRequestPending; + TRequestStatus* rs = &aStatus; + User::RequestComplete(rs, KErrNone); + } + + +bool TTestPluginInterface::TCallEntry::operator==(const TTestPluginInterface::TCallEntry& aRhs) const + { + if (iCallType != aRhs.iCallType) + return false; + + if (iCallType == CAuthExpressionImpl::EPluginId) + return iPluginId == aRhs.iPluginId; + else + return iPluginType == aRhs.iPluginType; + } + + +void TTestClientInterface::EvaluationSucceeded(TIdentityId aIdentityId) +/** + Implement MEvaluatorClientInterface by recording + that the evaluation succeeded, and the resulting identity. + */ + { + iMode = ESucceeded; + iIdentityId = aIdentityId; + + CActiveScheduler::Stop(); + } + + +void TTestClientInterface::EvaluationFailed(TInt aReason) +/** + Implement MEvaluatorClientInterface by recording + that the evaluation failed, and the failure reason. + */ + { + iMode = EFailed; + iReason = aReason; + + CActiveScheduler::Stop(); + } + + +CLaunchEval* CLaunchEval::NewL() +/** + Factory function allocates new instance of CLaunchEval. + + @return New instance of CLaunchEval. + */ + { + CLaunchEval* self = new(ELeave) CLaunchEval(); + CleanupStack::PushL(self); + self->ConstructL(); + CleanupStack::Pop(self); + return self; + } + + +CLaunchEval::CLaunchEval() +/** + Set timer priority and add self to active scheduler. + */ +: CActive(CActive::EPriorityStandard) + { + CActiveScheduler::Add(this); + } + + +void CLaunchEval::ConstructL() +/** + Allocate evaluator and initialize superclass timer. + */ + { +// CTimer::ConstructL(); + iEval = CEvaluator::NewL(&iPluginInterface, &iClientInterface); + } + + +CLaunchEval::~CLaunchEval() +/** + Deletes evaluator which was allocated for this object. + */ + { + ResetInterfaces(); + delete iEval; + } + + +void CLaunchEval::ResetInterfaces() +/** + Free resources used by plugin and client interfaces. + */ + { + iPluginInterface.iCallLog.Reset(); + iClientInterface.iMode = TTestClientInterface::ENone; + } + + +void CLaunchEval::Evaluate(const CAuthExpression* aExpr) +/** + Queue this timer object and start the active + scheduler. This function returns when the evaluation + has completed. + + This object's client and plugin interfaces are reset + before the expression is evaluated, so they can be + tested by the function which calls this. + + @param aExpr Expression to evaluate. + */ + { + ResetInterfaces(); + iExpr = aExpr; // store so can see in RunL + + // signal this object. This ensures there + // is a pending active object before the scheduler + // is started. + iStatus = KRequestPending; + TRequestStatus* rs = &iStatus; + User::RequestComplete(rs, KErrNone); + SetActive(); + + // block until the evaluation has completed. + CActiveScheduler::Start(); + } + + +void CLaunchEval::RunL() +/** + Implement CActive by launching the evaluation. + At this point the active scheduler should have + been started. + */ + { + iEval->Evaluate(static_cast(iExpr)); + } + + +void CLaunchEval::DoCancel() +/** + Implement CActive by cancelling the evaluation + which is currently in progress. + + Not yet implemented. + */ + { + // empty. + } + + +CTStepAuthExprEval::CTStepAuthExprEval() +/** + Record this test step's name. + */ + { + SetTestStepName(KTStepAuthExprTypePncBadRight); + } + + +TVerdict CTStepAuthExprEval::doTestStepL() + { + CActiveScheduler::Install(iActSchd); + User::SetJustInTime(ETrue); + + __UHEAP_MARK; + TestEvalCreateL(); + TestEvalSimpleL(); + TestEvalAndL(); + TestEvalOrL(); + TestRPNReallocL(); + __UHEAP_MARKEND; + + return EPass; + } + + +void CTStepAuthExprEval::TestEvalCreateL() +/** + Test allocating and deleting an evaluator, + without using it for anything. + */ + { + __UHEAP_MARK; + + TTestClientInterface tci; + TTestPluginInterface tpi; + + CEvaluator* ev = CEvaluator::NewL(&tpi, &tci); + delete ev; + + __UHEAP_MARKEND; + } + + +void CTStepAuthExprEval::TestEvalSimpleL() +/** + Test evaluating a simple plugin id, and + evaluating a simple plugin type. + */ + { + __UHEAP_MARK; + + CLaunchEval* le = CLaunchEval::NewL(); + CleanupStack::PushL(le); + + // simple plugin id + CAuthExpression* aeId = AuthExpr(KTestPluginId0); + User::LeaveIfNull(aeId); + le->Evaluate(aeId); + delete aeId; + + const TCE aceI0[] = {TCE(KTestPluginId0)}; + TestEvalResultL(le, KTestPluginId0, aceI0, elemCount(aceI0)); + + // simple plugin type + CAuthExpression* aeType = AuthExpr(EAuthBiometric); + User::LeaveIfNull(aeType); + le->Evaluate(aeType); + delete aeType; + + const TCE aceTB[] = {TCE(EAuthBiometric)}; + TestEvalResultL(le, EAuthBiometric, aceTB, elemCount(aceTB)); + + CleanupStack::PopAndDestroy(le); + + __UHEAP_MARKEND; + } + + +void CTStepAuthExprEval::TestEvalAndL() +/** + Test evaluating simple AND expressions. + */ + { + __UHEAP_MARK; + + CLaunchEval* le = CLaunchEval::NewL(); + CleanupStack::PushL(le); + + // U & U = U (sc) + CAuthExpression* aeUU = AuthAnd(AuthExpr(KTestPluginUnknown), AuthExpr(KTestPluginUnknown)); + User::LeaveIfNull(aeUU); + le->Evaluate(aeUU); + delete aeUU; + + const TCE aceUU[] = {TCE(KTestPluginUnknown)}; + TestEvalResultL(le, KUnknownIdentity, aceUU, elemCount(aceUU)); + + // U & I1 = U (sc) + CAuthExpression* aeUI1 = AuthAnd(AuthExpr(KTestPluginUnknown), AuthExpr(KTestPluginId1)); + User::LeaveIfNull(aeUI1); + le->Evaluate(aeUI1); + delete aeUI1; + + const TCE aceUI1[] = {TCE(KTestPluginUnknown)}; + TestEvalResultL(le, KUnknownIdentity, aceUI1, elemCount(aceUI1)); + + // I1 & U = U + CAuthExpression* aeI1U = AuthAnd(AuthExpr(KTestPluginId1), AuthExpr(KTestPluginUnknown)); + User::LeaveIfNull(aeI1U); + le->Evaluate(aeI1U); + delete aeI1U; + + const TCE aceI1U[] = {TCE(KTestPluginId1), TCE(KTestPluginUnknown)}; + TestEvalResultL(le, KUnknownIdentity, aceI1U, elemCount(aceI1U)); + + // I1 & I1 = I1 + CAuthExpression* aeI1I1 = AuthAnd(AuthExpr(KTestPluginId1), AuthExpr(KTestPluginId1)); + User::LeaveIfNull(aeI1I1); + le->Evaluate(aeI1I1); + delete aeI1I1; + + const TCE aceI1I1[] = {TCE(KTestPluginId1), TCE(KTestPluginId1)}; + TestEvalResultL(le, KTestPluginId1, aceI1I1, elemCount(aceI1I1)); + + // I1 & I2 = U + CAuthExpression* aeI1I2 = AuthAnd(AuthExpr(KTestPluginId1), AuthExpr(KTestPluginId2)); + User::LeaveIfNull(aeI1I2); + le->Evaluate(aeI1I2); + delete aeI1I2; + + const TCE aceI1I2[] = {TCE(KTestPluginId1), TCE(KTestPluginId2)}; + TestEvalResultL(le, KUnknownIdentity, aceI1I2, elemCount(aceI1I2)); + + CleanupStack::PopAndDestroy(le); + + __UHEAP_MARKEND; + } + + +void CTStepAuthExprEval::TestEvalOrL() +/** + Test evaluating simple OR expressions. + */ + { + __UHEAP_MARK; + + CLaunchEval* le = CLaunchEval::NewL(); + CleanupStack::PushL(le); + + // U | U = U + CAuthExpression* aeUU = AuthOr(AuthExpr(KTestPluginUnknown), AuthExpr(KTestPluginUnknown)); + User::LeaveIfNull(aeUU); + le->Evaluate(aeUU); + delete aeUU; + + const TCE aceUU[] = {TCE(KTestPluginUnknown), TCE(KTestPluginUnknown)}; + TestEvalResultL(le, KUnknownIdentity, aceUU, elemCount(aceUU)); + + // U | I1 = I1 + CAuthExpression* aeUI1 = AuthOr(AuthExpr(KTestPluginUnknown), AuthExpr(KTestPluginId1)); + User::LeaveIfNull(aeUI1); + le->Evaluate(aeUI1); + delete aeUI1; + + const TCE aceUI1[] = {TCE(KTestPluginUnknown), TCE(KTestPluginId1)}; + TestEvalResultL(le, KTestPluginId1, aceUI1, elemCount(aceUI1)); + + // I1 | U = I1 (sc) + CAuthExpression* aeI1U = AuthOr(AuthExpr(KTestPluginId1), AuthExpr(KTestPluginUnknown)); + User::LeaveIfNull(aeI1U); + le->Evaluate(aeI1U); + delete aeI1U; + + const TCE aceI1U[] = {TCE(KTestPluginId1)}; + TestEvalResultL(le, KTestPluginId1, aceI1U, elemCount(aceI1U)); + + // I1 | I1 = I1 (sc) + CAuthExpression* aeI1I1 = AuthOr(AuthExpr(KTestPluginId1), AuthExpr(KTestPluginId1)); + User::LeaveIfNull(aeI1I1); + le->Evaluate(aeI1I1); + delete aeI1I1; + + const TCE aceI1I1[] = {TCE(KTestPluginId1)}; + TestEvalResultL(le, KTestPluginId1, aceI1I1, elemCount(aceI1I1)); + + // I1 | I2 = I1 (sc) + CAuthExpression* aeI1I2 = AuthOr(AuthExpr(KTestPluginId1), AuthExpr(KTestPluginId2)); + User::LeaveIfNull(aeI1I2); + le->Evaluate(aeI1I2); + delete aeI1I2; + + const TCE aceI1I2[] = {TCE(KTestPluginId1)}; + TestEvalResultL(le, KTestPluginId1, aceI1I2, elemCount(aceI1I2)); + + CleanupStack::PopAndDestroy(le); + + __UHEAP_MARKEND; + } + + +void CTStepAuthExprEval::TestEvalResultL( + CLaunchEval* aLaunchEval, TIdentityId aIdentityId, + const TTestPluginInterface::TCallEntry* aExpEntries, TInt aEntryCount) +/** + Test the evaluation produced the expected result, and + that the expected plugins were called in the right order. + */ + { + const TTestClientInterface& cli = aLaunchEval->iClientInterface; + TESTL(cli.iMode == TTestClientInterface::ESucceeded); + TESTL(cli.iIdentityId == aIdentityId); + + const RArray& log = aLaunchEval->iPluginInterface.iCallLog; + + TESTL(log.Count() == aEntryCount); + for (TInt i = 0; i < aEntryCount; ++i) + { + TESTL(log[i] == aExpEntries[i]); + } + } + + +static TAuthExpressionWrapper BuildLeftAnd(TInt aRemainingLevels) +/** + Build an expression where the left side is an + AND expression and the right side is a plugin ID. + + @param aRemainingLevels The number of layers to build + below this layer. If + aRemainingLevels == 0 this function + returns a simple plugin ID expression. + */ + { + return (aRemainingLevels == 0) + ? AuthExpr(KTestPluginId1) + : AuthAnd(BuildLeftAnd(aRemainingLevels - 1), AuthExpr(KTestPluginId1)); + } + + +static TAuthExpressionWrapper BuildRightAnd(TInt aRemainingLevels) +/** + Build an expression where the left side is a + plugin ID and the right side is an AND expression. + + @param aRemainingLevels The number of layers to build + below this layer. If + aRemainingLevels == 0 this function + returns a simple plugin ID expression. + */ + { + return (aRemainingLevels == 0) + ? AuthExpr(KTestPluginId1) + : AuthAnd(AuthExpr(KTestPluginId1), BuildRightAnd(aRemainingLevels - 1)); + } + + +static TAuthExpressionWrapper BuildBalancedAnd(TInt aRemainingLevels) +/** + Build an expression where both the left and right side + have the same depth, aRemainingLevels - 1. + + @param aRemainingLevels The number of layers to build + below this layer. If + aRemainingLevels == 0 this function + returns a simple plugin ID expression. + */ + { + return (aRemainingLevels == 0) + ? AuthExpr(KTestPluginId1) + : AuthAnd( + BuildBalancedAnd(aRemainingLevels - 1), + BuildBalancedAnd(aRemainingLevels - 1)); + } + + +static TAuthExpressionWrapper BuildFailedAnd(TInt aRemainingLevels) +/** + This function creates an expression where the left node + is a simple plugin ID expression and the right node is + built recursively with this function. The final AND node + has a left unknown plugin ID. + + This causes an unknown plugin ID to be automatically pushed + onto the RPN stack as a right value before the compounder is used. + + @param aRemainingLevels Number of levels to generate after this. + If aRemainingLevels == 1 this function + creates an AND node where the left node + is unknown. Otherwise it generates an + AND node where the left node is a known + plugin ID and the right node is generated + recursively. + */ + { + return (aRemainingLevels == 1) + ? AuthAnd(AuthExpr(KTestPluginUnknown), AuthExpr(KTestPluginId1)) + : AuthAnd(AuthExpr(KTestPluginId1), BuildFailedAnd(aRemainingLevels - 1)); + } + + +static TAuthExpressionWrapper BuildSuccessfulOr(TInt aRemainingLevels) +/** + This function creates an AND node where the left node + is a known plugin ID, and the right right node is generated + recursively. This creates a right-descent list, but the + penultimate node is an OR expression whose left node is a + known plugin ID. + + This puts a series of known plugin IDs on the RPN stack from + the left nodes of the AND nodes. When the OR node is evaluated + the left node is known, and so automatically put on the + RPN stack. + + This means that an OR right node is automatically put on the + RPN stack at a known point, which is used to stress test failing + to append an OR right expression in OOM. + + @param aRemainingLevels Number of levels to generate after this. + If aRemainingLevels == 1 this function + generates an OR node. Otherwise it creates + and AND node as described above. + */ + { + return (aRemainingLevels == 1) + ? AuthOr(AuthExpr(KTestPluginId1), AuthExpr(KTestPluginId1)) + : AuthAnd(AuthExpr(KTestPluginId1), BuildSuccessfulOr(aRemainingLevels - 1)); + } + + +void CTStepAuthExprEval::TestRPNReallocL() +/** + Create a deeply nested expression which is + deep enough that the evaluator has to reallocate + its RPN stack, and checks the evaluation fails + gracefully in OOM. + */ + { + __UHEAP_MARK; + + RunOomTestsL(BuildLeftAnd, KTestPluginId1, 0); + RunOomTestsL(BuildRightAnd, KTestPluginId1, 0); + RunOomTestsL(BuildBalancedAnd, KTestPluginId1, 0); + RunOomTestsL(BuildFailedAnd, KUnknownIdentity, 1); + RunOomTestsL(BuildSuccessfulOr, KTestPluginId1, 1); + + __UHEAP_MARKEND; + } + + +void CTStepAuthExprEval::RunOomTestsL( + TAuthExpressionWrapper (*aAllocator)(TInt), + TIdentityId aExpectedIdentity, TInt aInitDepth) +/** + Attempt to evaluate the supplied expresision in OOM. + + Running in OOM will both fail the evaluation, when the + plugin interface attempts to append to the call log, and + when the evaluator attempts to extend the RPN stack. + + OOM can only be tested in debug builds. In release builds, + this function evaluates the expression at each depth and + tests the evaluator produces the correct result. + + @param aAllocator Function which allocates the expression. + @param aExpectedIdentity Identity which should be returned on + successful evaluation. + @param aInitDepth Initial depth. + */ + { + CLaunchEval* le = CLaunchEval::NewL(); + User::LeaveIfNull(le); + CleanupStack::PushL(le); + + const volatile TTestClientInterface& cli = le->iClientInterface; + + // depth starts at zero because, even though RPN stack + // is not used, the evaluator will attempt to grow its + // call log, and so fail the evaluation. (This test is + // therefore also used to test failed plugin evaluations.) + + // max depth is 13 because CStepControl::StartL creates + // a worker thread with a 1MB maximum heap. The + // number of allocated node cells for a balanced tree + // is 2^(depth+1) - 1. When depth==13, there are + // 16383 cells using 327,672 bytes excluding cell headers. + // Allocation fails for depth == 14. + + const TInt KMaxDepth = 13; + for (TInt depth = aInitDepth; depth <= KMaxDepth; ++depth) + { + CAuthExpression* ae = aAllocator(depth); + User::LeaveIfNull(ae); + CleanupStack::PushL(ae); + + // OOM testing only available in debug builds +#ifndef _DEBUG + le->Evaluate(ae); + TESTL(cli.iMode == TTestClientInterface::ESucceeded); + TESTL(cli.iIdentityId == aExpectedIdentity); +#else + TInt i = 0; + do + { + // Ideally, the heap would be marked before and + // after the evaluation. However, CEvaluator uses + // an CArrayFixFlat to store the RPN stack. + // When the first item is inserted, it allocates a + // CBufBase object to hold the data. This object + // is reset but not deleted when the RPN stack is + // reset, so there will be a heap imbalance of one + // if anything was added to the RPN stack, even though + // the stack is reset. + + TInt preSize; + TInt preCount = User::AllocSize(preSize); +// __UHEAP_MARK; + + __UHEAP_SETFAIL(RAllocator::EDeterministic, i); + le->Evaluate(ae); + __UHEAP_RESET; + + TESTL( cli.iMode == TTestClientInterface::EFailed + || cli.iMode == TTestClientInterface::ESucceeded); + + if (cli.iMode == TTestClientInterface::EFailed) + { + TESTL(cli.iReason == KErrNoMemory); + } + else + { + TESTL(cli.iIdentityId == aExpectedIdentity); + } + + // clear call log so heap checking will work + le->iPluginInterface.iCallLog.Reset(); + ++i; + + TInt postSize; + TInt postCount = User::AllocSize(postSize); + TESTL(postCount == preCount || postCount == preCount + 1); +// __UHEAP_MARKEND; + } while (cli.iMode != TTestClientInterface::ESucceeded); + + // test evaluation still succeeds and failed allocation + // was not ignored + TInt limit = 2 * i; + while (i++ < limit) + { + __UHEAP_SETFAIL(RAllocator::EDeterministic, i++); + le->Evaluate(ae); + __UHEAP_RESET; + + TESTL(cli.iMode == TTestClientInterface::ESucceeded); + TESTL(cli.iIdentityId == aExpectedIdentity); + } + + // clear plugin call log to reset mem usage for next iteration. + le->iPluginInterface.iCallLog.Reset(); +#endif // #else #ifndef _DEBUG + CleanupStack::PopAndDestroy(ae); + } + + CleanupStack::PopAndDestroy(le); + } +