diff -r 000000000000 -r bde4ae8d615e os/persistentdata/persistentstorage/sql/TEST/testexecute/SQLite/src/sqlfn.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/os/persistentdata/persistentstorage/sql/TEST/testexecute/SQLite/src/sqlfn.cpp Fri Jun 15 03:10:57 2012 +0200 @@ -0,0 +1,2365 @@ +// Copyright (c) 2006-2009 Nokia Corporation and/or its subsidiary(-ies). +// All rights reserved. +// This component and the accompanying materials are made available +// under the terms of "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: +// It would have been nice to create a new object every time we jump to +// a new config block but reporting (INFO_PRINTF and ERR_PRINTF) doesn't +// work from sub-objects. All in all the structure isn't what I'd like, +// perhaps I'm missing some TEF functionality which would get around this. +// Various bits of repetition may be removed into utility class(es). +// Some utility methods probably should go into utility class(es). +// Unimplemented 8-bit methods, e.g Exec8. +// +// + +#include "sqlfn.h" +#include "Te_SQL_SuiteDefs.h" +#include "common.h" + +// Contains code to perform functions on SQLite databases - what functions +// and in what order is determined by the content of the config (.ini) file. + +CSQLFnStep::~CSQLFnStep() +/** + * Destructor + */ + { + // Get rid of the RFs object.. Note this isn't set up in the constructor + // but in doTestStepL. + irfs.Close(); + + // Get rid of the semaphore objects. + isemA.Close(); + isemB.Close(); + + // Get rid of the hashes. These are originally set up in doTestStepL. + delete ierrhsh; + delete icoltypehsh; + delete iactionhsh; + delete icaphsh; + delete ipolhsh; + delete iobjhsh; + + // Get rid of the config item. + if(icfg)delete icfg; + } +CSQLFnStep::CSQLFnStep() + { + // Create a global semaphore to be used by all instances of this framework + // to be used for synchronising separate threads when 'CONCURRENT' is + // used. If it already exists, then perhaps another thread already has it + // which is fine - in that case just open it. + TInt err = isemA.CreateGlobal(_L("SQLiteSemA"), 0); + if(err == KErrAlreadyExists) + { + err = isemA.OpenGlobal(_L("SQLiteSemA")); + } + if(err != KErrNone) + { + INFO_PRINTF2(_L("Error %d creating semaphore"), err); + __ASSERT_ALWAYS(err == KErrNone, User::Invariant()); + } + + // + // Second semaphore require for DEF140385. + // + err = isemB.CreateGlobal(_L("SQLiteSemB"), 0); + if(err == KErrAlreadyExists) + { + err = isemB.OpenGlobal(_L("SQLiteSemB")); + } + if(err != KErrNone) + { + INFO_PRINTF2(_L("Error %d creating semaphoreB"), err); + __ASSERT_ALWAYS(err == KErrNone, User::Invariant()); + } + } +TVerdict CSQLFnStep::doTestStepPostambleL() + { + // Try to make sure that the database and statement resources have been + // properly closed (in case of problems). + isqlst.Close(); + isqldb.Close(); + return TestStepResult(); + } +TVerdict CSQLFnStep::doTestStepL() +/** + * @return - TVerdict code + * Override of base class pure virtual. Our implementation only gets called + * if the base class doTestStepPreambleL() did not leave. That being the case, + * the current test result value will be EPass. + */ + { + // Create the RFs object so we can talk to the file-system when necessary. + // Moved from the constructor to shut up leavescan. + User::LeaveIfError(irfs.Connect()); + irfs.ShareProtected(); + + // Make sure the database and statement objects get cleaned up.. + CleanupClosePushL(isqldb); + CleanupClosePushL(isqlst); + + // Make sure that the icfg member is definitely unset when we start. + icfg = NULL; + + // Get the hashes we use to associate words with numbers (e.g + // KErrNone with 0). If these fail due to lack of memory they will + // PANIC, which is fine. If we're that short of memory nothing is + // going to work anyway. + ierrhsh = new CSQLErrHash(); + icoltypehsh = new CSQLColTypeHash(); + iactionhsh = new CSQLTEFAction(); + icaphsh = new CSQLCapability(); + ipolhsh = new CSQLPolicy(); + iobjhsh = new CSQLObject(); + + // Set the test result to PASS to start with and call the main block.. + SetTestStepResult(EPass); + SQLDbStepL(ConfigSection()); + + // Clean up the database and statement objects. + CleanupStack::PopAndDestroy(2, &isqldb); + + return TestStepResult(); + } +// This is our 'main' function. It works out what method (e.g RSqlStatement:: +// Close) the user wants (based on the configuration file) and then calls +// the appropriate wrapper function which runs the wanted method and reports +// on any unexpected errors. +void CSQLFnStep::SQLDbStepL(const TPtrC& acfgblk) + { + _LIT(KTestFunction, "SQLDbStep"); + + /* + * Go through all of the actions defined in the configuration file + * acting on each. The counter will keep incrementing until we + * fail to find a config item called 'CreateNN', or 'OpenNN' etc. + * The two arrays hold Parameter and Column indices for use in + * any method that needs one. E.G.. + */ + TInt ended=0; + + iasync = i8bit = EFalse; + for(TInt count=0 ; ; count++) + { + TPtrC argument; + TInt whatfun=Efn_undefined; + for(TInt i=0 ; i < Efn_undefined ; i++) + { + // Construct something like 'ColumnInt37' + TBuf stfn(*(iactionhsh->GetStringFromNum(i))); + stfn.AppendNum(count); + + // Does it exist in the config file? If not try e.g 'ColumnReal37' + if(!GetStringFromConfig(acfgblk, stfn, argument)) + continue; + + whatfun = i; + if(whatfun == Ectrl_endblock) + ended = 1; + // The GetString was successful, so we drop out anyway. + break; + } + // If we hit an EndBlock marker or couldn't find any keyword with + // the current counter number then drop out. + if((whatfun == Efn_undefined) || (whatfun == Ectrl_endblock)) + break; + + // If there's a comma in the argument, split it up. We do + // this here (rather than, more logically, in the called methods) + // because we'd end up repeating the 'CommaSeparated' call in + // all of the wrapper methods. Also, we need the indices for + // Column and Parameter index resolution. + TInt arg1, arg2; + TPtrC arg3; + CommaSeparated(argument, arg1, arg2); + CommaSeparated(argument, arg1, arg3); + + TInt err=0; + switch(whatfun) + { + case Efn_nop: break; + // First the RSqlDatabase methods... + case Efn_create: + Create(argument, acfgblk, count); + break; + case Efn_createl: + CreateL_(argument, acfgblk, count); + break; + case Efn_createsp: + CreateSP(argument, acfgblk, count); + break; + case Efn_open: + Open(argument, acfgblk, count); + break; + case Efn_openl: + OpenL_(argument, acfgblk, count); + break; + case Efn_attach: + Attach(argument, acfgblk, count); + break; + case Efn_detach: + Detach(argument, acfgblk, count); + break; + case Efn_copy: + Copy(argument, acfgblk, count); + break; + case Efn_close: + Close(); + break; + case Efn_delete: + Delete(argument, acfgblk, count); + break; + case Efn_lasterrormessage: + LastErrorMessage(argument); + break; + case Efn_exec: + Exec(argument, acfgblk, count); + break; + case Efn_setisolationlevel: + SetIsolationLevel(argument, acfgblk, count); + break; + case Efn_reservedrivespace: + ReserveDriveSpace(arg1, acfgblk, count); + break; + case Efn_freereservedspace: + FreeReservedSpace(); + break; + case Efn_getreserveaccess: + GetReserveAccess(acfgblk, count); + break; + case Efn_releasereserveaccess: + ReleaseReserveAccess(); + break; + + // Now the RSqlStatement methods... + case Erstmt_prepare: + Prepare(argument, acfgblk, count); + break; + case Erstmt_preparel: + PrepareL_(argument, acfgblk, count); + break; + case Erstmt_close: + Close(1); + break; + case Erstmt_atrow: + AtRow(argument); + break; + case Erstmt_reset: + err = isqlst.Reset(); + ReportOnError(KTestFunction, _L("Reset"), acfgblk, + count, err); + break; + case Erstmt_exec: + { + TBuf apiname(_L("st_exec")); + if(!iasync) + err = isqlst.Exec(); + else + { + TChar ch = 'A'; + apiname.Append(ch); + TRequestStatus trs; + isqlst.Exec(trs); + User::WaitForRequest(trs); + err = trs.Int(); + } + ReportOnError(KTestFunction, apiname, + acfgblk, count, err); + } + break; + case Erstmt_next: + Next(argument, acfgblk, count); + break; + case Erstmt_paramindex: + { + TInt pidx = ParamIndex(argument, acfgblk, count); + // The test designer will have to remember how many + // param indices have been stuck in this array.. + if(pidx >= 0)ipidxs.Append(pidx); + } + break; + case Erstmt_colindex: + { + TInt cidx = ColumnIndex(argument, acfgblk, count); + // The test designer will have to remember how many + // column indices have been stuck in this array.. + if(cidx >= 0)icidxs.Append(cidx); + } + break; + case Erstmt_coltype: + // ColumnType needs the ColumnIndex (the last arg) + // and also the expected result, which it will get from + // the config file. We have to deal with the ColumnIndex + // here because it lives in our scope, not that of the + // method we're calling.. + // The test designer will have to remember how many + // column indices have been stuck in this array.. + ColumnType(icidxs[arg1], arg3); + break; + case Erstmt_colsize: + ColumnSize(icidxs[arg1], arg2); + break; + case Erstmt_bindnull: + BindNull(ipidxs[arg1], acfgblk, count); + break; + case Erstmt_bindint: + BindInt(ipidxs[arg1], arg2, acfgblk, count); + break; + case Erstmt_bindint64: + BindInt64(ipidxs[arg1], arg3, acfgblk, count); + break; + case Erstmt_bindreal: + { + TLex tl = arg3; + TReal tr; + tl.Val(tr); + BindReal(ipidxs[arg1], tr, acfgblk, count); + } + break; + case Erstmt_bindtext: + BindText(ipidxs[arg1], arg3, acfgblk, count); + break; + case Erstmt_bindbigtext: + // Not an RSqlStatement method, but calls BindText + // after reading from a file. + BindBigTextL(ipidxs[arg1], arg3, acfgblk, count); + break; + case Erstmt_bindbinary: + BindBinaryL(ipidxs[arg1], arg3, acfgblk, count); + break; + case Erstmt_isnull: + IsNull(icidxs[arg1], arg3); + break; + case Erstmt_colint: + ColumnInt(icidxs[arg1], arg2); + break; + case Erstmt_colint64: + ColumnInt64(icidxs[arg1], arg3); + break; + case Erstmt_colreal: + { + TLex tl = arg3; + TReal tr2; + tl.Val(tr2); + ColumnReal(icidxs[arg1], tr2); + } + break; + case Erstmt_coltextL: + ColumnTextL(icidxs[arg1], arg3, acfgblk, count); + break; + case Erstmt_coltextP: + ColumnTextPL(icidxs[arg1], arg3, acfgblk, count); + break; + case Erstmt_coltextD: + ColumnTextDL(icidxs[arg1], arg3, acfgblk, count); + break; + case Erstmt_colbinL: + ColumnBinaryL(icidxs[arg1], arg3, acfgblk, count); + break; + case Erstmt_colbinP: + ColumnBinaryPL(icidxs[arg1], arg3, acfgblk, count); + break; + case Erstmt_colbinD: + ColumnBinaryDL(icidxs[arg1], arg3, acfgblk, count); + break; + + case Esp_create: + SPCreate(acfgblk, count); + break; + case Esp_createl: + SPCreate(argument, acfgblk, count); + break; + case Esp_close: + SPClose(); + break; + case Esp_setdbpolicy: + SPSetDBPolicy(argument, acfgblk, count); + break; + case Esp_setpolicy: + SPSetPolicy(argument, acfgblk, count); + break; + case Esp_externalizel: + SPExternalize(argument, acfgblk, count); + break; + case Esp_internalizel: + SPInternalize(argument, acfgblk, count); + break; + + case Estreamwrite_bindtext: + SWBindTextL(ipidxs[arg1], arg3, acfgblk, count); + break; + case Estreamwrite_bindbinary: + SWBindBinaryL(ipidxs[arg1], arg3, acfgblk, count); + break; + case Estreamread_columntext: + SRColumnTextL(icidxs[arg1], arg3, acfgblk, count); + break; + case Estreamread_columnbinary: + SRColumnBinaryL(icidxs[arg1], arg3, acfgblk, count); + break; + case Edefineconfig: + { + if(icfg) + delete icfg; + TInt len = argument.Length(); + if(len) + { + // At the time of writing, configuration strings + // are limited to 255 bytes. + TBuf8<256> arg; + arg.Copy(argument); + icfg = new TPtrC8(arg); + } + else + icfg = NULL; + } + break; + + // Actions that aren't direct method calls.. + case Ectrl_newblock: + // Continue executing from another configuration + // block. Obviously this restarts the step + // counter at zero. It's unfortunate that we can't + // create a new test object here, that would be + // so much neater. But logging doesn't work in + // sub-objects (it could be bodged around but it + // really would be a bodge). A shame, we could have + // lots of member vars holding all this junk we're + // passing around. Note that because we don't create + // a new object we are playing with the same + // RSqlDatabase and RSqlStatement objects. + SQLDbStepL(argument); + break; + case Ectrl_function: + // Pure virtual. Lives elsewhere.. + ResolveTestFunctionL(acfgblk, count, argument); + break; + + case Ectrl_waitA: + // Wait for the isem member semaphore to receive + // arg1 signals for this thread. Obviously this assumes + // there's another thread that's going to execute + // a 'Signal' at some point. + for(TInt ii=0 ; ii apiname(KTestFunction); + TInt rc; + if(i8bit == EFalse) + { + if(!iasync) + rc = isqldb.Exec(arg); + else + { + TChar ch = 'A'; + apiname.Append(ch); + TRequestStatus trs; + isqldb.Exec(arg, trs); + User::WaitForRequest(trs); + rc = trs.Int(); + } + } + else + { + apiname.AppendNum(8); + RBuf8 b8; + b8.Create(arg.Length()); + b8.Copy(arg); + if(!iasync) + rc = isqldb.Exec(b8); + else + { + TChar ch = 'A'; + apiname.Append(ch); + TRequestStatus trs; + isqldb.Exec(b8, trs); + User::WaitForRequest(trs); + rc = trs.Int(); + } + b8.Close(); + } + ReportOnError(KTestFunction, apiname, acfgblk, acnnum, rc); + return; + } +void CSQLFnStep::SetIsolationLevel(const TPtrC& arg, + const TDesC &acfgblk, TInt acnnum) + { + _LIT(KTestFunction, "SetIsolationLevel"); + // Get the expected error code.. + TPtrC experrS; + TInt experr = ActionNoToErrEnum(acfgblk, acnnum, experrS); + + INFO_PRINTF2(_L("SetIsolationLevel: %S"), &arg); + RSqlDatabase::TIsolationLevel sil; + if(arg == _L("EReadUncommitted")) + sil = RSqlDatabase::EReadUncommitted; + else if(arg == _L("EReadCommitted")) + sil = RSqlDatabase::EReadCommitted; + else if(arg == _L("ERepeatableRead")) + sil = RSqlDatabase::ERepeatableRead; + else if(arg == _L("ESerializable")) + sil = RSqlDatabase::ESerializable; + else + { + SetTestStepResult(EFail); + INFO_PRINTF1(HTML_RED); + ERR_PRINTF3(_L("%S: Unrecognized TIsolationLevel '%S'"), + &KTestFunction, &arg); + INFO_PRINTF1(HTML_COLOUR_OFF); + return; + } + + TInt rc = isqldb.SetIsolationLevel(sil); + TPtrC err; + ErrEnumToString(rc,err); + if(rc != experr) + { + SetTestStepResult(EFail); + INFO_PRINTF1(HTML_RED); + ERR_PRINTF3(_L("Unexpected SetIsolationLevel error %d/%S"), rc, &err); + TPtrC lem = isqldb.LastErrorMessage(); + ERR_PRINTF2(_L(" - Last Error Message: %S"), &lem); + INFO_PRINTF1(HTML_COLOUR_OFF); + return; + } + else + INFO_PRINTF2(_L("SetIsolation level, got error %S as expected"), &err); + return; + } + +void CSQLFnStep::ReserveDriveSpace(TInt ares, + const TDesC& acfgblk, const TInt acnnum) + { + _LIT(KTestFunction, "ReserveDriveSpace"); + + // Try to reserve space.. + TInt rc = isqldb.ReserveDriveSpace(ares); + ReportOnError(KTestFunction, KTestFunction, acfgblk, acnnum, rc); + return; + } +void CSQLFnStep::FreeReservedSpace() + { +// _LIT(KTestFunction, "FreeReservedSpace"); + isqldb.FreeReservedSpace(); + return; + } +void CSQLFnStep::GetReserveAccess(const TDesC& acfgblk, + const TInt acnnum) + { + _LIT(KTestFunction, "GetReserveAccess"); + + // Try to reserve space.. + TInt rc = isqldb.GetReserveAccess(); + ReportOnError(KTestFunction, KTestFunction, acfgblk, acnnum, rc); + return; + } +void CSQLFnStep::ReleaseReserveAccess() + { +// _LIT(KTestFunction, "ReleaseReserveSpace"); + isqldb.ReleaseReserveAccess(); + return; + } + +// ----------Methods to exercise RSqlStatement methods ------------------------ +// +// Execute a Close on the current RSqlStatement. This also clears out the +// arrays of RBufs and RBuf8s which are used for BindText and +// BindBinary (just a way of keeping the buffers in scope until the +// Exec/Close happens) and loses all of the ParameterIndex and ColumnIndex's. +// The 'TInt' argument is just to differentiate between the RSqlDatabase +// Close wrapper, and this RSqlStatement Close wrapper. +void CSQLFnStep::Close(TInt) + { +// _LIT(KTestFunction, "St_Close"); + + // Close the RSqlStatement. + isqlst.Close(); + + // Empty the arrays where we keep references to BindXXX buffers, + // closing those buffers as we go. + for(TInt count = iBindRBufarr.Count() - 1 ; count >= 0; count--) + { + iBindRBufarr[count].Close(); + iBindRBufarr.Remove(count); + } + for(TInt count = iBindRBuf8arr.Count() - 1 ; count >= 0 ; count--) + { + iBindRBuf8arr[count].Close(); + iBindRBuf8arr.Remove(count); + } + if((iBindRBuf8arr.Count() != 0) || (iBindRBufarr.Count() != 0)) + { + User::Panic(_L("RBuf arrays not empty"), 512); + } + + // Empty the ParameterIndex and ColumnIndex arrays. + while(ipidxs.Count()) ipidxs.Remove(0); + while(icidxs.Count()) icidxs.Remove(0); + + return; + } +void CSQLFnStep::Next(TPtrC& arg, + const TDesC &acfgblk, TInt acnnum=-1) + { + _LIT(KTestFunction, "Next"); + TInt rc = isqlst.Next(); + + // If arg is not zero length it will be KSqlAtEnd/KSqlAtRow, turn that + // into the enumeration. + if(arg.Length()) + { + TInt expn = ErrStringToEnum(arg); + TPtrC errS; + ErrEnumToString(rc, errS); // Convert the actual rc to a string. + if(expn != rc) + { + SetTestStepResult(EFail); + INFO_PRINTF1(HTML_RED); + ERR_PRINTF7(_L("%S/%S: Got %S/%d, expected %S/%d"), &KTestFunction, + &acfgblk, &errS, rc, &arg, expn ); + INFO_PRINTF1(HTML_COLOUR_OFF); + } + } + ReportOnError(KTestFunction, _L("Next"), acfgblk, acnnum, rc); + return; + } + +// Call the RSqlStatement method 'AtRow'. This returns a boolean. This +// method expects the config file to contain a line resembling +// 'AtRow57=false'. The return value is checked against the config +// value and error/info is reported. +void CSQLFnStep::AtRow(const TPtrC &arg) + + { + _LIT(KTestFunction, "AtRow"); + TBuf<8> atrres(arg); + atrres.LowerCase(); + TBool expected = EFalse; + if(atrres == _L("false")) + expected = EFalse; + else if(atrres == _L("true")) + expected = ETrue; + else + { + SetTestStepResult(EFail); + INFO_PRINTF1(HTML_RED); + ERR_PRINTF3(_L("%S: expected config item to be true/false, got %S"), + &KTestFunction, &arg); + INFO_PRINTF1(HTML_COLOUR_OFF); + } + TBool atr = isqlst.AtRow(); + if(atr != expected) + { + SetTestStepResult(EFail); + INFO_PRINTF1(HTML_RED); + ERR_PRINTF4(_L("%S: expected AtRow to return %S, got %d"), + &KTestFunction, &atrres, atr); + INFO_PRINTF1(HTML_COLOUR_OFF); + return; + } + else + { + INFO_PRINTF1(HTML_GREEN); + INFO_PRINTF3(_L("%S: Got expected result, %S"), &KTestFunction, &atrres); + INFO_PRINTF1(HTML_COLOUR_OFF); + } + return; + } +// Call the RSqlStatement method 'Prepare'. This returns an int. This +// method expects the config file to contain a line resembling +// 'Prepare43=Create Table tbl3(f1 etc)'. The return value is checked +// against the expected error (in ReportOnError). +void CSQLFnStep::Prepare(const TPtrC &arg, + const TDesC &acfgblk, TInt acnnum=-1) + { + _LIT(KTestFunction, "Prepare"); + INFO_PRINTF3(_L("%S: Prepare command is %S"), &KTestFunction, &arg); + + TInt rc; + if(i8bit == EFalse) + rc = isqlst.Prepare(isqldb, arg); + else + { + RBuf8 b8; + b8.Create(arg.Length()); + b8.Copy(arg); + rc = isqlst.Prepare(isqldb, b8); + b8.Close(); + } + ReportOnError(KTestFunction, KTestFunction, acfgblk, acnnum, rc); + } +void CSQLFnStep::PrepareL_(const TPtrC &arg, + const TDesC &acfgblk, TInt acnnum=-1) + { + _LIT(KTestFunction, "PrepareL"); + INFO_PRINTF3(_L("%S: PrepareL command is %S"), &KTestFunction, &arg); + + TInt rc=KErrNone; + if(i8bit == EFalse) + { + TRAP(rc, isqlst.PrepareL(isqldb, arg)); + } + else + { + RBuf8 b8; + b8.Create(arg.Length()); + b8.Copy(arg); + TRAP(rc, isqlst.PrepareL(isqldb, b8)); + b8.Close(); + } + ReportOnError(KTestFunction, KTestFunction, acfgblk, acnnum, rc); + } +// Call the RSqlStatement method 'ParamIndex'. This returns an int. This +// method expects the config file to contain a line resembling +// 'ParamIndex12=:Frog'. The return value is returned. +TInt CSQLFnStep::ParamIndex(const TDesC &arg, + const TDesC &acfgblk, TInt acnnum=-1) + { + _LIT(KTestFunction, "ParameterIndex"); + + // If the test specifies ':?' was the parameter index, we'll assume that + // ':?' was given in the SELECT (a nameless parameter), this always gives + // '1' for the paramIndex. + if(arg == _L(":?")) + return 1; + + // If arg resembles '23,*explicit*', then return the leading integer. + // This is so we can call BindEtc with bad values for PANIC testing + // or otherwise generate a specific parameter index. + TInt pidx=0; + TPtrC rhs; + CommaSeparated(arg, pidx, rhs); + if(rhs == _L("*explicit*")) + { + INFO_PRINTF3(_L("%S: Returning explicit Parameter Index %d"), + &KTestFunction, pidx); + return pidx; + } + + // Ok, run ParameterIndex. + pidx = isqlst.ParameterIndex(arg); + + // ParameterIndex returns a non-negative integer on success. If the + // return is negative, we have a problem. We cannot know what the + // return is if the operation has succeeded, so checking the error + // code is limited to required errors, i.e < 0. + // ReportOnError will set test result to failure if the error doesn't + // match our expected error. + if(pidx < 0) + ReportOnError(KTestFunction, _L("ParameterIndex"), + acfgblk, acnnum, pidx); + return pidx; + } +// Call the RSqlStatement method 'ColumnIndex'. This returns an int. This +// method expects the config file to contain a line resembling +// 'ColumnIndex12=Fld3'. The return value is returned. +TInt CSQLFnStep::ColumnIndex(const TPtrC& arg, + const TDesC &acfgblk, TInt acnnum=-1) + { + _LIT(KTestFunction, "ColumnIndex"); + + // If no column is specified then return zero - this may be necessary + // if the test is for example counting lines in a table ... + // >select count(*) from mytbl; + // In this case obviously there is no namable column, but the Api just + // requires that the index for ColumnInt will be zero. + if(arg.Length() == 0) return 0; + + // If arg resembles '23,*explicit*', then return the leading integer. + // This is so we can call ColumnEtc with bad values for PANIC testing + // or otherwise generate a specific parameter index. + TInt colIndex=0; + TPtrC rhs; + CommaSeparated(arg, colIndex, rhs); + if(rhs == _L("*explicit*")) + { + INFO_PRINTF3(_L("%S: Returning explicit Column Index %d"), + &KTestFunction, colIndex); + return colIndex; + } + colIndex = isqlst.ColumnIndex(arg); + // ColumnIndex returns a non-negative integer on success. If the + // return is negative, we have a problem. We cannot know what the + // return is if the operation has succeeded, so checking the error + // code is limited to required errors, i.e < 0. + // ReportOnError will set test result to failure if the error doesn't + // match our expected error. + if(colIndex < 0) + ReportOnError(KTestFunction, _L("ColumnIndex"), + acfgblk, acnnum, colIndex); + return colIndex; + } +// Call the RSqlStatement method 'ColumnType'. +void CSQLFnStep::ColumnType(const TInt &acidx, const TPtrC &aexp) + + { + _LIT(KTestFunction, "ColumnType"); + TSqlColumnType gottype = isqlst.ColumnType(acidx); + TPtrC got(SqlColumnTypeToString(gottype)); + + if((aexp.Length() ==0) || (StringToSqlColumnType(aexp)==gottype)) + { + INFO_PRINTF1(HTML_GREEN); + INFO_PRINTF3(_L("%S: Got Column type %S"), &KTestFunction, &got); + INFO_PRINTF1(HTML_COLOUR_OFF); + return; + } + + // If the expected type hasn't been specified then just display what + // we've got. + INFO_PRINTF1(HTML_RED); + ERR_PRINTF4(_L("%S: Got Column type %S, expected %S"), + &KTestFunction, &got, &aexp); + SetTestStepResult(EFail); + INFO_PRINTF1(HTML_COLOUR_OFF); + return; + } +// Call the RSqlStatement method 'ColumnSize'. Check it against the +// expected size specified in the config file. +void CSQLFnStep::ColumnSize(const TInt& acolidx, const TInt &axexp) + { + _LIT(KTestFunction, "ColumnSize"); + TInt csize = isqlst.ColumnSize(acolidx); + + if((axexp != -1) && (axexp != csize)) + { + INFO_PRINTF1(HTML_RED); + SetTestStepResult(EFail); + ERR_PRINTF4(_L("%S: Got Column size %d, expected %d"), + &KTestFunction, csize, axexp); + INFO_PRINTF1(HTML_COLOUR_OFF); + return; + } + // If colsize is -1 display what we have. + INFO_PRINTF3(_L("%S: Got Column size %d"), &KTestFunction, csize); + + return; + } +// Onto the Bind methods... +void CSQLFnStep::BindNull(const TInt& apidx, + const TDesC &acfgblk, const TInt acnnum=-1) + { + _LIT(KTestFunction, "BindNull"); + + TInt err = isqlst.BindNull(apidx); + ReportOnError(KTestFunction, _L("BindNull"), acfgblk, acnnum, err); + + return; + } +void CSQLFnStep::BindInt(const TInt& apidx, const TInt& atob, + const TDesC &acfgblk, const TInt acnnum=-1) + { + _LIT(KTestFunction, "BindInt"); + + TInt err = isqlst.BindInt(apidx, atob); + ReportOnError(KTestFunction, _L("BindInt"), acfgblk, acnnum, err); + + return; + } +void CSQLFnStep::BindInt64(const TInt& apidx, const TPtrC& atob, + const TDesC &acfgblk, const TInt acnnum=-1) + { + _LIT(KTestFunction, "BindInt64"); + TInt64 bind; + TLex tl = atob; + tl.Val(bind); + TInt err = isqlst.BindInt64(apidx, bind); + ReportOnError(KTestFunction, _L("BindInt64"), acfgblk, acnnum, err); + + return; + } +void CSQLFnStep::BindReal(const TInt& apidx, const TReal& areal, + const TDesC &acfgblk, const TInt acnnum=-1) + { + _LIT(KTestFunction, "BindReal"); + TInt err = isqlst.BindReal(apidx, areal); + ReportOnError(KTestFunction, _L("BindReal"), acfgblk, acnnum, err); + + return; + } +// BindText from the config line... +// May be modified to return a ref which we can keep on the +// stack in the main loop. Then, when we hit a 'Next' that can be cleared. +// This is necessary because there are scoping problems with text and +// binarys - the SQL code expects buffers to remain in scope until +// the Exec/Next. +void CSQLFnStep::BindText(const TInt& apidx, const TPtrC& atxt, + const TDesC &acfgblk, const TInt acnnum=-1) + { + _LIT(KTestFunction, "BindText"); + TInt err = isqlst.BindText(apidx, atxt); + ReportOnError(KTestFunction, _L("BindText"), acfgblk, acnnum, err); + + return; + } +// An additional method to let us bind more than one line of text from +// a config file... +// If the Bind is successful the buffer which has been bound is appended +// to the 'iBindRBufarr' array. This is necessary to keep it in scope until +// the 'Next'/'Exec' actions. +void CSQLFnStep::BindBigTextL(const TInt& apidx, const TPtrC& arg, + const TDesC &acfgblk, const TInt acnnum=-1) + { + _LIT(KTestFunction, "BindBigText"); + RBuf buf; + + TInt fsize = FileSize(arg); + if(fsize < 0) return; // This will have reported an error if necessary. + +#ifdef _UNICODE + fsize >>= 1; // We're assuming here that one character is two bytes.. +#endif + + // Create a buffer big enough for the text in the file. + TInt err = buf.Create(fsize); + if(err != KErrNone) + { + SetTestStepResult(EFail); + INFO_PRINTF1(HTML_RED); + ERR_PRINTF3(_L("%S: Can't allocate file buffer %S"), &KTestFunction, &arg); + INFO_PRINTF1(HTML_COLOUR_OFF); + return; + } + // Don't push buf onto the cleanup stack. We'll keep a reference to it + // which get removed on RSqlStatement::Reset. + + // Use an RFileReadStream because we need to worry about characters, + // not just bytes. + RFileReadStream rflrs; + if(rflrs.Open(irfs, arg, EFileRead) != KErrNone) + { + SetTestStepResult(EFail); + INFO_PRINTF1(HTML_RED); + ERR_PRINTF3(_L("%S: Can't open file %S"), &KTestFunction, &arg); + buf.Close(); + INFO_PRINTF1(HTML_COLOUR_OFF); + return; + } + CleanupClosePushL(rflrs); + rflrs.ReadL(buf); + + // Do the bind... + err = isqlst.BindText(apidx, buf); + ReportOnError(KTestFunction, _L("BindText"), acfgblk, acnnum, err); + // Drop out if it failed. + if(err != KErrNone) + { + CleanupStack::PopAndDestroy(1, &rflrs); + buf.Close(); + return; + } + CleanupStack::PopAndDestroy(1, &rflrs); + + // Tack the buffer onto the internal array. This keeps it in + // scope until a RSqlStatement::Close is performed when it will get + // destroyed. + iBindRBufarr.Append(buf); + + return; + } +// If the Bind is successful the buffer which has been bound is appended +// to the 'iBindRBuf8arr' array. This is necessary to keep it in scope until +// the 'Next'/'Exec' actions. +void CSQLFnStep::BindBinaryL(const TInt& apidx, const TDesC& arg, + const TDesC &acfgblk, const TInt acnnum) + { + _LIT(KTestFunction, "BindBinary"); + TInt fsize = FileSize(arg); + if(fsize < 0) + { + SetTestStepResult(EFail); + INFO_PRINTF1(HTML_RED); + ERR_PRINTF3(_L("%S: Can't find file %S"), &KTestFunction, &arg); + INFO_PRINTF1(HTML_COLOUR_OFF); + return; + } + // Create the buffer we're going to bind from. We'll keep a reference + // to this so it doesn't go out of scope (it mustn't + // until Exec/Reset) so don't put it on the cleanup stack. + RBuf8 ap; + TInt err = ap.Create(fsize); + ReportOnError(KTestFunction, _L("BufferCreate"), acfgblk, acnnum, err); + if(err != KErrNone) return; + + // Now open the file specified in the argument. + RFile file; + TFileName fn = arg; + err = file.Open(irfs, fn, 0); + ReportOnError(KTestFunction, _L("FileOpen"), acfgblk, acnnum, err); + if(err != KErrNone) + { + ap.Close(); + return; + } + CleanupClosePushL(file); + + // Attempt to read from the file. + err = file.Read(ap, fsize); + ReportOnError(KTestFunction, _L("FileRead"), acfgblk, acnnum, err); + if(err != KErrNone) + { + CleanupStack::PopAndDestroy(1, &file); + ap.Close(); + return; + } + + // Do the bind... + err = isqlst.BindBinary(apidx, ap); + ReportOnError(KTestFunction, _L("BindBinary"), acfgblk, acnnum, err); + + // Drop out if it failed. + if(err != KErrNone) + { + CleanupStack::PopAndDestroy(1, &file); + ap.Close(); + return; + } + + CleanupStack::PopAndDestroy(1, &file); + + // Ok things seemed to have worked. Tack the buffer onto our internal + // RBuf8 array to keep it in scope. It will finally get trashed when + // we do the next RSqlStatement::Reset. + iBindRBuf8arr.Append(ap); + + return; + } +void CSQLFnStep::IsNull(const TInt& apidx, const TPtrC& atxt) + { + _LIT(KTestFunction, "IsNull"); + TBuf<8> isn(atxt); + isn.LowerCase(); + TBool expected; + if(isn == _L("false")) + expected = EFalse; + else if(isn == _L("true")) + expected = ETrue; + else + { + SetTestStepResult(EFail); + INFO_PRINTF1(HTML_RED); + ERR_PRINTF3(_L("%S: expected argument item to be true/false, got %S"), + &KTestFunction, &atxt); + INFO_PRINTF1(HTML_COLOUR_OFF); + return; + } + if(isqlst.IsNull(apidx) != expected) + { + SetTestStepResult(EFail); + INFO_PRINTF1(HTML_RED); + ERR_PRINTF3(_L("%S: expected IsNull to return %S"), &KTestFunction, + &atxt); + INFO_PRINTF1(HTML_COLOUR_OFF); + return; + } + else + { + INFO_PRINTF1(HTML_GREEN); + INFO_PRINTF3(_L("%S: Got expected result, %S"), &KTestFunction, &atxt); + INFO_PRINTF1(HTML_COLOUR_OFF); + } + return; + } +void CSQLFnStep::ColumnInt(const TInt& acidx, const TInt& aint) + { + _LIT(KTestFunction, "ColumnInt"); + + TInt got = isqlst.ColumnInt(acidx); + if(got != aint) + { + SetTestStepResult(EFail); + INFO_PRINTF1(HTML_RED); + ERR_PRINTF4(_L("%S: expected ColumnInt to return %d, got %d"), + &KTestFunction, aint, got); + INFO_PRINTF1(HTML_COLOUR_OFF); + return; + } + else + { + INFO_PRINTF1(HTML_GREEN); + INFO_PRINTF3(_L("%S: Got expected result, %d"), &KTestFunction, + got); + INFO_PRINTF1(HTML_COLOUR_OFF); + } + return; + } +void CSQLFnStep::ColumnInt64(const TInt& acidx, const TPtrC& aintS) + { + _LIT(KTestFunction, "ColumnInt64"); + + TLex tl(aintS); + TInt64 aint; + tl.Val(aint); + TInt64 got = isqlst.ColumnInt64(acidx); + if(got != aint) + { + SetTestStepResult(EFail); + INFO_PRINTF1(HTML_RED); + ERR_PRINTF3(_L("%S: expected ColumnInt to return %S"), + &KTestFunction, &aint); + INFO_PRINTF1(HTML_COLOUR_OFF); + return; + } + else + { + INFO_PRINTF1(HTML_GREEN); + INFO_PRINTF3(_L("%S: Got expected result, %ld"), &KTestFunction, + got); + INFO_PRINTF1(HTML_COLOUR_OFF); + } + return; + } +void CSQLFnStep::ColumnReal(const TInt& acidx, const TReal& areal) + { + _LIT(KTestFunction, "ColumnReal"); + + TReal got = isqlst.ColumnReal(acidx); + if(got != areal) + { + SetTestStepResult(EFail); + INFO_PRINTF1(HTML_RED); + ERR_PRINTF4(_L("%S: expected ColumnReal to return %f, got %f"), + &KTestFunction, areal, got); + INFO_PRINTF1(HTML_COLOUR_OFF); + return; + } + else + { + INFO_PRINTF1(HTML_GREEN); + INFO_PRINTF3(_L("%S: Got expected result, %f"), &KTestFunction, got); + INFO_PRINTF1(HTML_COLOUR_OFF); + } + return; + } +void CSQLFnStep::ColumnTextL(const TInt& acidx, const TPtrC& atxt, + const TDesC &acfgblk, const TInt acnnum=-1) + { + _LIT(KTestFunction, "ColumnTextL"); + + TPtrC got = isqlst.ColumnTextL(acidx); + TInt err; + + // First the simplest, does the text match the config text? + if(got == atxt) + { + INFO_PRINTF1(HTML_GREEN); + INFO_PRINTF3(_L("%S: Got expected result, %S"), &KTestFunction, + &got); + INFO_PRINTF1(HTML_COLOUR_OFF); + return; + } + + // Perhaps 'atxt' is a file, CompareTextAgainstFile will + // return KErrNotFound if it can't find the file. + if((err = CompareTextAgainstFileL(got, atxt)) == KErrNone) + { + INFO_PRINTF1(HTML_GREEN); + INFO_PRINTF3(_L("%S: Text match with file %S"), &KTestFunction, + &atxt); + INFO_PRINTF1(HTML_COLOUR_OFF); + return; + } + + ReportOnError(KTestFunction, _L("ColumnTextL"), acfgblk, acnnum, err); + + return; + } +void CSQLFnStep::ColumnTextPL(const TInt& acidx, const TPtrC &arg, + const TDesC& acfgblk, const TInt acnnum=-1) + { + _LIT(KTestFunction, "ColumnTextP"); + + TPtrC got; + TInt err = isqlst.ColumnText(acidx, got); + ReportOnError(KTestFunction, _L("ColumnTextP"), acfgblk, acnnum, err); + + // First the simplest, does the text match the config text? + if(got == arg) + { + INFO_PRINTF1(HTML_GREEN); + INFO_PRINTF3(_L("%S: Got expected result, %S"), &KTestFunction, + &got); + INFO_PRINTF1(HTML_COLOUR_OFF); + return; + } + + // Perhaps 'arg' is a file, CompareTextAgainstFile will + // return KErrNotFound if it can't find the file. + if((err = CompareTextAgainstFileL(got, arg)) == KErrNone) + { + INFO_PRINTF1(HTML_GREEN); + INFO_PRINTF3(_L("%S: Text match with file %S"), &KTestFunction, + &arg); + INFO_PRINTF1(HTML_COLOUR_OFF); + return; + } + + ReportOnError(KTestFunction, _L("ColumnTextP"), acfgblk, acnnum, err); + } +void CSQLFnStep::ColumnTextDL(const TInt& acidx, const TPtrC &arg, + const TDesC& acfgblk, const TInt acnnum=-1) + { + _LIT(KTestFunction, "ColumnTextD"); +// Masses of duplication.. We should have a common method to get around +// this, but perhaps when time permits.. + + // How big is this item? This is measured in bytes, not characters. + TInt colsize = isqlst.ColumnSize(acidx); + + // Allocate a buffer. + RBuf buf; + TInt err = buf.Create(colsize); + INFO_PRINTF3(_L("%S: colsize %d"), &KTestFunction, colsize); + if(err != KErrNone) + { + SetTestStepResult(EFail); + INFO_PRINTF1(HTML_RED); + ERR_PRINTF3(_L("%S: Failed to allocate %d"), &KTestFunction, + colsize); + INFO_PRINTF1(HTML_COLOUR_OFF); + } + CleanupClosePushL(buf); + + // Call ColumnText(TInt aColumnIndex, TDes& aDest); + err = isqlst.ColumnText(acidx, buf); + ReportOnError(KTestFunction, _L("ColumnTextD"), acfgblk, acnnum, err); + if(err != KErrNone) + { + CleanupStack::PopAndDestroy(1, &buf); + return; + } + + // First the simplest, does the text match the config text? + if(buf == arg) + { + INFO_PRINTF1(HTML_GREEN); + INFO_PRINTF3(_L("%S: Got expected result, %S"), &KTestFunction, + &buf); + INFO_PRINTF1(HTML_COLOUR_OFF); + CleanupStack::PopAndDestroy(1, &buf); + return; + } + + // Perhaps 'arg' is a file, CompareTextAgainstFile will + // return KErrNotFound if it can't find the file. + if((err = CompareTextAgainstFileL(buf, arg)) == KErrNone) + { + INFO_PRINTF1(HTML_GREEN); + INFO_PRINTF3(_L("%S: Text match with file %S"), &KTestFunction, + &arg); + INFO_PRINTF1(HTML_COLOUR_OFF); + CleanupStack::PopAndDestroy(1, &buf); + return; + } + + ReportOnError(KTestFunction, _L("ColumnTextD"), acfgblk, acnnum, err); + CleanupStack::PopAndDestroy(1, &buf); + return; + } +void CSQLFnStep::ColumnBinaryL(const TInt& acidx, const TPtrC &arg, + const TDesC& acfgblk, const TInt acnnum=-1) + { + _LIT(KTestFunction, "ColumnBinaryL"); + + // Get the output from ColumnBinary. + TPtrC8 colb = isqlst.ColumnBinaryL(acidx); + INFO_PRINTF3(_L("%S: Got length %d"), &KTestFunction, colb.Length()); + // If both are zero length, then we're expected nothing, which is a + // reasonable possibility. + if((colb.Length() == 0) && (arg.Length() == 0)) + { + INFO_PRINTF1(HTML_GREEN); + INFO_PRINTF2(_L("%S: Got expected empty buffer."), &KTestFunction); + INFO_PRINTF1(HTML_COLOUR_OFF); + return; + } + + // Compare ColumnBinary return against a file. + TInt err = CompareBinaryAgainstFileL(colb, arg); + ReportOnError(KTestFunction, _L("ColumnBinaryL"), acfgblk, acnnum, err); + + return; + } +void CSQLFnStep::ColumnBinaryPL(const TInt& acidx, const TPtrC &arg, + const TDesC& acfgblk, const TInt acnnum=-1) + { + _LIT(KTestFunction, "ColumnBinaryP"); + + TInt csize = isqlst.ColumnSize(acidx); + INFO_PRINTF3(_L("%S: Colsize %d"), &KTestFunction, csize); +#ifdef _UNICODE + if(isqlst.ColumnType(acidx) == ESqlText) csize <<= 1; +#endif + + RBuf8 data; + TInt err; + if((err = data.Create(csize)) != KErrNone) + { + ReportOnError(KTestFunction, _L("Createbuf"), acfgblk, acnnum, err); + return; + } + CleanupClosePushL(data); + err = isqlst.ColumnBinary(acidx, data); + ReportOnError(KTestFunction, _L("ColumnBinaryP"), acfgblk, acnnum, err); + if((data.Length()==0) && (arg.Length()==0)) + { + INFO_PRINTF1(HTML_GREEN); + INFO_PRINTF2(_L("%S: Got expected empty buffer."), &KTestFunction); + INFO_PRINTF1(HTML_COLOUR_OFF); + CleanupStack::PopAndDestroy(1, &data); + return; + } + if(err != KErrNone) + { + CleanupStack::PopAndDestroy(1, &data); + return; + } + + // Compare ColumnBinary return against a file. + err = CompareBinaryAgainstFileL(data, arg); + ReportOnError(KTestFunction, _L("FileCompare"), acfgblk, acnnum, err); + CleanupStack::PopAndDestroy(1, &data); + return; + } +void CSQLFnStep::ColumnBinaryDL(const TInt& acidx, const TPtrC &arg, + const TDesC& acfgblk, const TInt acnnum=-1) + { + _LIT(KTestFunction, "ColumnBinaryD"); + + // How big is this item? + TInt colsize = isqlst.ColumnSize(acidx); + INFO_PRINTF3(_L("%S: colsize %d"), &KTestFunction, colsize); +#ifdef _UNICODE + if(isqlst.ColumnType(acidx) == ESqlText) colsize <<= 1; +#endif + + // Allocate a buffer. + RBuf8 buf; + TInt err = buf.Create(colsize); + if(err != KErrNone) + { + SetTestStepResult(EFail); + INFO_PRINTF1(HTML_RED); + ERR_PRINTF3(_L("%S: Failed to allocate %d bytes"), &KTestFunction, + colsize); + INFO_PRINTF1(HTML_COLOUR_OFF); + } + CleanupClosePushL(buf); + + // Call ColumnBinary(TInt aColumnIndex, TDes8& aDest); + err = isqlst.ColumnBinary(acidx, buf); + ReportOnError(KTestFunction, _L("ColumnBinaryD"), acfgblk, acnnum, err); + if(err != KErrNone) + { + CleanupStack::PopAndDestroy(1, &buf); + return; + } + if((buf.Length()==0) && (arg.Length()==0)) + { + CleanupStack::PopAndDestroy(1, &buf); + INFO_PRINTF1(HTML_GREEN); + INFO_PRINTF2(_L("%S: Got expected empty buffer."), &KTestFunction); + INFO_PRINTF1(HTML_COLOUR_OFF); + return; + } + + // Compare ColumnBinary return against a file. + err = CompareBinaryAgainstFileL(buf, arg); + ReportOnError(KTestFunction, _L("ColumnBinaryD"), acfgblk, acnnum, err); + CleanupStack::PopAndDestroy(1, &buf); + + return; + } + +// The following four methods, SWBindText, SWBindBinary, SRColumnText and +// SRColumnBinary are Stream-Write and Stream-Read methods. +// In each case 'arg' specifies a file which is opened as a source (SW) +// of data, or else as another stream (SR) to compare against. +void CSQLFnStep::SWBindTextL(const TInt& apidx, const TPtrC &arg, + const TDesC& acfgblk, const TInt acnnum=-1) + { + _LIT(KTestFunction, "SWBindText"); + RSqlParamWriteStream sqlw; + // Get the WriteStream to stuff data down.. + TInt err = sqlw.BindText(isqlst, apidx); + ReportOnError(KTestFunction, KTestFunction, acfgblk, acnnum, err); + if(err != KErrNone) return; + WriteFileToStreamL(sqlw, arg); + sqlw.Close(); + return; + } +void CSQLFnStep::SWBindBinaryL(const TInt& apidx, const TPtrC &arg, + const TDesC& acfgblk, const TInt acnnum=-1) + { + _LIT(KTestFunction, "SWBindBinary"); + RSqlParamWriteStream sqlw; + // Get the WriteStream to stuff data down.. + TInt err = sqlw.BindBinary(isqlst, apidx); + ReportOnError(KTestFunction, KTestFunction, acfgblk, acnnum, err); + if(err != KErrNone) return; + WriteFileToStreamL(sqlw, arg); + sqlw.Close(); + return; + } +void CSQLFnStep::SRColumnTextL(const TInt& acidx, const TPtrC &arg, + const TDesC& acfgblk, const TInt acnnum=-1) + { + _LIT(KTestFunction, "SRColumnText"); + + // First find out how much data is in this cell.. + TInt dsize = isqlst.ColumnSize(acidx); + INFO_PRINTF3(_L("%S: ColumnSize is %d"), &KTestFunction, dsize); + + RSqlColumnReadStream sqlr; + TInt err = sqlr.ColumnText(isqlst, acidx); + ReportOnError(KTestFunction, KTestFunction, acfgblk, acnnum, err); + if(err != KErrNone) return; + // Ok, we have a Read Stream.. + CleanupClosePushL(sqlr); + + // Compare it.. + TInt rc = CompareTextStreamAgainstFileL(sqlr, dsize, arg); + if(rc) + { + SetTestStepResult(EFail); + INFO_PRINTF1(HTML_RED); + ERR_PRINTF3(_L("%S: Stream comparison failure, file %S"), + &KTestFunction, &arg); + INFO_PRINTF1(HTML_COLOUR_OFF); + } + else + { + INFO_PRINTF1(HTML_GREEN); + INFO_PRINTF3(_L("%S: Stream comparison success, file %S"), + &KTestFunction, &arg); + INFO_PRINTF1(HTML_COLOUR_OFF); + } + CleanupStack::PopAndDestroy(1,&sqlr); + return; + } +void CSQLFnStep::SRColumnBinaryL(const TInt& acidx, const TPtrC &arg, + const TDesC& acfgblk, const TInt acnnum=-1) + { + _LIT(KTestFunction, "SRColumnBinary"); + + // First find out how much data is in this cell.. + TInt dsize = isqlst.ColumnSize(acidx); + INFO_PRINTF3(_L("%S: Colsize is %d"), &KTestFunction, dsize); +#ifdef _UNICODE + if(isqlst.ColumnType(acidx) == ESqlText) dsize <<= 1; +#endif + + // Get our RReadStream and check for errors. + RSqlColumnReadStream sqlr; + TInt err = sqlr.ColumnBinary(isqlst, acidx); + ReportOnError(KTestFunction, KTestFunction, acfgblk, acnnum, err); + if(err != KErrNone) return; + // Ok, we have a Read Stream.. + CleanupClosePushL(sqlr); + + // Compare it.. + TInt rc = CompareBinaryStreamAgainstFileL(sqlr, dsize, arg); + if(rc) + { + SetTestStepResult(EFail); + INFO_PRINTF1(HTML_RED); + ERR_PRINTF3(_L("%S: Stream comparison failure, file %S"), + &KTestFunction, &arg); + INFO_PRINTF1(HTML_COLOUR_OFF); + } + else + { + INFO_PRINTF1(HTML_GREEN); + INFO_PRINTF3(_L("%S: Stream comparison success, file %S"), + &KTestFunction, &arg); + INFO_PRINTF1(HTML_COLOUR_OFF); + } + CleanupStack::PopAndDestroy(1,&sqlr); + return; + } + +TBool CSQLFnStep::SPCreate(const TDesC &acfgblk, const TInt acnnum) + { + _LIT(KTestFunction, "SPCreate"); + + TSecurityPolicy defaultPolicy; + + // Try to create the SQLDB security policy. + TInt rc = isqlsp.Create(defaultPolicy); + ReportOnError(KTestFunction, KTestFunction, acfgblk, acnnum, rc); + if(rc == KErrNone) return ETrue; + return EFalse; + } + +TBool CSQLFnStep::SPCreate(const TPtrC&, const TDesC &acfgblk, const TInt acnnum) + { + _LIT(KTestFunction, "SPCreate"); + + TSecurityPolicy defaultPolicy; + + // Try to create the SQLDB security policy. + TInt rc = KErrNone; + TRAP(rc, isqlsp.CreateL(defaultPolicy)); + ReportOnError(KTestFunction, KTestFunction, acfgblk, acnnum, rc); + if(rc == KErrNone) return ETrue; + return EFalse; + } + +void CSQLFnStep::SPClose() + { + isqlsp.Close(); + return; + } + +TBool CSQLFnStep::SPSetDBPolicy(const TPtrC& apol, const TDesC &acfgblk, const TInt acnnum) + { + _LIT(KTestFunction, "SPSetDBPolicy"); + + //extract the policy level and capability from the argument passed in + TPtrC level, cap; + CommaSeparated(apol, level, cap); + + //create the security policy object with the supplied capability + TSecurityPolicy sp((TCapability)(icaphsh->GetNumFromString(cap))); + + INFO_PRINTF2(_L("SetDBPolicy: %S"), &level); + INFO_PRINTF2(_L("Capabilities are: %S"), &cap); + + TInt rc = isqlsp.SetDbPolicy(((RSqlSecurityPolicy::TPolicyType)ipolhsh->GetNumFromString(level)), sp); + ReportOnError(KTestFunction, KTestFunction, acfgblk, acnnum, rc); + if(rc == KErrNone) return ETrue; + return EFalse; + } + +TBool CSQLFnStep::SPSetPolicy(const TPtrC& apol, const TDesC &acfgblk, const TInt acnnum) + { + _LIT(KTestFunction, "SPSetPolicy"); + + //extract the policy level and capability from the argument passed in + TPtrC arg, arg2, object, name, level, caps; + CommaSeparated(apol, object, arg); + CommaSeparated(arg, name, arg2); + CommaSeparated(arg2, level, caps); + + //create the security policy object with the supplied capability + TSecurityPolicy sp((TCapability)(icaphsh->GetNumFromString(caps))); + + INFO_PRINTF2(_L("SetPolicy: %S"), &level); + INFO_PRINTF2(_L("Capabilities are: %S"), &caps); + INFO_PRINTF2(_L("Object type is: %S"), &object); + INFO_PRINTF2(_L("Object name is: %S"), &name); + + TInt rc = isqlsp.SetPolicy(((RSqlSecurityPolicy::TObjectType)iobjhsh->GetNumFromString(object)), name, ((RSqlSecurityPolicy::TPolicyType)ipolhsh->GetNumFromString(level)), sp); + ReportOnError(KTestFunction, KTestFunction, acfgblk, acnnum, rc); + if(rc == KErrNone) return ETrue; + return EFalse; + } + +void CSQLFnStep::SPExternalize(const TPtrC &arg, const TDesC &acfgblk, const TInt acnnum) + { + _LIT(KTestFunction, "SPExternalize"); + + RFileWriteStream rfws; + + TInt err = rfws.Create(irfs, arg, EFileStream); + if(err != KErrNone) + { + SetTestStepResult(EFail); + INFO_PRINTF1(HTML_RED); + ERR_PRINTF4(_L("%S: Can't open file %S, err %d"), &KTestFunction, &arg, + err ); + INFO_PRINTF1(HTML_COLOUR_OFF); + return; + } + + TInt rc = KErrNone; + TRAP(rc, isqlsp.ExternalizeL(rfws)); + TRAP(rc, rfws.CommitL()); + ReportOnError(KTestFunction, KTestFunction, acfgblk, acnnum, rc); + rfws.Close(); + return; + } + +void CSQLFnStep::SPInternalize(const TPtrC &arg, const TDesC &acfgblk, const TInt acnnum) + { + _LIT(KTestFunction, "SPInternalize"); + + RFileReadStream rfrs; + + TInt err = rfrs.Open(irfs, arg, EFileStream); + if(err != KErrNone) + { + SetTestStepResult(EFail); + INFO_PRINTF1(HTML_RED); + ERR_PRINTF3(_L("%S: Failed to open stream from file %S"), &KTestFunction, &arg); + INFO_PRINTF1(HTML_COLOUR_OFF); + return; + } + + TInt rc = KErrNone; + TRAP(rc, isqlsp.InternalizeL(rfrs)); + + ReportOnError(KTestFunction, KTestFunction, acfgblk, acnnum, rc); + rfrs.Close(); + return; + } + +// - Utility functions... --- we should remove these into a separate class -- +// +// Stream functions - should we have a persistent stream? Here it goes +// out of scope once the method completes, but what about a case where for +// example we write to a cell, then write some more later to the same cell? +// +// Loads more duplication.. +TInt CSQLFnStep::CompareTextStreamAgainstFileL(RReadStream &as, TInt asiz, + const TPtrC &afile) + { + _LIT(KTestFunction, "CompareTextStreamAgainstFile"); + // Get the file size. This is in bytes, so for Unicode will be + // out by a factor of two. Also, if created with notepad or similar + // will have a 'FEFF' two byte marker at the start (which SQLite + // strips when it sees it). + TInt fsize = FileSize(afile); + if(fsize < 0) return fsize;; + // We don't divide asiz by two: This will have originated in ColumnSize + // which returns the number of characters in a cell. + TInt textlen = asiz; +#ifdef _UNICODE + fsize >>= 1; +#endif + + // If fsize is 1 different (2 bytes) from textlen, then we'll expect + // a unicode marker. If not 1 or zero give up straight away. + TInt diff = fsize - textlen; + TInt ucmark = (diff == 1); + if((diff>1) || (diff<0)) + { + INFO_PRINTF1(HTML_COLOUR_OFF); + ERR_PRINTF4(_L("%S Size mismatch. Expected %d, got %d"), + &KTestFunction, fsize, textlen); + SetTestStepResult(EFail); + INFO_PRINTF1(HTML_COLOUR_OFF); + return KTEFSQLSizeError; + } + + // Open the reference file specified in the argument. + // Use an RFileReadStream because we need to worry about characters, + // not just bytes. + RFileReadStream rflrs; + TInt err = rflrs.Open(irfs, afile, EFileRead); + if(err != KErrNone) + return err; + CleanupClosePushL(rflrs); + + // Dumps the FEFF marker bytes for unicode files. + // SQLite does the same for text. + if(ucmark) + { + TInt16 mark = rflrs.ReadInt16L(); // byte order? + if((mark != (TInt16)0xfeff) && (mark != (TInt16)0xfffe)) + { + CleanupStack::PopAndDestroy(1, &rflrs); + return KTEFSQLSizeError; + } + } + + // For each 32768 chars in the text buffer... + const TInt slice = 32768; + RBuf fbuf, abuf; + err = fbuf.Create(slice); + if(err == KErrNone) err = abuf.Create(slice); + if(err != KErrNone) + { + CleanupStack::PopAndDestroy(1, &rflrs); + return KErrNoMemory; + } + CleanupClosePushL(fbuf); + CleanupClosePushL(abuf); + + TInt rc = KErrNone; + for(TInt pos=0; pos < textlen ; pos += slice) + { + TInt toread = textlen - pos; + if(toread > slice) + toread = slice; + + // Read 'toread' characters from the file and from the passed in + // data. + as.ReadL(abuf, toread); + TPtrC txtslice = abuf.Left(toread); + rflrs.ReadL(fbuf, toread); + TPtrC fileslice = fbuf.Left(toread); + + // Compare .. + if (fileslice != txtslice) + { + rc = KTEFSQLSizeError; + break; + } + } + CleanupStack::PopAndDestroy(3, &rflrs); + return rc; + } + +// Loads more duplication.. +TInt CSQLFnStep::CompareBinaryStreamAgainstFileL(RReadStream &as, TInt asiz, + const TPtrC &afile) + { + _LIT(KTestFunction, "CompareBinaryStreamAgainstFile"); + // Get the file size. This is in bytes. + TInt fsize = FileSize(afile); + if(fsize < 0) return fsize;; + + // If sizes differ give up immediately. + if(fsize - asiz) + { + INFO_PRINTF1(HTML_COLOUR_OFF); + ERR_PRINTF4(_L("%S Size mismatch. Expected %d, got %d"), + &KTestFunction, fsize, asiz); + SetTestStepResult(EFail); + INFO_PRINTF1(HTML_COLOUR_OFF); + return KTEFSQLSizeError; + } + + // Open the reference file specified in the argument. + // Use an RFileReadStream because we need to worry about characters, + // not just bytes. + RFileReadStream rflrs; + TInt err = rflrs.Open(irfs, afile, EFileRead); + if(err != KErrNone) return err; + CleanupClosePushL(rflrs); + + // For each 32768 chars in the text buffer... + const TInt slice = 32768; + RBuf8 fbuf, abuf; + err = fbuf.Create(slice); + if(err == KErrNone) err = abuf.Create(slice); + if(err != KErrNone) + { + CleanupStack::PopAndDestroy(1, &rflrs); + return err; + } + CleanupClosePushL(fbuf); + CleanupClosePushL(abuf); + + TInt rc = KErrNone; + for(TInt pos=0; pos < asiz ; pos += slice) + { + TInt toread = asiz - pos; + if(toread > slice) + toread = slice; + + // Read 'toread' characters from the file and from the passed in + // data. Do we really need to chop out only the read bits for + // comparison? Wouldn't comparing fbuf and abuf work? + rflrs.ReadL(fbuf, toread); + TPtr8 fslice = fbuf.LeftTPtr(toread); + as.ReadL(abuf, toread); + TPtr8 aslice = abuf.LeftTPtr(toread); + + // Compare .. (does this compare only what's been read?) + if (fslice != aslice) + { + rc = KTEFSQLSizeError; + break; + } + } + CleanupStack::PopAndDestroy(3, &rflrs); + return rc; + } +void CSQLFnStep::WriteFileToStreamL(RWriteStream &as1, const TPtrC &afile) + { + _LIT(KTestFunction, "WriteFileToStream"); + // Open the reference file specified in the argument. + RFileReadStream rflrs; + TInt err = rflrs.Open(irfs, afile, EFileRead); + // Do we want to return anything? + if(err != KErrNone) + { + SetTestStepResult(EFail); + INFO_PRINTF1(HTML_RED); + ERR_PRINTF3(_L("%S: Failed to open stream from file %S"), + &KTestFunction, &afile); + INFO_PRINTF1(HTML_COLOUR_OFF); + return; + } + CleanupClosePushL(rflrs); + as1.WriteL(rflrs); + CleanupStack::PopAndDestroy(1, &rflrs); + return; + } + +TInt CSQLFnStep::CompareBinaryAgainstFileL(const TDesC8 &abuf, + const TFileName& afile) + { + _LIT(KTestFunction, "CompareBinaryAgainstFile"); + // Get the file size. + TInt fsize = FileSize(afile); + if(fsize < 0) return fsize; + // How much binary do we have? + TInt binlen = abuf.Length(); + + INFO_PRINTF4(_L("%S: Filelen %d, Binlen %d"), &KTestFunction, fsize, binlen); + + // If sizes differ drop out immediately. + if(fsize - binlen) + return KTEFSQLSizeError; + + // Open the reference file specified in the argument. + // Use an RFileReadStream because we need to worry about characters, + // not just bytes. + RFileReadStream rflrs; + TInt err = rflrs.Open(irfs, afile, EFileRead); + if(err != KErrNone) + return err; + CleanupClosePushL(rflrs); + + // For each 32768 chars in the text buffer... + const TInt slice = 32768; + RBuf8 fbuf; + fbuf.Create(slice); + CleanupClosePushL(fbuf); + + TInt rc = KErrNone; + for(TInt pos=0; pos < binlen ; pos += slice) + { + TInt toread = binlen - pos; + if(toread > slice) + toread = slice; + + // Read 'toread' bytes from the file and from the passed in + // data. + rflrs.ReadL(fbuf, toread); + TPtrC8 fileslice = fbuf.Left(toread); + TPtrC8 binslice = abuf.Mid(pos, toread); + + // Compare .. + if (fileslice != binslice) + { + rc = KTEFSQLSizeError; + break; + } + } + + INFO_PRINTF2(_L("%S: Comparison successful"), &KTestFunction); + CleanupStack::PopAndDestroy(2, &rflrs); + return rc; + } + +// Tested and working.. +TInt CSQLFnStep::CompareTextAgainstFileL(const TDesC &atxt, + const TFileName& afile) + { + // Get the file size. This is in bytes, so for Unicode will be + // out by a factor of two. Also, if created with notepad or similar + // will have a 'FEFF' two byte marker at the start (which SQLite + // strips when it sees it). + TInt fsize = FileSize(afile); + if(fsize < 0) return fsize; +#ifdef _UNICODE + fsize >>= 1; +#endif + // How much text do we have? + TInt textlen = atxt.Length(); + + // If fsize is 1 different (2 bytes) from textlen, then we'll expect + // a unicode marker. If not 1 or zero give up straight away. + TInt diff = fsize - textlen; + TInt ucmark = (diff == 1); + if((diff>1) || (diff<0)) + { + ERR_PRINTF3(_L("FSIZE is %d, textlen is %d"), fsize, textlen); + return KTEFSQLSizeError; + } + + // Open the reference file specified in the argument. + // Use an RFileReadStream because we need to worry about characters, + // not just bytes. + RFileReadStream rflrs; + TInt err = rflrs.Open(irfs, afile, EFileRead); + if(err != KErrNone) + return err; + CleanupClosePushL(rflrs); + + // Dumps the FEFF marker bytes for unicode files. + // SQLite does the same for text. + if(ucmark) + { + TInt16 mark = rflrs.ReadInt16L(); // byte order? + if((mark != (TInt16)0xfeff) && (mark != (TInt16)0xfffe)) + { + CleanupStack::PopAndDestroy(1, &rflrs); + return KTEFSQLSizeError; + } + } + + // For each 32768 chars in the text buffer... + const TInt slice = 32768; + RBuf fbuf; + fbuf.Create(slice); + CleanupClosePushL(fbuf); + + TInt rc = KErrNone; + for(TInt pos=0; pos < textlen ; pos += slice) + { + TInt toread = textlen - pos; + if(toread > slice) + toread = slice; + + // Read 'toread' characters from the file and from the passed in + // data. + rflrs.ReadL(fbuf, toread); + TPtrC txtslice = atxt.Mid(pos, toread); + + // Compare .. + if (fbuf != txtslice) + { + rc = KTEFSQLSizeError; + break; + } + } + CleanupStack::PopAndDestroy(2, &rflrs); + return rc; + } + +// Get the expected error code for the current action. Assume KErrNone if it +// isn't in the config file. We might have (in the config file) +// ExpectedError27=KSqlErrPermission +int CSQLFnStep::ActionNoToErrEnum(const TDesC& acfgsec, const TInt aActionNum, + TPtrC& aes) + { + TBuf cnfgerr(_L("ExpectedError")); + if(aActionNum != -1) cnfgerr.AppendNum(aActionNum); + if(!GetStringFromConfig(acfgsec, cnfgerr, aes)) + aes.Set(_L("KErrNone")); + return(ErrStringToEnum(aes)); +} +TInt CSQLFnStep::ErrStringToEnum(TPtrC &aerr) + { + return(ierrhsh->GetNumFromString(aerr)); + } +void CSQLFnStep::ErrEnumToString(const TInt &aerr, TPtrC &aptrstr) + { + aptrstr.Set(*(ierrhsh->GetStringFromNum(aerr))); + return; + } +const TPtrC CSQLFnStep::SqlColumnTypeToString(TSqlColumnType &asqltype) + { + return *icoltypehsh->GetStringFromNum(asqltype); + } +TSqlColumnType CSQLFnStep::StringToSqlColumnType(const TDesC &atype) + { + return (TSqlColumnType) icoltypehsh->GetNumFromString(atype); + } +/* + * A helper function to report on an error. This won't say anything if the + * error received (aerr) is equal to the error expected as defined in the + * configuration file. + */ +void CSQLFnStep::ReportOnError(const TDesC &afnnam, const TDesC &apinam, + const TDesC &acfgblk, const TInt acfgno, + const TInt aerr) + { + // Get the expected error. + TPtrC experrS; + TInt experr = ActionNoToErrEnum(acfgblk, acfgno, experrS); + + // Some methods such as Exec and Next return a positive value on + // success. If we're not expecting an error and the actual error code + // is positive just return, everything is cool. + if((experr == KErrNone) && (aerr >= 0)) + return; + + // Is the actual error the same as the expected error? + if(aerr != experr) + { + INFO_PRINTF1(HTML_RED); + SetTestStepResult(EFail); + TPtrC errS; + ErrEnumToString(aerr, errS); // Convert the actual error to a string. + + ERR_PRINTF7(_L("%S: %S gave error %d/%S, expected %d/%S"), + &afnnam, &apinam, aerr, &errS, experr, &experrS); + // Run 'LastErrorMessage' if we unexpectedly have 'KSqlErrGeneral', + // often what it has to say is very helpful. + if(aerr == KSqlErrGeneral) + LastErrorMessage(_L("")); + + INFO_PRINTF1(HTML_COLOUR_OFF); + } + else if(aerr != KErrNone) + { + INFO_PRINTF1(HTML_GREEN); + INFO_PRINTF5(_L("%S: %S got expected error %d/%S"), &afnnam, &apinam, + aerr, &experrS); + INFO_PRINTF1(HTML_COLOUR_OFF); + } + return; + } +TBool CSQLFnStep::FromConfig(const TDesC &afnnam, const TDesC &acfgblk, + const TDesC &acfgname, TPtrC &acfgval) + { + if(!GetStringFromConfig(acfgblk, acfgname, acfgval)) + { + INFO_PRINTF1(HTML_RED); + ERR_PRINTF4(_L("%S: Failed to get %S:%S parameter."), &afnnam, &acfgblk, + &acfgname); + SetTestStepResult(EFail); + INFO_PRINTF1(HTML_COLOUR_OFF); + return EFalse; + } + return ETrue; + } +TBool CSQLFnStep::FromConfig(const TDesC &afnnam, const TDesC &acfgblk, + const TDesC &acfgname, TInt &acfgval) + { + if(!GetIntFromConfig(acfgblk, acfgname, acfgval)) + { + INFO_PRINTF1(HTML_RED); + ERR_PRINTF4(_L("%S: Failed to get %S:%S parameter."), &afnnam, &acfgblk, + &acfgname); + SetTestStepResult(EFail); + INFO_PRINTF1(HTML_COLOUR_OFF); + return EFalse; + } + return ETrue; + } +TBool CSQLFnStep::FromConfig(const TDesC &afnnam, const TDesC &acfgblk, + const TDesC &acfgname, TReal &acfgval) + { + TPtrC gotS; + if(!GetStringFromConfig(acfgblk, acfgname, gotS)) + { + INFO_PRINTF1(HTML_RED); + ERR_PRINTF4(_L("%S: Failed to get %S:%S parameter."), &afnnam, &acfgblk, + &acfgname); + SetTestStepResult(EFail); + INFO_PRINTF1(HTML_COLOUR_OFF); + return EFalse; + } + TLex tl = gotS; + if(tl.Val(acfgval) != KErrNone) + { + ERR_PRINTF5(_L("%S:%S Failed to convert %S:%S to real."), + &afnnam, &acfgblk, &acfgname, &gotS); + return EFalse; + } + + return ETrue; + } +// Looking for, e.g, "9876,1234" +void CSQLFnStep::CommaSeparated(const TPtrC& ainp, TInt &aint, TInt &aint2) + { +// _LIT(KTestFunction, "CommaSeparated"); + + // Read in what kind of binds we'll be doing.. + TInt origlen = ainp.Length(); + TChar comma(','); + TInt comoffset = ainp.Locate(comma); + if(comoffset != KErrNotFound) + { + TPtrC left = ainp.Left(comoffset); + TLex tl(left); + tl.Val(aint); + TInt rightlen = origlen - comoffset - 1; + TPtrC right = ainp.Right(rightlen); + tl = right; + tl.Val(aint2); + } + else + { + TLex tl(ainp); + tl.Val(aint); + aint2=-1; + } + } +// Looking for, e.g, "9876,some words" +void CSQLFnStep::CommaSeparated(const TPtrC& ainp, TInt &aint, TPtrC &astr) + { +// _LIT(KTestFunction, "CommaSeparated"); + + TInt origlen = ainp.Length(); + TChar comma(','); + TInt comoffset = ainp.Locate(comma); + if(comoffset != KErrNotFound) + { + TPtrC left = ainp.Left(comoffset); + TLex tl(left); + tl.Val(aint); + TInt rightlen = origlen - comoffset - 1; + astr.Set(ainp.Right(rightlen)); + } + else + { + TLex tl(ainp); + tl.Val(aint); + astr.Set(_L("")); + } + } +void CSQLFnStep::CommaSeparated(const TPtrC& ainp, TPtrC &aleft, TPtrC &aright) + { +// _LIT(KTestFunction, "CommaSeparated"); + aleft.Set(ainp); + aright.Set(_L("")); + + TInt origlen = ainp.Length(); + TChar comma(','); + TInt comoffset = ainp.Locate(comma); + if(comoffset != KErrNotFound) + { + aleft.Set(ainp.Left(comoffset)); + TInt rightlen = origlen - comoffset - 1; + aright.Set(ainp.Right(rightlen)); + } + else + return; + } +TInt CSQLFnStep::FileSize(const TPtrC &afile) + { + // First open the file specified in the argument. + // This lot gets duplicated a lot. + RFile file; + TFileName fn = afile; + TInt err; + if((err = file.Open(irfs, fn, 0)) != KErrNone) + { + INFO_PRINTF1(HTML_RED); + ERR_PRINTF3(_L("Cannot open %S to get filesize, err %d"), &afile, err); + SetTestStepResult(EFail); + INFO_PRINTF1(HTML_COLOUR_OFF); + + return err; + } + TInt fsize; + file.Size(fsize); + file.Close(); + return fsize; + } +