authenticationservices/authenticationserver/test/tauthdb/step_persist2.cpp
changeset 29 ece3df019add
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/authenticationservices/authenticationserver/test/tauthdb/step_persist2.cpp	Tue Nov 24 09:06:03 2009 +0200
@@ -0,0 +1,595 @@
+/*
+* 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;
+	}
+
+
+