sl@0: // Copyright (c) 2005-2009 Nokia Corporation and/or its subsidiary(-ies). sl@0: // All rights reserved. sl@0: // This component and the accompanying materials are made available sl@0: // under the terms of "Eclipse Public License v1.0" sl@0: // which accompanies this distribution, and is available sl@0: // at the URL "http://www.eclipse.org/legal/epl-v10.html". sl@0: // sl@0: // Initial Contributors: sl@0: // Nokia Corporation - initial contribution. sl@0: // sl@0: // Contributors: sl@0: // sl@0: // Description: sl@0: // This file contains code to test the EcomCachedDriveInfo class. sl@0: // sl@0: // sl@0: sl@0: #include sl@0: #include sl@0: #include sl@0: #include "EComPatchDataConstantv2.h" sl@0: #include "DriveInfo.h" sl@0: #include "EComInternalErrorCodes.h" sl@0: #define UNUSED_VAR(a) (a = a) sl@0: sl@0: LOCAL_D RTest TheTest(_L("t_driveinfo")); sl@0: LOCAL_D RFs TheFs; sl@0: static TInt IteratorPanicTest(TAny* aFuncCode); sl@0: sl@0: const TInt KPanicIndexOutOfBound = 133; sl@0: _LIT(KTestFolder, "C:\\TestTemp\\"); sl@0: sl@0: enum TIteratorFunctionToTest sl@0: { sl@0: EIterFunctionDriveUnit, sl@0: EIterFunctionDriveNumber, sl@0: EIterFunctionIsReadOnlyInternal, sl@0: EIterFunctionIsRemovable sl@0: }; sl@0: sl@0: class TDriveInfo_StateAccessor sl@0: { sl@0: public: sl@0: static CEComCachedDriveInfo* GetCachedDriveInfoL(RFs& aFs, TUint32 aDrvMask); sl@0: static void EComDrvFlagsL(TInt aDriveNum, sl@0: TUint32& aDrvFlags, sl@0: const CEComCachedDriveInfo& aCachedDriveInfo); sl@0: }; sl@0: sl@0: /** sl@0: Because this class is friend of CEComCachedDriveInfo, it can call the sl@0: private constructor of CEComCachedDriveInfo to make object instance sl@0: with drive disabled mask different from the patchable constant. sl@0: @param aFs Connected RFs session. sl@0: @param aDrvMask The discovery disabled drive mask to pass to sl@0: CEComCachedDriveInfo. sl@0: @return fully constructed CEComCachedDriveInfo. Caller owns the pointer. sl@0: @leave KErrNoMemory if system out of memory. sl@0: */ sl@0: CEComCachedDriveInfo* TDriveInfo_StateAccessor::GetCachedDriveInfoL( sl@0: RFs& aFs, sl@0: TUint32 aDrvMask) sl@0: { sl@0: // Set this bool to false otherwise ConstructL will do nothing. sl@0: CEComCachedDriveInfo::iInitialized = EFalse; sl@0: sl@0: CEComCachedDriveInfo* ptr = new (ELeave) CEComCachedDriveInfo(); sl@0: CleanupStack::PushL(ptr); sl@0: ptr->ConstructL(aFs, aDrvMask); sl@0: CleanupStack::Pop(); sl@0: return ptr; sl@0: } sl@0: sl@0: /** sl@0: static method sl@0: sl@0: Retrieve the flag word stored by CEComCachedDriveInfo about a given drive. sl@0: @param aDriveNum the drive of interest sl@0: @param aDrvFlags output parameter to return the drive attributes sl@0: @param aCachedDriveInfo the object instance to access. sl@0: @leave KErrNotFound if no such drive sl@0: */ sl@0: void TDriveInfo_StateAccessor::EComDrvFlagsL(TInt aDriveNum, sl@0: TUint32& aDrvFlags, sl@0: const CEComCachedDriveInfo& aCachedDriveInfo) sl@0: { sl@0: for (TInt i = 0; i <= aCachedDriveInfo.iLastIndex; i++) sl@0: { sl@0: if (aCachedDriveInfo.iDriveAttr[i].iDrvNumber == aDriveNum) sl@0: { sl@0: aDrvFlags = aCachedDriveInfo.iDriveAttr[i].iFlags; sl@0: return; sl@0: } sl@0: } sl@0: sl@0: User::Leave(KErrNotFound); sl@0: } sl@0: sl@0: //Test macroses and functions sl@0: LOCAL_C void CheckL(TInt aValue, TInt aLine) sl@0: { sl@0: if(!aValue) sl@0: { sl@0: TheTest(EFalse, aLine); sl@0: } sl@0: } sl@0: #define TESTL(arg) ::CheckL((arg), __LINE__) sl@0: sl@0: sl@0: /** Check CEComCachedDriveInfo has the correct attributes for the given drive sl@0: and the iterator will return the drive if discovery is not disabled. sl@0: */ sl@0: LOCAL_C void VerifyDrvAttributeL(const TInt aDriveNum, sl@0: TUint32 aDisableMask, sl@0: const CEComCachedDriveInfo& aCachedDriveInfo) sl@0: { sl@0: TEComCachedDriveInfoIterator iter(aCachedDriveInfo); sl@0: sl@0: TDriveInfo driveInfo; sl@0: User::LeaveIfError(TheFs.Drive(driveInfo, aDriveNum)); sl@0: sl@0: if (0 == driveInfo.iDriveAtt) sl@0: { sl@0: // Drive not exist, i.e. drive letter not in-used sl@0: sl@0: TESTL( !iter.SetPos(aDriveNum) ); sl@0: return; sl@0: } sl@0: sl@0: TUint32 expectedAttr = 0; sl@0: if ((driveInfo.iDriveAtt & KDriveAttInternal) && sl@0: (driveInfo.iMediaAtt & KMediaAttWriteProtected)) sl@0: { sl@0: // Drive is ROnly internal which cannot be disabled. sl@0: expectedAttr = EEComDrvAttrReadOnlyInternal; sl@0: } sl@0: else sl@0: { sl@0: TUint32 drvBitMask = 1; sl@0: if ((drvBitMask << aDriveNum) & aDisableMask || sl@0: driveInfo.iDriveAtt & KDriveAttSubsted || sl@0: driveInfo.iDriveAtt & KDriveAttRemote) sl@0: { sl@0: expectedAttr |= EEComDrvAttrNoDiscovery; sl@0: } sl@0: sl@0: if (driveInfo.iDriveAtt & KDriveAttRemovable) sl@0: { sl@0: expectedAttr |= EEComDrvAttrRemovable; sl@0: } sl@0: sl@0: if (0 == (driveInfo.iDriveAtt & KDriveAttRom)) sl@0: { sl@0: expectedAttr |= EEComDrvAttrWritable; sl@0: } sl@0: } sl@0: sl@0: // Test iterator does not return disabled drives. sl@0: TBool found = EFalse; sl@0: for (iter.First(); iter.InRange() && !found; iter.Next()) sl@0: { sl@0: if (iter.DriveNumber() == aDriveNum) sl@0: { sl@0: found = ETrue; sl@0: } sl@0: } sl@0: sl@0: TBool expectedFound = !(expectedAttr & EEComDrvAttrNoDiscovery); sl@0: if (found != expectedFound) sl@0: { sl@0: TheTest.Printf(_L("Error drive %d, expected att 0x%X, iter found %d"), aDriveNum, expectedAttr, found); sl@0: } sl@0: TESTL(expectedFound == found); sl@0: sl@0: // verify drive attributes sl@0: TUint32 actualAttr = 0; sl@0: TDriveInfo_StateAccessor::EComDrvFlagsL(aDriveNum, actualAttr, sl@0: aCachedDriveInfo); sl@0: if (actualAttr != expectedAttr) sl@0: { sl@0: TheTest.Printf(_L("Error drive %d, expected att 0x%X, got 0x%X"), aDriveNum, expectedAttr, actualAttr); sl@0: } sl@0: TESTL(actualAttr == expectedAttr); sl@0: } sl@0: sl@0: /** sl@0: @SYMTestCaseID SYSLIB-ECOM-UT-3536 sl@0: @SYMTestCaseDesc Disable/enable each drive and verify CEComCachedDriveInfo sl@0: has correct attribute for each drive and the iterator will not return sl@0: drives that are disabled, subst or remote. sl@0: @SYMTestPriority High sl@0: @SYMTestActions Instantiate CEComCachedDriveInfo with each drive sl@0: disabled in turn. Verify the attribute and iterator operation on the drive. sl@0: Instantiate CEComCachedDriveInfo with all drive enable. Verify sl@0: attribute and iterator operation on each drive. sl@0: @SYMTestExpectedResults CEComCachedDriveInfo has the expected attributes for sl@0: each drive whethe the drive is enabled or disabled. The iterator will only sl@0: return the drive is the drive is enabled. sl@0: @SYMCR CR1049 sl@0: */ sl@0: LOCAL_C void DriveMaskTestL() sl@0: { sl@0: CEComCachedDriveInfo* cachedDriveInfo; sl@0: sl@0: // Disable each drive in turn to check that disable works as expected. sl@0: TInt i; sl@0: for (i = EDriveA; i <= EDriveZ; i++) sl@0: { sl@0: TUint32 disableMask = 1 << i; sl@0: cachedDriveInfo = TDriveInfo_StateAccessor::GetCachedDriveInfoL(TheFs, disableMask); sl@0: CleanupStack::PushL(cachedDriveInfo); sl@0: sl@0: // Check CEComCachedDriveInfo has the expected value. sl@0: VerifyDrvAttributeL(i, disableMask, *cachedDriveInfo); sl@0: sl@0: // Test CEComCachedDriveInfo::DriveIsReadOnlyInternalL and sl@0: // DriveIsRemovableL leaving. They should be used on drives that sl@0: // are known to be valid, e.g. drive extracted from the path sl@0: // of a discovered DLL. sl@0: // Since C: is disabled, CEComCachedDriveInfo will leave instead sl@0: // of answering true or false (because the answer is misleading). sl@0: if (i == EDriveC) sl@0: { sl@0: TRAPD(err, cachedDriveInfo->DriveIsReadOnlyInternalL(i) ); sl@0: TESTL(err == KEComErrDriveNotFound); sl@0: sl@0: TRAP(err, cachedDriveInfo->DriveIsRemovableL(i) ); sl@0: TESTL(err == KEComErrDriveNotFound); sl@0: } sl@0: sl@0: CleanupStack::PopAndDestroy(cachedDriveInfo); sl@0: } sl@0: sl@0: // Test enable case. sl@0: // Make sure the disable mask is zero. sl@0: TESTL(KDiscoveryDisabledDriveList == 0); sl@0: sl@0: cachedDriveInfo = TDriveInfo_StateAccessor::GetCachedDriveInfoL(TheFs,0); sl@0: CleanupStack::PushL(cachedDriveInfo); sl@0: sl@0: for (i = EDriveA; i < KMaxDrives; i++) sl@0: { sl@0: VerifyDrvAttributeL(i, 0, *cachedDriveInfo); sl@0: } sl@0: sl@0: CleanupStack::PopAndDestroy(cachedDriveInfo); sl@0: } sl@0: sl@0: /** sl@0: @SYMTestCaseID SYSLIB-ECOM-UT-3537 sl@0: @SYMTestCaseDesc Test the CEComCachedDriveInfo and its iterator classes sl@0: handles substituted drives correctly. sl@0: @SYMTestPriority High sl@0: @SYMTestActions Create a substituted drive, instantiate the cached drive info and verify that sl@0: the sustitued drive is not in the valid drive list. sl@0: @SYMTestExpectedResults Substituted drive is not in the valid drive list. sl@0: @SYMCR CR1049 sl@0: */ sl@0: LOCAL_C void SubstitutedDriveTestL() sl@0: { sl@0: //Create c:\TestTemp folder sl@0: TInt err = TheFs.MkDir(KTestFolder); sl@0: //Create substituted drive L:, it maps to C:\TestTemp\ folder sl@0: err = TheFs.SetSubst(KTestFolder,EDriveL); sl@0: TESTL(err==KErrNone); sl@0: sl@0: //Verify that L Drive is not in the valid list. sl@0: CEComCachedDriveInfo* cachedDriveInfo = sl@0: TDriveInfo_StateAccessor::GetCachedDriveInfoL(TheFs,0); sl@0: CleanupStack::PushL(cachedDriveInfo); sl@0: sl@0: VerifyDrvAttributeL(EDriveL, 0, *cachedDriveInfo); sl@0: sl@0: CleanupStack::PopAndDestroy(cachedDriveInfo); sl@0: sl@0: // undo subst sl@0: err = TheFs.SetSubst(KNullDesC, EDriveL); sl@0: TESTL(err==KErrNone); sl@0: sl@0: err = TheFs.RmDir(KTestFolder); sl@0: TESTL(err==KErrNone); sl@0: } sl@0: sl@0: /** sl@0: @SYMTestCaseID SYSLIB-ECOM-UT-3539 sl@0: @SYMTestCaseDesc Test the various methods exposed by TEComCachedDriveInfoIterator class. sl@0: @SYMTestPriority High sl@0: @SYMTestActions For each drive returned by the iterator, call all the sl@0: getter methods. sl@0: @SYMTestExpectedResults No leave or panic occur. sl@0: @SYMCR CR1049 sl@0: */ sl@0: LOCAL_C void ExerciseIterator() sl@0: { sl@0: CEComCachedDriveInfo* cachedDriveInfo = CEComCachedDriveInfo::NewL(TheFs); sl@0: CleanupStack::PushL(cachedDriveInfo); sl@0: sl@0: TEComCachedDriveInfoIterator iter(*cachedDriveInfo); sl@0: sl@0: for (iter.Last(); iter.InRange(); iter.Prev()) sl@0: { sl@0: TDriveNumber drvNum = iter.DriveNumber(); sl@0: TDriveUnit drvUnit = iter.DriveUnit(); sl@0: sl@0: // A trivial test just to use the returned objects. sl@0: TESTL(drvNum == drvUnit); sl@0: sl@0: TBool b = iter.DriveIsReadOnlyInternal(); sl@0: UNUSED_VAR(b); sl@0: sl@0: b = iter.DriveIsRemovable(); sl@0: UNUSED_VAR(b); sl@0: } sl@0: sl@0: CleanupStack::PopAndDestroy(cachedDriveInfo); sl@0: } sl@0: sl@0: /** sl@0: Intended Usage : Capture the PANIC that occurs in the thread. sl@0: @param : aName The name to be assigned to this thread. sl@0: @param : aFunction The function which causes the panic. sl@0: */ sl@0: LOCAL_C void ThreadPanicTest(const TDesC& aName,TThreadFunction aFunction) sl@0: { sl@0: TheTest.Next(aName); sl@0: TRequestStatus threadStatus; sl@0: RThread thread; sl@0: TBool jit; sl@0: jit=User::JustInTime(); sl@0: User::SetJustInTime(EFalse); sl@0: sl@0: for (TInt i = EIterFunctionDriveUnit; i <= EIterFunctionIsRemovable; i++) sl@0: { sl@0: TIteratorFunctionToTest func = static_cast(i); sl@0: TInt err=thread.Create(aName,aFunction,KDefaultStackSize,KMinHeapSize,KMinHeapSize, &func); sl@0: TESTL(err==KErrNone); sl@0: sl@0: thread.Logon(threadStatus); sl@0: thread.Resume(); sl@0: User::WaitForRequest(threadStatus); sl@0: sl@0: //Now check why the thread Exit sl@0: TExitType exitType = thread.ExitType(); sl@0: TInt exitReason = thread.ExitReason(); sl@0: TheTest.Printf(_L("PanicTest: exitType %d, reason %d\n"), exitType, exitReason); sl@0: sl@0: TESTL(exitType == EExitPanic); sl@0: TESTL(exitReason == KPanicIndexOutOfBound); sl@0: thread.Close(); sl@0: } sl@0: sl@0: User::SetJustInTime(jit); sl@0: } sl@0: sl@0: /** sl@0: @SYMTestCaseID SYSLIB-ECOM-UT-3540 sl@0: @SYMTestCaseDesc Test the TEComCachedDriveInfoIterator class will panic if object instance of this class access out of bound elements. sl@0: @SYMTestPriority High sl@0: @SYMTestActions Cause the iterator to panic by accessing out of range sl@0: cell. sl@0: @SYMTestExpectedResults Panic occur. sl@0: @SYMCR CR1049 sl@0: */ sl@0: LOCAL_C void DoIteratorPanicTestL(TIteratorFunctionToTest aFuncCode) sl@0: { sl@0: CEComCachedDriveInfo* cachedDriveInfo = CEComCachedDriveInfo::NewL(TheFs); sl@0: CleanupStack::PushL(cachedDriveInfo); sl@0: sl@0: TEComCachedDriveInfoIterator iter(*cachedDriveInfo); sl@0: // Run the iterator out of range. sl@0: for (iter.Last(); iter.InRange(); iter.Prev()) sl@0: { sl@0: } sl@0: sl@0: // Access the getter function to trigger panic. sl@0: switch (aFuncCode) sl@0: { sl@0: case EIterFunctionDriveUnit: sl@0: { sl@0: TDriveUnit d = iter.DriveUnit(); sl@0: break; sl@0: } sl@0: case EIterFunctionDriveNumber: sl@0: { sl@0: TDriveNumber d = iter.DriveNumber(); sl@0: break; sl@0: } sl@0: case EIterFunctionIsReadOnlyInternal: sl@0: { sl@0: TBool f = iter.DriveIsReadOnlyInternal(); sl@0: break; sl@0: } sl@0: case EIterFunctionIsRemovable: sl@0: { sl@0: TBool f = iter.DriveIsRemovable(); sl@0: break; sl@0: } sl@0: default: sl@0: // do nothing and the test will fail. sl@0: break; sl@0: } sl@0: sl@0: CleanupStack::PopAndDestroy(cachedDriveInfo); sl@0: } sl@0: sl@0: /** sl@0: @SYMTestCaseID SYSLIB-ECOM-CT-1485 sl@0: @SYMTestCaseDesc Tests EcomCachedDriveInfo class. sl@0: @SYMTestPriority High sl@0: @SYMTestActions Test the various methods exposed by EcomCachedDriveInfo, sl@0: ensuring that the returned values are correct. sl@0: @SYMTestExpectedResults The test must not fail. sl@0: @SYMDEF DEF073919 sl@0: */ sl@0: LOCAL_C void SystemDriveTestL() sl@0: { sl@0: CEComCachedDriveInfo* cachedDriveInfo = CEComCachedDriveInfo::NewL(TheFs); sl@0: CleanupStack::PushL(cachedDriveInfo); sl@0: sl@0: TDriveNumber driveSys = RFs::GetSystemDrive(); sl@0: TESTL( !cachedDriveInfo->DriveIsReadOnlyInternalL(driveSys) ); sl@0: TESTL( cachedDriveInfo->DriveIsWritableL(driveSys) ); sl@0: sl@0: // The old EcomCachedDriveInfo class has DriveIsRemoteL and sl@0: // DriveIsSubstL methods. To verify that system drive is neither sl@0: // remote nor subst, use TEComCachedDriveInfoIterator::SetPos(). sl@0: sl@0: TEComCachedDriveInfoIterator iter(*cachedDriveInfo); sl@0: TESTL( iter.SetPos(driveSys) ); sl@0: sl@0: // Test Z: drive property sl@0: TESTL( cachedDriveInfo->DriveIsReadOnlyInternalL(EDriveZ) ); sl@0: sl@0: CleanupStack::PopAndDestroy(cachedDriveInfo); sl@0: } sl@0: sl@0: /** Setup the TRAP harness to invoke DoIteratorPanicTestL sl@0: */ sl@0: TInt IteratorPanicTest(TAny* aFuncCode) sl@0: { sl@0: CTrapCleanup* threadcleanup = CTrapCleanup::New(); sl@0: TIteratorFunctionToTest* funcCode = static_cast(aFuncCode); sl@0: TRAPD(ret, DoIteratorPanicTestL(*funcCode)); sl@0: sl@0: delete threadcleanup; sl@0: return ret; sl@0: } sl@0: sl@0: typedef void (*ClassFuncPtrL) (void); sl@0: /** sl@0: Test under OOM conditions. sl@0: This is a wrapper function to call all test functions. sl@0: @param aTestFunctionL Pointer to test function. sl@0: @param aTestDesc test function name sl@0: */ sl@0: LOCAL_C void DoOOMTestL(ClassFuncPtrL aTestFunctionL, const TDesC& aTestDesc) sl@0: { sl@0: TheTest.Next(aTestDesc); sl@0: TInt err = KErrNone; sl@0: TInt tryCount = 0; sl@0: do sl@0: { sl@0: __UHEAP_MARK; sl@0: sl@0: // find out the number of open handles sl@0: TInt startProcessHandleCount; sl@0: TInt startThreadHandleCount; sl@0: RThread().HandleCount(startProcessHandleCount, startThreadHandleCount); sl@0: sl@0: __UHEAP_SETFAIL(RHeap::EDeterministic, ++tryCount); sl@0: sl@0: TRAP(err, aTestFunctionL()); sl@0: sl@0: __UHEAP_SETFAIL(RHeap::ENone, 0); sl@0: sl@0: // check that no handles have leaked sl@0: TInt endProcessHandleCount; sl@0: TInt endThreadHandleCount; sl@0: RThread().HandleCount(endProcessHandleCount, endThreadHandleCount); sl@0: TESTL(startProcessHandleCount == endProcessHandleCount); sl@0: TESTL(startThreadHandleCount == endThreadHandleCount); sl@0: sl@0: __UHEAP_MARKEND; sl@0: } while(err == KErrNoMemory); sl@0: sl@0: TESTL(err==KErrNone); sl@0: TheTest.Printf(_L("- succeeded at heap failure rate of %i\n"), tryCount); sl@0: } sl@0: sl@0: /** sl@0: Wrapper function to call all test functions sl@0: @param aTestFunctionL pointer to test function sl@0: @param aTestDesc test function name sl@0: */ sl@0: LOCAL_C void DoBasicTestL(ClassFuncPtrL aTestFunctionL, const TDesC& aTestDesc) sl@0: { sl@0: TheTest.Next(aTestDesc); sl@0: __UHEAP_MARK; sl@0: // find out the number of open handles sl@0: TInt startProcessHandleCount; sl@0: TInt startThreadHandleCount; sl@0: RThread().HandleCount(startProcessHandleCount, startThreadHandleCount); sl@0: sl@0: aTestFunctionL(); sl@0: sl@0: // check that no handles have leaked sl@0: TInt endProcessHandleCount; sl@0: TInt endThreadHandleCount; sl@0: RThread().HandleCount(endProcessHandleCount, endThreadHandleCount); sl@0: sl@0: TESTL(startProcessHandleCount == endProcessHandleCount); sl@0: TESTL(startThreadHandleCount == endThreadHandleCount); sl@0: sl@0: __UHEAP_MARKEND; sl@0: } sl@0: sl@0: LOCAL_C void DoTestsL() sl@0: { sl@0: __UHEAP_MARK; sl@0: sl@0: User::LeaveIfError(TheFs.Connect()); sl@0: CleanupClosePushL(TheFs); sl@0: sl@0: // normal tests sl@0: DoBasicTestL(&DriveMaskTestL, _L("Drive Mask Test.")); sl@0: DoBasicTestL(&SubstitutedDriveTestL, _L("Substituted Drive Test.")); sl@0: DoBasicTestL(&ExerciseIterator, _L("Getter Test.")); sl@0: DoBasicTestL(&SystemDriveTestL, _L("System Drive Test.")); sl@0: sl@0: // panic tests sl@0: ThreadPanicTest(_L("Iterator Panic Testing"),IteratorPanicTest); sl@0: sl@0: // OOM tests sl@0: DoOOMTestL(&DriveMaskTestL, _L("OOM Test for Drive Mask Test.")); sl@0: DoOOMTestL(&ExerciseIterator, _L("OOM Test for Getter.")); sl@0: DoOOMTestL(&SystemDriveTestL, _L("OOM Test for System Drive Test.")); sl@0: sl@0: CleanupStack::PopAndDestroy(); sl@0: sl@0: __UHEAP_MARKEND; sl@0: } sl@0: sl@0: sl@0: GLDEF_C TInt E32Main() sl@0: { sl@0: __UHEAP_MARK; sl@0: sl@0: TheTest.Title(); sl@0: TheTest.Start(_L("Start Drive Info Tests.")); sl@0: sl@0: User::LeaveIfError (TheFs.Connect ()); sl@0: sl@0: CTrapCleanup* cleanup = CTrapCleanup::New(); sl@0: CActiveScheduler* scheduler = new(ELeave)CActiveScheduler; sl@0: CActiveScheduler::Install(scheduler); sl@0: sl@0: TRAPD(err,DoTestsL()); sl@0: TESTL(err==KErrNone); sl@0: sl@0: delete scheduler; sl@0: delete cleanup; sl@0: sl@0: TheFs.Close(); sl@0: sl@0: TheTest.End(); sl@0: TheTest.Close(); sl@0: sl@0: __UHEAP_MARKEND; sl@0: return(0); sl@0: }