authenticationservices/authenticationserver/test/tauthdb/step_persist2.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Mon, 18 Jan 2010 21:34:37 +0200
changeset 32 3490edffc34e
parent 19 ece3df019add
child 56 c11c717470d0
permissions -rw-r--r--
Revision: 201003 Kit: 201003

/*
* 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: 
*
*/


#include <s32mem.h>
#include "tauthdbstep.h"
#include <f32file.h>

using namespace AuthServer;

const TIdentityId KIdentity1Id = 'I1ID';
const TIdentityId KIdentity2Id = 'I2ID';
const TIdentityId KIdentity3Id = 'I3ID';
_LIT(KIdentity1Desc, "identity-1-desc");
_LIT(KIdentity1DescB, "identity-1-descB");
_LIT(KIdentity2Desc, "identity-2-desc");
_LIT(KIdentity3Desc, "identity-3-desc");

const TPluginId KPlugin1Id = 'PID1';
const TPluginId KPlugin2Id = 'PID2';
const TPluginId KPlugin3Id = 'PID3';
const TPluginId KPlugin4Id = 'PID4';
const TPluginId KPluginAId = 'PIDA';
const TPluginId KPluginBId = 'PIDB';


CTStepPersist2::CTStepPersist2()
	{
	SetTestStepName(KTStepCreateTestDb);
	}


TVerdict CTStepPersist2::doTestStepL()
	{
	__UHEAP_MARK;
	
	RemoveExistingDbL();
		
	TestOpenCloseL();
	TestIdentitiesL();
	TestPrefsL();
	TestTrainedPluginsL();
	TestTrainingStatusL();
	TestRepairL();
	TestCompressL();
	
	__UHEAP_MARKEND;
	return EPass;
	}


void CTStepPersist2::TestOpenCloseL()
/**
	Test simply opening and closing a database,
	without using any other operations.
 */
	{
	CAuthDb2* db = CAuthDb2::NewL(iFs);
	delete db;
	}


void CTStepPersist2::TestIdentitiesL()
	{
	TInt r;
	
	__UHEAP_MARK;
	RemoveExistingDbL();
	CAuthDb2* db = CAuthDb2::NewLC(iFs);
	
	RArray<TIdentityId> ids;
	
	// get id list before any ids defined
	ids.AppendL('SYMB');	// garbage; ensure removed
	db->IdentitiesL(ids);
	TESTL(ids.Count() == 0);
	
	// add an identity
	db->AddIdentityL(KIdentity1Id, KIdentity1Desc);
	db->IdentitiesL(ids);
	TESTL(ids.Count() == 1);
	TESTL(ids[0] == KIdentity1Id);
	
	// retrieve the identity's description
	TestDescriptionL(db, KIdentity1Id, KIdentity1Desc);
	
	// change the identity's description
	db->SetDescriptionL(KIdentity1Id, KIdentity1DescB);
	TestDescriptionL(db, KIdentity1Id, KIdentity1DescB);

	// change the identity's description to a string which is
	// too long; ensure fails.
	TBuf<KMaxDescLen + 1> dbuf;
	dbuf.Fill('a', KMaxDescLen - 1);		// limit - 1
	db->SetDescriptionL(KIdentity1Id, dbuf);
	TestDescriptionL(db, KIdentity1Id, dbuf);
	
	dbuf.Fill('a', KMaxDescLen);			// limit
	db->SetDescriptionL(KIdentity1Id, dbuf);
	TestDescriptionL(db, KIdentity1Id, dbuf);
	
	db->SetDescriptionL(KIdentity1Id, KIdentity1DescB);
	TestDescriptionL(db, KIdentity1Id, KIdentity1DescB);
	dbuf.Fill('a', KMaxDescLen + 1);		// limit + 1
	TRAP(r, db->SetDescriptionL(KIdentity1Id, dbuf));
	TESTL(r == KErrAuthServDescTooLong);
	TestDescriptionL(db, KIdentity1Id, KIdentity1DescB);	// keep old desc
	
	// add a second identity
	db->AddIdentityL(KIdentity2Id, KIdentity2Desc);
	db->IdentitiesL(ids);
	TESTL(ids.Count() == 2);
	TESTL(	(ids[0] == KIdentity1Id && ids[1] == KIdentity2Id)
		||	(ids[0] == KIdentity2Id && ids[1] == KIdentity1Id) );
	TestDescriptionL(db, KIdentity1Id, KIdentity1DescB);
	TestDescriptionL(db, KIdentity2Id, KIdentity2Desc);
	
	// add an identity which already exists
	TRAP(r, db->AddIdentityL(KIdentity2Id, KIdentity2Desc));
	TESTL(r == KErrAuthServIdentityAlreadyExists);
	
	// retrieve description for an unregistered identity
	TRAP(r, db->DescriptionL(KIdentity3Id));
	TESTL(r == KErrAuthServIdentityNotFound);
	
	// set description for an unregistered identity
	TRAP(r, db->SetDescriptionL(KIdentity3Id, KIdentity3Desc));
	TESTL(r == KErrAuthServIdentityNotFound);
	
	// remove an unregistered identity
	TRAP(r, db->RemoveIdentityL(KIdentity3Id));
	TESTL(r == KErrAuthServIdentityNotFound);
	
	// remove an identity
	db->RemoveIdentityL(KIdentity1Id);
	db->IdentitiesL(ids);
	TESTL(ids.Count() == 1);
	TESTL(ids[0] == KIdentity2Id);
	TestDescriptionL(db, KIdentity2Id, KIdentity2Desc);
	
	// remove the last identity
	db->RemoveIdentityL(KIdentity2Id);
	db->IdentitiesL(ids);
	TESTL(ids.Count() == 0);
	
	// create an identity with a description to a string which is
	// too long; ensure fails.
	dbuf.Fill('a', KMaxDescLen - 1);		// limit - 1
	db->AddIdentityL(KIdentity3Id, dbuf);
	TestDescriptionL(db, KIdentity3Id, dbuf);
	db->RemoveIdentityL(KIdentity3Id);
	
	dbuf.Fill('a', KMaxDescLen);			// limit
	db->AddIdentityL(KIdentity3Id, dbuf);
	TestDescriptionL(db, KIdentity3Id, dbuf);
	db->RemoveIdentityL(KIdentity3Id);
	
	dbuf.Fill('a', KMaxDescLen + 1);		// limit + 1
	TRAP(r, db->AddIdentityL(KIdentity3Id, dbuf));
	TESTL(r == KErrAuthServDescTooLong);
	db->IdentitiesL(ids);							// no identity created
	TESTL(ids.Count() == 0);
	
	CleanupStack::PopAndDestroy(db);
	__UHEAP_MARKEND;
	}

void CTStepPersist2::TestDescriptionL(
	CAuthDb2* aDb, TIdentityId aIdentityId, const TDesC& aExpDesc)
/**
	Helper function for TestIdentitiesL.  Ensures the
	supplied identity has the supplied description.
	
	@param	aDb				Database which contains the identity.
	@param	aIdentity		Identity whose description should be
							retrieved.
	@param	aDesc			Expected description.
 */
	{
	HBufC* desc = aDb->DescriptionL(aIdentityId);
	CleanupStack::PushL(desc);
	TESTL(*desc == aExpDesc);
	CleanupStack::PopAndDestroy(desc);
	}


void CTStepPersist2::TestPrefsL()
/**
	Tests adding, modifying, and removing
	preferences from the database.
 */
	{
	TPluginId prefId;	
	__UHEAP_MARK;
	
	RemoveExistingDbL();
	CAuthDb2* db = CAuthDb2::NewLC(iFs);

	// get a preferred plugin for a type with no preferred plugin
	prefId = db->PreferredPluginL(EAuthBiometric);
	TESTL(prefId == KUnknownPluginId);

	// set a type's preferred plugin
	db->SetPreferredPluginL(EAuthBiometric, KPlugin1Id);
	prefId = db->PreferredPluginL(EAuthBiometric);
	TESTL(prefId == KPlugin1Id);
	
	// change a type's preferred plugin
	db->SetPreferredPluginL(EAuthBiometric, KPlugin2Id);
	prefId = db->PreferredPluginL(EAuthBiometric);
	TESTL(prefId == KPlugin2Id);
	
	// clear a type's preferred plugin
	db->ClearPreferredPluginL(EAuthBiometric);
	prefId = db->PreferredPluginL(EAuthBiometric);
	TESTL(prefId == KUnknownPluginId);
	
	// set preferred plugin for more than one type
	db->SetPreferredPluginL(EAuthBiometric, KPlugin3Id);
	db->SetPreferredPluginL(EAuthKnowledge, KPlugin4Id);
	prefId = db->PreferredPluginL(EAuthBiometric);
	TESTL(prefId == KPlugin3Id);
	prefId = db->PreferredPluginL(EAuthKnowledge);
	TESTL(prefId == KPlugin4Id);
	
	CleanupStack::PopAndDestroy(db);
	
	__UHEAP_MARKEND;
	}


void CTStepPersist2::TestTrainedPluginsL()
/**
	Test adding and removing trained plugins.
 */
	{
	__UHEAP_MARK;
	
	TInt r;
	RemoveExistingDbL();
	CAuthDb2* db = CAuthDb2::NewLC(iFs);

	const CTransientKeyInfo& tkiA = *iId1Keys[0];
	const CTransientKeyInfo& tkiB = *iId1Keys[1];
	const CTransientKeyInfo& tkiC = *iId1Keys[2];

	// set key info for an unregistered identity
	TRAP(r, db->SetTrainedPluginL(KIdentity1Id, KPluginAId, tkiA));
	TESTL(r == KErrAuthServIdentityNotFound);

	// set key info for a registered identity
	db->AddIdentityL(KIdentity1Id, KIdentity1Desc);
	db->SetTrainedPluginL(KIdentity1Id, KPluginAId, tkiA);
	
	// get key info for a trained identity
	TestKeyPresentL(db, KIdentity1Id, KPluginAId, tkiA);

	// change key info for a trained identity
	db->SetTrainedPluginL(KIdentity1Id, KPluginAId, tkiB);
	TestKeyPresentL(db, KIdentity1Id, KPluginAId, tkiB);

	// ---- bad KeyInfoL args ----
	
	// get key info for registered identity but untrained plugin
	TRAP(r, db->KeyInfoL(KIdentity1Id, KPluginBId))
	TESTL(r == KErrAuthServTrainingNotFound);

	// get key info for unregistered identity but trained plugin	
	TRAP(r, db->KeyInfoL(KIdentity3Id, KPluginAId))
	TESTL(r == KErrAuthServTrainingNotFound);
	
	// get key info for unregistered identity and untrained plugin
	TRAP(r, db->KeyInfoL(KIdentity3Id, KPluginBId));
	TESTL(r == KErrAuthServTrainingNotFound);
	
	// ---- bad RemoveTrainedPluginL args ----
	
	// clear key info for registered identity but untrained plugin
	TRAP(r, db->KeyInfoL(KIdentity1Id, KPluginBId))
	TESTL(r == KErrAuthServTrainingNotFound);

	// clear key info for unregistered identity but trained plugin	
	TRAP(r, db->RemoveTrainedPluginL(KIdentity3Id, KPluginAId))
	TESTL(r == KErrAuthServTrainingNotFound);
	
	// clear key info for unregistered identity and untrained plugin
	TRAP(r, db->RemoveTrainedPluginL(KIdentity3Id, KPluginBId));
	TESTL(r == KErrAuthServTrainingNotFound);
	
	// -----

	// clear a present trained plugin
	db->RemoveTrainedPluginL(KIdentity1Id, KPluginAId);
	TRAP(r, db->KeyInfoL(KIdentity3Id, KPluginAId))
	TESTL(r == KErrAuthServTrainingNotFound);
	
	// register multiple trained plugins for a single identity	
	db->SetTrainedPluginL(KIdentity1Id, KPluginAId, tkiA);
	db->SetTrainedPluginL(KIdentity1Id, KPluginBId, tkiB);
	TestKeyPresentL(db, KIdentity1Id, KPluginAId, tkiA);
	TestKeyPresentL(db, KIdentity1Id, KPluginAId, tkiB);

	// train a single plugin for multiple identities
	db->AddIdentityL(KIdentity2Id, KIdentity2Desc);
	db->SetTrainedPluginL(KIdentity2Id, KPluginAId, tkiC);
	TestKeyPresentL(db, KIdentity1Id, KPluginAId, tkiA);
	TestKeyPresentL(db, KIdentity1Id, KPluginBId, tkiB);
	TestKeyPresentL(db, KIdentity2Id, KPluginAId, tkiC);
	
	// ---- atomic identity and key addition ----
	
	// add identity with trained plugin
	db->AddIdentityWithTrainedPluginL(KIdentity3Id, KIdentity3Desc, tkiA);
	TestKeyPresentL(db, KIdentity3Id, tkiA.PluginId(), tkiA);
	
	// fail to add identity with trained plugin - id already exists
	TRAP(r, db->AddIdentityWithTrainedPluginL(KIdentity3Id, KIdentity3Desc, tkiA));
	TESTL(r == KErrAuthServIdentityAlreadyExists);
	
	CleanupStack::PopAndDestroy(db);
	
	__UHEAP_MARKEND;
	}


void CTStepPersist2::TestKeyPresentL(
	CAuthDb2* aAuthDb, TIdentityId aIdentityId, TPluginId aPluginId,
	const CTransientKeyInfo& aTarget)
/**
	Helper function for TestTrainedPluginsL.
	
	Test the described transient key exists in the database,
	and that is is equal to the supplied key.  Leaves if not
	the case.

	@param	aAuthDb			Authorisation database to retrieve
							transient key info from.
	@param	aIdentityId		Identity which should be trained for
							the supplied plugin.
	@param	aPluginId		Plugin for which the supplied identity
							should be trained.
	@param	aTarget			The retrieved transient key info should
							be equal to this.
 */
	{
	CTransientKeyInfo* tkiActual = aAuthDb->KeyInfoL(aIdentityId, aPluginId);
	CleanupStack::PushL(tkiActual);

	// ensure key infos have same externalized size
	TSizeStream ssTarget;
	RWriteStream wsTarget(&ssTarget);
	aTarget.ExternalizeL(wsTarget);
	TInt targetSize = ssTarget.Size();

	TSizeStream ssActual;
	RWriteStream wsActual(&ssActual);
	tkiActual->ExternalizeL(wsActual);
	TESTL(targetSize == ssActual.Size());

	HBufC8* targetBuf = HBufC8::NewLC(targetSize);
	TPtr8 targetBufDes = targetBuf->Des();
	RDesWriteStream dwsTarget(targetBufDes);
	aTarget.ExternalizeL(dwsTarget);

	HBufC8* actualBuf = HBufC8::NewLC(targetSize);
	TPtr8 actualBufDes = actualBuf->Des();
	RDesWriteStream dwsActual(actualBufDes);
	tkiActual->ExternalizeL(dwsActual);

	TESTL(targetBufDes == actualBufDes);

	CleanupStack::PopAndDestroy(3, tkiActual);
	}


void CTStepPersist2::TestTrainingStatusL()
/**
	Test the plugins' training statuses accurately
	reflect the identities which are registered
	with them.
 */
	{
	__UHEAP_MARK;

	RemoveExistingDbL();
	
	CAuthDb2* db = CAuthDb2::NewLC(iFs);

	// if there are no users then a plugin should be marked
	// as untrained, even though its training count is equal
	// to the number of registered identities.
	TESTL(db->PluginStatusL(KPluginAId) == EAuthUntrained);

	// untrained when no users trained
	db->AddIdentityL(KIdentity1Id, KIdentity1Desc);
	TESTL(db->PluginStatusL(KPluginAId) == EAuthUntrained);
	db->AddIdentityL(KIdentity2Id, KIdentity2Desc);
	TESTL(db->PluginStatusL(KPluginAId) == EAuthUntrained);

	// trained when some, but not all, users trained
	const CTransientKeyInfo& tkiA = *iId1Keys[0];
	db->SetTrainedPluginL(KIdentity1Id, KPluginAId, tkiA);
	TESTL(db->PluginStatusL(KPluginAId) == EAuthTrained);
	
	// fully trained when all users trained
	db->SetTrainedPluginL(KIdentity2Id, KPluginAId, tkiA);
	TESTL(db->PluginStatusL(KPluginAId) == EAuthFullyTrained);
	
	// back to trained when identity untrained
	db->RemoveTrainedPluginL(KIdentity1Id, KPluginAId);
	TESTL(db->PluginStatusL(KPluginAId) == EAuthTrained);
	
	// back to untrained when last training removed
	db->RemoveTrainedPluginL(KIdentity2Id, KPluginAId);
	TESTL(db->PluginStatusL(KPluginAId) == EAuthUntrained);
	
	// restored to trained when identity trained
	db->SetTrainedPluginL(KIdentity1Id, KPluginAId, tkiA);
	TESTL(db->PluginStatusL(KPluginAId) == EAuthTrained);

	// restored to fully trained when last identity trained
	db->SetTrainedPluginL(KIdentity2Id, KPluginAId, tkiA);
	TESTL(db->PluginStatusL(KPluginAId) == EAuthFullyTrained);
	
	// back to trained when new identity added
	db->AddIdentityL(KIdentity3Id, KIdentity3Desc);
	TESTL(db->PluginStatusL(KPluginAId) == EAuthTrained);

	// upgraded to fully trained when untrained identity removed
	db->RemoveIdentityL(KIdentity3Id);
	TESTL(db->PluginStatusL(KPluginAId) == EAuthFullyTrained);
	
	// kept at fully trained when trained identity removed
	db->RemoveIdentityL(KIdentity2Id);
	TESTL(db->PluginStatusL(KPluginAId) == EAuthFullyTrained);
	
	// drop to untrained when last identity removed
	db->RemoveIdentityL(KIdentity1Id);
	TESTL(db->PluginStatusL(KPluginAId) == EAuthUntrained);
	
	CleanupStack::PopAndDestroy(db);
	
	__UHEAP_MARKEND;
	}


static void IdToTestDesc(TIdentityId aId, TDes& aDesc)
/**
	Helper function for TestRepairL generates a description
	from the supplied identity.
	
	@param	aId				Identity Id.
	@param	aDesc			Out paramater is populated with
							description text.
 */
	{
	_LIT(KDescFmt, "desc_%08x");
	aDesc.Format(KDescFmt, aId);
	}


void CTStepPersist2::TestRepairL()
/**
	Test CAuthDb repairs the database if possible.
 */
	{
	RemoveExistingDbL();
	
	CAuthDb2* db = CAuthDb2::NewLC(iFs);
	
	// create a set of identities
	const TInt KTestIdCount = 8;
	for (TInt i = 1; i <= KTestIdCount; ++i)
		{
		TBuf<13> buf;
		IdToTestDesc(i, buf);
		db->AddIdentityL(i, buf);
		}
	CleanupStack::PopAndDestroy(db);
	
	// damage the database by inserting a new entry but then
	// rolling it back.
	RDbNamedDatabase ndb;
	TFileName dbName(KDbName);
	dbName[0] = RFs::GetSystemDriveChar();
	
	User::LeaveIfError(ndb.Open(iFs, dbName));
	CleanupClosePushL(ndb);
	
	User::LeaveIfError(ndb.Begin());
	
	RDbTable table;
	CleanupClosePushL(table);
	User::LeaveIfError(table.Open(ndb, KIdentitiesTableName));
	
	table.InsertL();
	table.SetColL(KIdentitiesIdentityIdCol, KTestIdCount+1);
	table.SetColL(KIdentitiesDescCol, _L("descb"));
	table.PutL();
	table.Close();
	ndb.Rollback();
	TESTL(ndb.IsDamaged());
	
	CleanupStack::PopAndDestroy(2, &ndb);	// table ndb
	
	// ensure db contains the same identities when it
	// is reopened.
	db = CAuthDb2::NewLC(iFs);
	
	RArray<TIdentityId> ids;
	db->IdentitiesL(ids);
	TInt idCount = ids.Count();
	ids.Reset();
	TESTL(idCount == KTestIdCount);
	
	for (TInt index = 1; index <= KTestIdCount; ++index)
		{
		TBuf<13> bufExp;
		IdToTestDesc(index, bufExp);
		TBuf<KMaxDescLen> actDesc;
		HBufC* descAct = db->DescriptionL(index);
		CleanupStack::PushL(descAct);
		TESTL(bufExp == *descAct);
		CleanupStack::PopAndDestroy(descAct);
		}
	CleanupStack::PopAndDestroy(db);
	}


void CTStepPersist2::TestCompressL()
/**
	Creates and deletes identities to create unused
	space in database file, and tests compressed.
 */
	{
	RemoveExistingDbL();
	CAuthDb2* db = CAuthDb2::NewLC(iFs);
	
	const TInt KIdentityCount = 32;
	for (TInt i = 0; i < KIdentityCount; ++i)
		{
		_LIT(KCompDesc, "tc-test");
		db->AddIdentityL(i, KCompDesc);
		db->RemoveIdentityL(i);
		}
	CleanupStack::PopAndDestroy(db);
	
	TInt szPreComp = DbFileSizeL();
	
	db = CAuthDb2::NewLC(iFs);
	db->CompactIfRequired();
	CleanupStack::PopAndDestroy(db);
	
	TInt szPostComp = DbFileSizeL();
	
	TESTL(szPreComp > szPostComp);
	}


TInt CTStepPersist2::DbFileSizeL()
/**
	Helper function for TestCompressL returns
	the size of the database file in bytes.
 */
	{
	RFile f;
	TFileName dbName(KDbName);
	dbName[0] = RFs::GetSystemDriveChar();

	User::LeaveIfError(f.Open(iFs, dbName, EFileRead | EFileStream));
	CleanupClosePushL(f);
	
	TInt sz;
	User::LeaveIfError(f.Size(sz));
	CleanupStack::PopAndDestroy(&f);
	
	return sz;
	}