/*
* Copyright (c) 2007-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:
* Exercises the test implementation of the session count server,
* to test the base SCS functionality.
*
*/
/**
@file
*/
#include <e32ldr.h>
#include <e32ldr_private.h>
#include <f32file.h>
#include "rtestwrapper.h"
#include <e32def_private.h>
#include <scs/scscommon.h>
#include "scstestcommon.h"
#include "scstestclient.h"
const TInt KOneSecondUs = 1000 * 1000; ///< One second in microseconds.
/** Top-level test object renders stages and confirms conditions. */
static RTestWrapper test(_L("SCSTEST"));
/**
This session handle is defined at the file level so each individual test
does not have to connect to the server.
*/
static RScsTestSession sTestSession;
/**
This subsession handle is defined at the file level so each individual test
does not have to connect to the server and create a subssesion.
*/
static RScsTestSubsession sTestSubsession;
/** Arbitrary integer value used to construct subsession. */
static const TInt KSubsessValue = 10;
static void LetServerRun()
/**
Sleep for 100ms so the server thread can run to clean up,
terminate, or just process the last request which it was sent.
*/
{
User::After(100 * 1000);
}
// -------- OOM testing --------
static void RunOomTest(TInt (*aAllocFunc)(), void (*aFreeFunc)())
/**
Run the supplied allocator function while forcing OOM on the server side.
On failure, this functions tests that the server heap is balanced. On
success, it runs the free function and also tests that the server heap is
balanced.
@param aAllocFunc Function which allocates resources on the server heap.
@param aFreeFunc Frees the resources allocated with aAllocFunc. It should
only be necessary to call this function if aAllocFunc
succeeds. This argument can be NULL if there is no
corresponding free function.
*/
{
TInt r = KErrNoMemory;
for (TInt i = 1; r == KErrNoMemory; ++i)
{
sTestSession.SetServerHeapFail(i);
r = aAllocFunc();
test(r == KErrNone || r == KErrNoMemory);
if (r == KErrNone && aFreeFunc != NULL)
aFreeFunc();
sTestSession.ResetServerHeapFail();
}
}
// -------- panic testing --------
/**
To test that a client is panicked, pass a function with this
signature to TestPanic. It will be called from a new thread.
@see TestPanic
*/
typedef void (*TPanicFunc)(RScsTestSession&);
static void ConfirmPanicReason(RThread aThread, TInt aExpectedReason)
/**
Test the supplied thread was panicked with category
ScsImpl::KScsClientPanicCat and the supplied reason.
@param aThread Panicked thread.
@param aExpectedReason The thread should have been panicked with
this reason.
@see ScsImpl::KScsClientPanicCat
*/
{
TExitType exitType = aThread.ExitType();
TExitCategoryName exitCat = aThread.ExitCategory();
TInt exitReason = aThread.ExitReason();
test(exitType == EExitPanic);
test(exitCat == ScsImpl::KScsClientPanicCat);
test(exitReason == aExpectedReason);
}
static TInt TestPanicEntrypoint(TAny* aPtr)
/**
This entrypoint is called by the panic thread. The
function connects to the SCS test server and passes
the handle to the function that should be panicked.
@param aPtr Standard entrypoint argument. This is actually
a pointer to the function to call with the connected
session.
@return KErrNone. Required to satisfy entrypoint signature.
*/
{
RScsTestSession s;
TInt r = s.Connect();
test(r == KErrNone);
TPanicFunc f = TAnyPtrToFuncPtr<TPanicFunc>(aPtr);
f(s);
test(EFalse); // should not reach here
return KErrNone;
}
static void TestPanic(TPanicFunc aFunc, TInt aExpectedReason)
/**
Create a thread which should be panicked because it will
pass invalid data to the SCS test server. Test the thread
is panicked with the expected category and reason.
@param aFunc Function to call from panic thread.
@param aExpectedReason Reason with which the thread should
be panicked.
*/
{
RThread thd;
TInt r = thd.Create(
_L("ScsTestPanic"), TestPanicEntrypoint,
KDefaultStackSize, KMinHeapSize, KMinHeapSize, (TAny*) aFunc);
test(r == KErrNone);
TRequestStatus rs;
thd.Rendezvous(rs);
test(rs == KRequestPending);
TBool jit = User::JustInTime();
User::SetJustInTime(EFalse);
thd.Resume();
User::WaitForRequest(rs);
User::SetJustInTime(jit);
ConfirmPanicReason(thd, aExpectedReason);
thd.Close();
}
// -------- open / close --------
/** Handle to a session which the test code attempts to open in server-side OOM. */
RScsTestSession sOomTestSession;
static TInt OpenOomSession()
/**
This function is invoked by RunOomTest. It just
attempts to connect to the SCS test server.
@return Return code from RScsTestSession::Connect.
@see CloseOomSession
*/
{
return sOomTestSession.Connect();
}
static void CloseOomSession()
/**
Closes the session which was opened by OpenOomSession.
@see OpenOomSession
@pre OpenOomSession was successfully opened by OpenOomSession.
*/
{
sOomTestSession.Close();
}
static void TestServerDeath()
{
test.Start(_L("TestServerDeath"));
test.Next(_L("Starting server without shutdown timer"));
// Create marker file to get server to run without a activity timeout
RFs fs;
TInt r = fs.Connect();
test(r == KErrNone);
(void) fs.Delete(KDisableScsTestServerTimeout());
RFile file;
r = file.Create(fs, KDisableScsTestServerTimeout(), EFileShareAny|EFileWrite);
test(r == KErrNone);
file.Close();
// Start server
RScsTestSession scsts;
r = scsts.Connect();
test(r == KErrNone);
test.Next(_L("Checking ShutdownServer is not allowed"));
r = scsts.ShutdownServer();
test(r == KErrNotSupported);
test.Next(_L("Doing an async call to check server does not attempt to restart the timer and crash"));
TPckgBuf<TInt> x1 = 1;
TRequestStatus rs1;
scsts.Treble(x1, rs1);
User::WaitForRequest(rs1);
test.Printf(_L("NukeServer - scstestserver.exe SHOULD panic\n"));
r = scsts.NukeServer();
test(r == KErrServerTerminated);
(void) fs.Delete(KDisableScsTestServerTimeout());
fs.Close();
scsts.Close();
test.End();
}
static void TestOpenClose()
/**
Attempt to connect to the server when supplying default,
lower, and higher version numbers. Attempt to open a session
in server-side OOM.
*/
{
test.Start(_L("TestOpenClose"));
TInt r;
RScsTestSession scsts;
// default version
r = scsts.Connect();
test(r == KErrNone);
scsts.Close();
// lower-than-supported version
TVersion v = ScsTestImpl::Version();
--v.iMajor;
r = scsts.Connect(v);
test(r == KErrNone);
scsts.Close();
// supported version (should be same as default)
++v.iMajor;
r = scsts.Connect(v);
test(r == KErrNone);
scsts.Close();
// greater-than-supported version
++v.iMajor;
r = scsts.Connect(v);
test(r == KErrNotSupported);
// test opening session when server running out of memory
r = sTestSession.Connect(); // required to send OOM commands
test(r == KErrNone);
RunOomTest(OpenOomSession, CloseOomSession);
sTestSession.Close();
#if 0
// Test closing server with an out standing request
r = scsts.Connect();
test(r == KErrNone);
test(r == KErrNone);
TPckgBuf<TInt> x1 = 1;
TRequestStatus rs1;
scsts.Treble(x1, rs1);
// Bypass the presession close
RSessionBase *session = &scsts;
session->Close();
User::WaitForRequest(rs1);
test(rs1 == KErrCancel);
#endif
test.End();
}
// -------- invalid session function --------
static void TestInvalidSessionFunction()
/**
Send an unrecognized SCS code and confirm that it is
rejected with KErrNotSupported. This tests the SCS
implementation.
Send an unrecognized function identifier to the session
and confirm that it is also rejected with KErrNotSupported.
This tests the test server session implementation.
*/
{
test.Start(_L("TestInvalidSessionFunction"));
TInt r;
r = sTestSession.SendCustomFunction(ScsImpl::EScsUnused);
test(r == KErrNotSupported);
r = sTestSession.SendCustomFunction(ScsImpl::ECallSessionFunc | ScsTestImpl::ESessUnused);
test(r == KErrNotSupported);
test.End();
}
// -------- synchronous session function --------
static void TestSyncSessionFunction()
/**
Send a recognized function to the SCS test session,
and confirm that it is executed correctly.
*/
{
test.Start(_L("TestSyncSessionFunction"));
TInt x = 3;
TInt r = sTestSession.Double(x);
test(r == KErrNone);
test(x == 6);
test.End();
}
// -------- asynchronous session function --------
static TInt LaunchSessionTrebleOom()
/**
Attempt to launch an asynchronous command on a session
under OOM conditions.
*/
{
TPckgBuf<TInt> x = 3;
TRequestStatus rs;
sTestSession.Treble(x, rs);
User::WaitForRequest(rs);
if (rs == KErrNone)
{
test(x() == 9);
}
return rs.Int();
}
static void TestAsyncInvalidDescPanic(RScsTestSession& aSession)
/**
Pass an invalid descriptor to the SCS test server.
The current thread should be panicked.
@param aSession Open session to SCS test server,
supplied by TestPanic.
@see TestPanic
*/
{
TDes8* nullDes8 = 0;
TRequestStatus rs;
aSession.Treble(*nullDes8, rs);
User::WaitForRequest(rs);
}
static void TestRequeueOutstandingPanic(RScsTestSession& aSession)
/**
Attempt to requeue an asynchronous request which is still
outstanding. The current thread should be panicked.
@param aSession Connected session to SCS test server.
@see TestPanic
*/
{
TPckgBuf<TInt> x1 = 1;
TRequestStatus rs1;
aSession.Treble(x1, rs1);
TPckgBuf<TInt> x2 = 2;
TRequestStatus rs2;
aSession.Treble(x2, rs2);
User::WaitForRequest(rs1, rs2);
User::WaitForRequest(rs1, rs2);
}
static void TestAsyncSessionFunction()
/**
Test asynchronous session-relative functions.
Launch a request and wait for it to complete.
Launch a request and cancel it.
Launch a request with an invalid descriptor.
Cancel a request which is not outstanding.
Launch a request in OOM.
Launch an asynchronous request on a session and wait
for it to complete.
*/
{
test.Start(_L("TestAsyncSessionFunction"));
TInt r;
LaunchSessionTrebleOom();
// test launching async request in OOM
RunOomTest(LaunchSessionTrebleOom, NULL);
// cancel async request
TPckgBuf<TInt> x = 3;
TRequestStatus rs;
sTestSession.Treble(x, rs);
sTestSession.CancelTreble();
User::WaitForRequest(rs);
test(rs == KErrCancel);
// bad descriptor panic
TestPanic(TestAsyncInvalidDescPanic, ScsImpl::EScsClBadDesc);
// safely cancel async request which is not queued
sTestSession.CancelTreble();
// panic if requeue outstanding request
TestPanic(TestRequeueOutstandingPanic, ScsImpl::EScsClAsyncAlreadyQueued);
// test outstanding request cancelled when session closed with RScsSessionBase::Close
RScsTestSession asyncSession;
r = asyncSession.Connect();
test(r == KErrNone);
x() = 4;
asyncSession.Treble(x, rs);
asyncSession.Close();
User::After(ScsTestImpl::KTrebleTimerDelayUs + KOneSecondUs);
test(rs == KErrCancel);
User::WaitForRequest(rs);
// test outstanding request not cancelled when session closed with RSessionBase::Close
r = asyncSession.Connect();
test(r == KErrNone);
x() = 4;
asyncSession.Treble(x, rs);
asyncSession.RSessionBase::Close();
User::After(ScsTestImpl::KTrebleTimerDelayUs + KOneSecondUs);
test(rs == KRequestPending); // client request not cancelled or completed
test.End();
}
// -------- open / close subsession --------
/**
This subsession handle is opened in OOM testing.
@see OpenOomSubsession
@see CloseOomSubsession
*/
RScsTestSubsession sOomSubsession;
static TInt OpenOomSubsession()
/**
Attempt to open a subsession in OOM.
@see CloseOomSubsession
@see RunOomTest
*/
{
return sOomSubsession.Create(sTestSession, 10);
}
static void CloseOomSubsession()
/**
Free the subsession which was opened in OOM.
@see OpenOomSubsession
@see RunOomTest
*/
{
sOomSubsession.Close();
}
static void TestOpenCloseSubsession()
/**
Test opening and closing a subsession, including
opening in OOM.
*/
{
test.Start(_L("TestOpenCloseSubsession"));
TInt r;
RScsTestSubsession ss;
r = ss.Create(sTestSession, 10);
test(r == KErrNone);
ss.Close();
// test creating in OOM
RunOomTest(OpenOomSubsession, CloseOomSubsession);
test.End();
}
// -------- invalid subsession function --------
static void TestInvalidSubsessionFunction()
/**
Pass an unrecognized function to a subession and test
the subsession handles it properly. This tests the SCS
test implementation, rather than the SCS itself.
*/
{
test.Start(_L("TestInvalidSubsessionFunction"));
TInt r = sTestSubsession.SendFunction(ScsTestImpl::ESubsessUnused);
test(r == KErrNotSupported);
test.End();
}
// -------- synchronous subsession function --------
static void TestBadHandlePanic(RScsTestSession& aSession)
/**
Call a subsession function passing in an invalid handle.
The current thread should be panicked.
@param aSession Open session to SCS test server,
supplied by TestPanic.
@see TestPanic
*/
{
RScsTestSubsession ss;
TInt r = ss.Create(aSession, 10);
test(r == KErrNone);
// modify the subsession handle
TUint8* addrOfHandle;
addrOfHandle = (TUint8*)&ss;
addrOfHandle += sizeof(RSessionBase);
*((TInt*)addrOfHandle) ^= ~0;
TInt x = 3;
r = ss.Quadruple(x); // should be panicked with EScsClBadHandle
}
static void TestSyncSubsessionFunction()
/**
Call a synchronous function on a subsession. This ensures
the request is routed to the subsession by the SCS. Also test
the SCS correctly handles an invalid subsession handle.
*/
{
test.Start(_L("TestSyncSubsessionFunction"));
TInt x = -1;
TInt r = sTestSubsession.Quadruple(x);
test(r == KErrNone);
test(x == 4 * KSubsessValue);
TestPanic(TestBadHandlePanic, ScsImpl::EScsClBadHandle);
test.End();
}
// -------- asynchronous subsession function --------
static TInt LaunchSsTrebleOom()
/**
Launch an asynchronous request on a subsession in OOM.
@see RunOomTest
*/
{
TPckgBuf<TInt> x = 3;
TRequestStatus rs;
sTestSubsession.Treble(x, rs);
User::WaitForRequest(rs);
if (rs == KErrNone)
{
test(x() == 9);
}
return rs.Int();
}
static void TestAsyncSubsessionFunction()
/**
Test queueing and cancelling asynchronous requests on a subsession.
*/
{
test.Start(_L("TestAsyncSubsessionFunction"));
TPckgBuf<TInt> x = 7;
TRequestStatus rs;
sTestSubsession.Treble(x, rs);
User::WaitForRequest(rs);
test(rs == KErrNone);
test(x() == 21);
// test launching async request in OOM
RunOomTest(LaunchSsTrebleOom, NULL);
// cancel async request
sTestSubsession.Treble(x, rs);
sTestSubsession.CancelTreble();
User::WaitForRequest(rs);
test(rs == KErrCancel);
// cancel when no outstanding request
sTestSubsession.CancelTreble();
test.End();
}
// -------- leak subsession --------
static void TestLeakSubsession()
/**
Test closing a session with a remaining subsession;
ensure server does not leak memory.
*/
{
test.Start(_L("TestLeakSubsession"));
TInt r;
sTestSession.SetServerHeapFail(KMaxTInt);
RScsTestSession s;
r = s.Connect();
test(r == KErrNone);
RScsTestSubsession ss;
r = ss.Create(s, KSubsessValue);
test(r == KErrNone);
s.Close();
LetServerRun(); // 100ms, let server clean up
sTestSession.ResetServerHeapFail();
test.End();
}
// -------- correct async requests cancelled --------
static void TestCorrectAsyncCancelled()
/**
Test the correct requests are cancelled.
*/
{
test.Start(_L("TestCorrectAsyncCancelled"));
TInt r;
RScsTestSession s1;
r = s1.Connect();
test(r == KErrNone);
RScsTestSubsession ss1a;
r = ss1a.Create(s1, KSubsessValue);
test(r == KErrNone);
RScsTestSubsession ss1b;
r = ss1b.Create(s1, KSubsessValue);
test(r == KErrNone);
RScsTestSession s2;
r = s2.Connect();
test(r == KErrNone);
RScsTestSubsession ss2a;
r = ss2a.Create(s2, KSubsessValue);
test(r == KErrNone);
TRequestStatus r1, r1a, r1b, r2, r2a;
TPckgBuf<TInt> i1(10), i1a(20), i1b(30), i2(40), i2a(50);
s1.Treble(i1, r1);
ss1a.Treble(i1a, r1a);
ss1b.Treble(i1b, r1b);
s2.Treble(i2, r2);
ss2a.Treble(i2a, r2a);
test(r1 == KRequestPending);
test(r1a == KRequestPending);
test(r1b == KRequestPending);
test(r2 == KRequestPending);
test(r2a == KRequestPending);
ss1a.CancelTreble(); // subsession doesn't affect parent or siblings
LetServerRun();
test(r1 == KRequestPending);
test(r1a == KErrCancel);
test(r1b == KRequestPending);
test(r2 == KRequestPending);
test(r2a == KRequestPending);
s2.CancelTreble(); // session doesn't affect child
LetServerRun();
test(r1 == KRequestPending);
test(r1a == KErrCancel);
test(r1b == KRequestPending);
test(r2 == KErrCancel);
test(r2a == KRequestPending);
ss2a.Close(); // close subsession cancels outstanding request
LetServerRun();
test(r1 == KRequestPending);
test(r1a == KErrCancel);
test(r1b == KRequestPending);
test(r2 == KErrCancel);
test(r2a == KErrCancel);
// consume pending signals
User::WaitForRequest(r1);
User::WaitForRequest(r1a);
User::WaitForRequest(r1b);
User::WaitForRequest(r2);
User::WaitForRequest(r2a);
test(r1 == KErrNone);
test(r1a == KErrCancel);
test(r1b == KErrNone);
test(r2 == KErrCancel);
test(r2a == KErrCancel);
s1.Close();
s2.Close();
test.End();
}
// -------- entrypoint --------
void MainL()
{
test.Title(_L("c:\\scstest.log"));
test.Start(_L(" @SYMTestCaseID:SEC-SCSTEST-0001 scstest "));
TestServerDeath();
TestOpenClose();
TInt r = sTestSession.Connect();
test(r == KErrNone);
TestInvalidSessionFunction();
TestSyncSessionFunction();
TestAsyncSessionFunction();
TestOpenCloseSubsession();
r = sTestSubsession.Create(sTestSession, KSubsessValue);
test(r == KErrNone);
TestInvalidSubsessionFunction();
TestSyncSubsessionFunction();
TestAsyncSubsessionFunction();
TestLeakSubsession();
TestCorrectAsyncCancelled();
sTestSubsession.Close();
sTestSession.ShutdownServer(); // Synchronous shutdown of server
test.End();
test.Close();
}
void PanicIfError(TInt r)
{
if(r != KErrNone)
{
User::Panic(_L("upstest failed: "), r);
}
}
TInt E32Main()
/**
Executable entrypoint establishes connection with SCS test server
and then invokes tests for each functional area.
*/
{
// disable lazy DLL unloading so kernel heap balances at end
RLoader l;
PanicIfError(l.Connect());
PanicIfError(l.CancelLazyDllUnload());
l.Close();
(void)test.Console();
__UHEAP_MARK;
__KHEAP_MARK;
// allocating a cleanup stack also installs it
CTrapCleanup* tc = CTrapCleanup::New();
if (tc == 0)
return KErrNoMemory;
TRAPD(err, MainL());
PanicIfError(err);
delete tc;
// The kernel appears to leave some memory allocated related to
// econs.dll (maybe some sort of internal cache), which is
// indirectly used by the test code, so we need to ignore this
// allocation to allow our test to pass...
User::__DbgMarkEnd(TRUE,1);
__UHEAP_MARKEND;
return KErrNone;
}
// End of file