|
|
|
/* This file is part of the KDE project
|
|
|
|
Copyright (C) 2006 Jaroslaw Staniek <js@iidea.pl>
|
|
|
|
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
|
|
modify it under the terms of the GNU Library General Public
|
|
|
|
License as published by the Free Software Foundation; either
|
|
|
|
version 2 of the License, or (at your option) any later version.
|
|
|
|
|
|
|
|
This library is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
Library General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU Library General Public License
|
|
|
|
along with this library; see the file COPYING.LIB. If not, write to
|
|
|
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
|
|
* Boston, MA 02110-1301, USA.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "altertable.h"
|
|
|
|
|
|
|
|
#include <unistd.h>
|
|
|
|
|
|
|
|
#include <tqapplication.h>
|
|
|
|
#include <tqfile.h>
|
|
|
|
#include <tqdir.h>
|
|
|
|
#include <tqregexp.h>
|
|
|
|
#include <tqclipboard.h>
|
|
|
|
|
|
|
|
#include <kdebug.h>
|
|
|
|
|
|
|
|
#include <main/keximainwindowimpl.h>
|
|
|
|
#include <core/kexiaboutdata.h>
|
|
|
|
#include <core/kexidialogbase.h>
|
|
|
|
#include <core/kexiviewbase.h>
|
|
|
|
#include <core/kexipartitem.h>
|
|
|
|
#include <core/kexitabledesignerinterface.h>
|
|
|
|
#include <core/kexiinternalpart.h>
|
|
|
|
#include <kexiutils/utils.h>
|
|
|
|
#include <koproperty/set.h>
|
|
|
|
#include <kexidb/connection.h>
|
|
|
|
#include <kexidb/utils.h>
|
|
|
|
|
|
|
|
TQString testFilename;
|
|
|
|
TQFile testFile;
|
|
|
|
TQTextStream testFileStream;
|
|
|
|
TQStringList testFileLine;
|
|
|
|
uint testLineNumber = 0;
|
|
|
|
TQString origDbFilename, dbFilename;
|
|
|
|
int variableI = 1; // simple variable 'i' support
|
|
|
|
int newArgc;
|
|
|
|
char** newArgv;
|
|
|
|
KexiMainWindowImpl* win = 0;
|
|
|
|
KexiProject* prj = 0;
|
|
|
|
|
|
|
|
void showError(const TQString& msg)
|
|
|
|
{
|
|
|
|
TQString msg_(msg);
|
|
|
|
msg_.prepend(TQString("Error at line %1: ").tqarg(testLineNumber));
|
|
|
|
kdDebug() << msg_ << endl;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Reads a single line from testFileStream, fills testFileLine, updates testLineNumber
|
|
|
|
text in quotes is extracted, e.g. \"ab c\" is treat as one item "ab c"
|
|
|
|
Returns flas on failure (e.g. end of file).
|
|
|
|
Empty lines and lines or parts of lines with # (comments) are omitted. */
|
|
|
|
tristate readLineFromTestFile(const TQString& expectedCommandName = TQString())
|
|
|
|
{
|
|
|
|
TQString s;
|
|
|
|
bool blockComment = false;
|
|
|
|
while (true) {
|
|
|
|
if (testFileStream.atEnd())
|
|
|
|
return cancelled;
|
|
|
|
testLineNumber++;
|
|
|
|
s = testFileStream.readLine().stripWhiteSpace();
|
|
|
|
if (blockComment) {
|
|
|
|
if (s.endsWith("*/"))
|
|
|
|
blockComment = false;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (!blockComment && s.startsWith("/*")) {
|
|
|
|
blockComment = true;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (s.startsWith("#"))
|
|
|
|
continue; //skip commented line
|
|
|
|
if (!s.isEmpty())
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
s.append(" "); //sentinel
|
|
|
|
TQString item;
|
|
|
|
testFileLine.clear();
|
|
|
|
const int len = s.length();
|
|
|
|
bool skipWhiteSpace = true, quoted = false;
|
|
|
|
for (int i=0; i<len; i++) {
|
|
|
|
const TQChar ch( s.ref(i) );
|
|
|
|
if (skipWhiteSpace) {
|
|
|
|
if (ch=='#')
|
|
|
|
break; //eoln
|
|
|
|
if (ch==' ' || ch=='\t')
|
|
|
|
continue;
|
|
|
|
skipWhiteSpace = false;
|
|
|
|
if (ch=='\"') {
|
|
|
|
quoted = true;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
item.append(ch);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if ((quoted && ch=='\"') || (!quoted && (ch==' ' || ch=='\t'))) { //end of item
|
|
|
|
skipWhiteSpace = true;
|
|
|
|
quoted = false;
|
|
|
|
testFileLine.append( item );
|
|
|
|
item = TQString();
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
item.append(ch);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!expectedCommandName.isEmpty() && testFileLine[0]!=expectedCommandName) {
|
|
|
|
showError( TQString("Invalid command '%1', expected '%2'")
|
|
|
|
.tqarg(testFileLine[0]).tqarg(expectedCommandName));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (quoted) {
|
|
|
|
showError( "Invalid contents" );
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool checkItemsNumber(int expectedNumberOfItems, int optionalNumberOfItems = -1)
|
|
|
|
{
|
|
|
|
bool ok = expectedNumberOfItems==(int)testFileLine.count();
|
|
|
|
if (optionalNumberOfItems>0)
|
|
|
|
ok = ok || optionalNumberOfItems==(int)testFileLine.count();
|
|
|
|
if (!ok) {
|
|
|
|
TQString msg = TQString("Invalid number of args (%1) for command '%2', expected: %3")
|
|
|
|
.tqarg(testFileLine.count()).tqarg(testFileLine[0]).tqarg(expectedNumberOfItems);
|
|
|
|
if (optionalNumberOfItems>0)
|
|
|
|
msg.append( TQString(" or %1").tqarg(optionalNumberOfItems) );
|
|
|
|
showError( msg );
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
TQVariant::Type typeNameToTQVariantType(const TQCString& name_)
|
|
|
|
{
|
|
|
|
TQCString name( name_.lower() );
|
|
|
|
if (name=="string")
|
|
|
|
return TQVariant::String;
|
|
|
|
if (name=="int")
|
|
|
|
return TQVariant::Int;
|
|
|
|
if (name=="bool" || name=="boolean")
|
|
|
|
return TQVariant::Bool;
|
|
|
|
if (name=="double" || name=="float")
|
|
|
|
return TQVariant::Double;
|
|
|
|
if (name=="date")
|
|
|
|
return TQVariant::Date;
|
|
|
|
if (name=="datetime")
|
|
|
|
return TQVariant::DateTime;
|
|
|
|
if (name=="time")
|
|
|
|
return TQVariant::Time;
|
|
|
|
if (name=="bytearray")
|
|
|
|
return TQVariant::ByteArray;
|
|
|
|
if (name=="longlong")
|
|
|
|
return TQVariant::LongLong;
|
|
|
|
//todo more types
|
|
|
|
showError(TQString("Invalid type '%1'").tqarg(name_));
|
|
|
|
return TQVariant::Invalid;
|
|
|
|
}
|
|
|
|
|
|
|
|
// casts string to TQVariant
|
|
|
|
bool castStringToTQVariant( const TQString& string, const TQCString& type, TQVariant& result )
|
|
|
|
{
|
|
|
|
if (string.lower()=="<null>") {
|
|
|
|
result = TQVariant();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if (string=="\"\"") {
|
|
|
|
result = TQString("");
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
const TQVariant::Type vtype = typeNameToTQVariantType( type );
|
|
|
|
bool ok;
|
|
|
|
result = KexiDB::stringToVariant( string, vtype, ok );
|
|
|
|
return ok;
|
|
|
|
}
|
|
|
|
|
|
|
|
// returns a number parsed from argument; if argument is i or i++, variableI is used
|
|
|
|
// 'ok' is set to false on failure
|
|
|
|
static int getNumber(const TQString& argument, bool& ok)
|
|
|
|
{
|
|
|
|
int result;
|
|
|
|
ok = true;
|
|
|
|
if (argument=="i" || argument=="i++") {
|
|
|
|
result = variableI;
|
|
|
|
if (argument=="i++")
|
|
|
|
variableI++;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
result = argument.toInt(&ok);
|
|
|
|
if (!ok) {
|
|
|
|
showError(TQString("Invalid value '%1'").tqarg(argument));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------
|
|
|
|
|
|
|
|
AlterTableTester::AlterTableTester()
|
|
|
|
: TQObject()
|
|
|
|
, m_finishedCopying(false)
|
|
|
|
{
|
|
|
|
//copy the db file to a temp file
|
|
|
|
qInitNetworkProtocols();
|
|
|
|
TQPtrList<TQNetworkOperation> list = m_copyOperator.copy(
|
|
|
|
"file://" + TQDir::current().path() + "/" + origDbFilename,
|
|
|
|
"file://" + TQDir::current().path() + "/" + dbFilename, false, false );
|
|
|
|
connect(&m_copyOperator, TQT_SIGNAL(finished(TQNetworkOperation*)),
|
|
|
|
this, TQT_SLOT(slotFinishedCopying(TQNetworkOperation*)));
|
|
|
|
}
|
|
|
|
|
|
|
|
AlterTableTester::~AlterTableTester()
|
|
|
|
{
|
|
|
|
TQFile(dbFilename).remove();
|
|
|
|
}
|
|
|
|
|
|
|
|
void AlterTableTester::slotFinishedCopying(TQNetworkOperation* oper)
|
|
|
|
{
|
|
|
|
if (oper->operation()==TQNetworkProtocol::OpPut)
|
|
|
|
m_finishedCopying = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool AlterTableTester::changeFieldProperty(KexiTableDesignerInterface* designerIface)
|
|
|
|
{
|
|
|
|
if (!checkItemsNumber(5))
|
|
|
|
return false;
|
|
|
|
TQVariant newValue;
|
|
|
|
TQCString propertyName( testFileLine[2].latin1() );
|
|
|
|
TQCString propertyType( testFileLine[3].latin1() );
|
|
|
|
TQString propertyValueString(testFileLine[4]);
|
|
|
|
if (propertyName=="type")
|
|
|
|
newValue = (int)KexiDB::Field::typeForString(testFileLine[4]);
|
|
|
|
else {
|
|
|
|
if (!castStringToTQVariant(propertyValueString, propertyType, newValue)) {
|
|
|
|
showError( TQString("Could not set property '%1' value '%2' of type '%3'")
|
|
|
|
.tqarg(propertyName).tqarg(propertyValueString).tqarg(propertyType) );
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
bool ok;
|
|
|
|
int row = getNumber(testFileLine[1], ok)-1;
|
|
|
|
if (!ok)
|
|
|
|
return false;
|
|
|
|
designerIface->changeFieldPropertyForRow( row, propertyName, newValue, 0, true );
|
|
|
|
if (propertyName=="type") {
|
|
|
|
//clean subtype name, e.g. from "longText" to "LongText", because dropdown list is case-sensitive
|
|
|
|
TQString realSubTypeName;
|
|
|
|
if (KexiDB::Field::BLOB == KexiDB::Field::typeForString(testFileLine[4]))
|
|
|
|
//! @todo hardcoded!
|
|
|
|
realSubTypeName = "image";
|
|
|
|
else
|
|
|
|
realSubTypeName = KexiDB::Field::typeString( KexiDB::Field::typeForString(testFileLine[4]) );
|
|
|
|
designerIface->changeFieldPropertyForRow( row, "subType", realSubTypeName, 0, true );
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
//helper
|
|
|
|
bool AlterTableTester::getSchemaDump(KexiDialogBase* dlg, TQString& schemaDebugString)
|
|
|
|
{
|
|
|
|
KexiTableDesignerInterface* designerIface
|
|
|
|
= dynamic_cast<KexiTableDesignerInterface*>( dlg->selectedView() );
|
|
|
|
if (!designerIface)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// Get the result
|
|
|
|
tristate result;
|
|
|
|
schemaDebugString = designerIface->debugStringForCurrentTableSchema(result);
|
|
|
|
if (true!=result) {
|
|
|
|
showError( TQString("Loading modified schema failed. Result: %1")
|
|
|
|
.tqarg(~result ? "cancelled" : "false") );
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
schemaDebugString.remove(TQRegExp(",$")); //no need to have "," at the end of lines
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool AlterTableTester::showSchema(KexiDialogBase* dlg, bool copyToClipboard)
|
|
|
|
{
|
|
|
|
TQString schemaDebugString;
|
|
|
|
if (!getSchemaDump(dlg, schemaDebugString))
|
|
|
|
return false;
|
|
|
|
if (copyToClipboard)
|
|
|
|
TQApplication::tqclipboard()->setText( schemaDebugString );
|
|
|
|
else
|
|
|
|
kdDebug() << TQString("Schema for '%1' table:\n").tqarg(dlg->partItem()->name())
|
|
|
|
+ schemaDebugString + "\nendSchema" << endl;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool AlterTableTester::checkInternal(KexiDialogBase* dlg,
|
|
|
|
TQString& debugString, const TQString& endCommand, bool skipColonsAndStripWhiteSpace)
|
|
|
|
{
|
|
|
|
Q_UNUSED(dlg);
|
|
|
|
TQTextStream resultStream(&debugString, IO_ReadOnly);
|
|
|
|
// Load expected result, compare
|
|
|
|
TQString expectedLine, resultLine;
|
|
|
|
while (true) {
|
|
|
|
const bool testFileStreamAtEnd = testFileStream.atEnd();
|
|
|
|
if (!testFileStreamAtEnd) {
|
|
|
|
testLineNumber++;
|
|
|
|
expectedLine = testFileStream.readLine();
|
|
|
|
if (skipColonsAndStripWhiteSpace) {
|
|
|
|
expectedLine = expectedLine.stripWhiteSpace();
|
|
|
|
expectedLine.remove(TQRegExp(",$")); //no need to have "," at the end of lines
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (testFileStreamAtEnd || endCommand==expectedLine.stripWhiteSpace()) {
|
|
|
|
if (!resultStream.atEnd()) {
|
|
|
|
showError( "Test file ends unexpectedly." );
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
//test line loaded, load result
|
|
|
|
if (resultStream.atEnd()) {
|
|
|
|
showError( TQString("Result ends unexpectedly. There is at least one additinal test line: '")
|
|
|
|
+ expectedLine +"'" );
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
resultLine = resultStream.readLine();
|
|
|
|
if (skipColonsAndStripWhiteSpace) {
|
|
|
|
resultLine = resultLine.stripWhiteSpace();
|
|
|
|
resultLine.remove(TQRegExp(",$")); //no need to have "," at the end of lines
|
|
|
|
}
|
|
|
|
if (resultLine!=expectedLine) {
|
|
|
|
showError(
|
|
|
|
TQString("Result differs from the expected:\nExpected: ")
|
|
|
|
+expectedLine+"\n????????: "+resultLine+"\n");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool AlterTableTester::checkSchema(KexiDialogBase* dlg)
|
|
|
|
{
|
|
|
|
TQString schemaDebugString;
|
|
|
|
if (!getSchemaDump(dlg, schemaDebugString))
|
|
|
|
return false;
|
|
|
|
bool result = checkInternal(dlg, schemaDebugString, "endSchema", true /*skipColonsAndStripWhiteSpace*/);
|
|
|
|
kdDebug() << TQString("Schema check for table '%1': %2").tqarg(dlg->partItem()->name())
|
|
|
|
.tqarg(result ? "OK" : "Failed") << endl;
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool AlterTableTester::getActionsDump(KexiDialogBase* dlg, TQString& actionsDebugString)
|
|
|
|
{
|
|
|
|
KexiTableDesignerInterface* designerIface
|
|
|
|
= dynamic_cast<KexiTableDesignerInterface*>( dlg->selectedView() );
|
|
|
|
if (!designerIface)
|
|
|
|
return false;
|
|
|
|
tristate result = designerIface->simulateAlterTableExecution(&actionsDebugString);
|
|
|
|
if (true!=result) {
|
|
|
|
showError( TQString("Computing simplified actions for table '%1' failed.").tqarg(dlg->partItem()->name()) );
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool AlterTableTester::showActions(KexiDialogBase* dlg, bool copyToClipboard)
|
|
|
|
{
|
|
|
|
TQString actionsDebugString;
|
|
|
|
if (!getActionsDump(dlg, actionsDebugString))
|
|
|
|
return false;
|
|
|
|
if (copyToClipboard)
|
|
|
|
TQApplication::tqclipboard()->setText( actionsDebugString );
|
|
|
|
else
|
|
|
|
kdDebug() << TQString("Simplified actions for altering table '%1':\n").tqarg(dlg->partItem()->name())
|
|
|
|
+ actionsDebugString+"\n" << endl;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool AlterTableTester::checkActions(KexiDialogBase* dlg)
|
|
|
|
{
|
|
|
|
TQString actionsDebugString;
|
|
|
|
if (!getActionsDump(dlg, actionsDebugString))
|
|
|
|
return false;
|
|
|
|
bool result = checkInternal(dlg, actionsDebugString, "endActions", true /*skipColonsAndStripWhiteSpace*/);
|
|
|
|
kdDebug() << TQString("Actions check for table '%1': %2").tqarg(dlg->partItem()->name())
|
|
|
|
.tqarg(result ? "OK" : "Failed") << endl;
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool AlterTableTester::saveTableDesign(KexiDialogBase* dlg)
|
|
|
|
{
|
|
|
|
KexiTableDesignerInterface* designerIface
|
|
|
|
= dynamic_cast<KexiTableDesignerInterface*>( dlg->selectedView() );
|
|
|
|
if (!designerIface)
|
|
|
|
return false;
|
|
|
|
tristate result = designerIface->executeRealAlterTable();
|
|
|
|
if (true!=result) {
|
|
|
|
showError( TQString("Saving design of table '%1' failed.").tqarg(dlg->partItem()->name()) );
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool AlterTableTester::getTableDataDump(KexiDialogBase* dlg, TQString& dataString)
|
|
|
|
{
|
|
|
|
KexiTableDesignerInterface* designerIface
|
|
|
|
= dynamic_cast<KexiTableDesignerInterface*>( dlg->selectedView() );
|
|
|
|
if (!designerIface)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
TQMap<TQString,TQString> args;
|
|
|
|
TQTextStream ts( &dataString, IO_WriteOnly );
|
|
|
|
args["textStream"] = KexiUtils::ptrToString<TQTextStream>( &ts );
|
|
|
|
args["destinationType"]="file";
|
|
|
|
args["delimiter"]="\t";
|
|
|
|
args["textQuote"]="\"";
|
|
|
|
args["itemId"] = TQString::number(
|
|
|
|
prj->dbConnection()->tableSchema( dlg->partItem()->name() )->id() );
|
|
|
|
if (!KexiInternalPart::executeCommand("csv_importexport", win, "KexiCSVExport", &args)) {
|
|
|
|
showError( "Error exporting table contents." );
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool AlterTableTester::showTableData(KexiDialogBase* dlg, bool copyToClipboard)
|
|
|
|
{
|
|
|
|
TQString dataString;
|
|
|
|
if (!getTableDataDump(dlg, dataString))
|
|
|
|
return false;
|
|
|
|
if (copyToClipboard)
|
|
|
|
TQApplication::tqclipboard()->setText( dataString );
|
|
|
|
else
|
|
|
|
kdDebug() << TQString("Contents of table '%1':\n").tqarg(dlg->partItem()->name())+dataString+"\n" << endl;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool AlterTableTester::checkTableData(KexiDialogBase* dlg)
|
|
|
|
{
|
|
|
|
TQString dataString;
|
|
|
|
if (!getTableDataDump(dlg, dataString))
|
|
|
|
return false;
|
|
|
|
bool result = checkInternal(dlg, dataString, "endTableData", false /*!skipColonsAndStripWhiteSpace*/);
|
|
|
|
kdDebug() << TQString("Table '%1' contents: %2").tqarg(dlg->partItem()->name())
|
|
|
|
.tqarg(result ? "OK" : "Failed") << endl;
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool AlterTableTester::closeWindow(KexiDialogBase* dlg)
|
|
|
|
{
|
|
|
|
if (!dlg)
|
|
|
|
return true;
|
|
|
|
TQString name = dlg->partItem()->name();
|
|
|
|
tristate result = true == win->closeDialog(dlg, true/*layoutTaskBar*/, true/*doNotSaveChanges*/);
|
|
|
|
kdDebug() << TQString("Closing window for table '%1': %2").tqarg(name)
|
|
|
|
.tqarg(result==true ? "OK" : (result==false ? "Failed" : "Cancelled")) << endl;
|
|
|
|
return result == true;
|
|
|
|
}
|
|
|
|
|
|
|
|
//! Processes test file
|
|
|
|
tristate AlterTableTester::run(bool &closeAppRequested)
|
|
|
|
{
|
|
|
|
closeAppRequested = false;
|
|
|
|
while (!m_finishedCopying)
|
|
|
|
tqApp->processEvents(300);
|
|
|
|
|
|
|
|
kdDebug() << "Database copied to temporary: " << dbFilename << endl;
|
|
|
|
|
|
|
|
if (!checkItemsNumber(2))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
tristate res = win->openProject( dbFilename, 0 );
|
|
|
|
if (true != res)
|
|
|
|
return res;
|
|
|
|
prj = win->project();
|
|
|
|
|
|
|
|
//open table in design mode
|
|
|
|
res = readLineFromTestFile("designTable");
|
|
|
|
if (true != res)
|
|
|
|
return ~res;
|
|
|
|
|
|
|
|
TQString tableName(testFileLine[1]);
|
|
|
|
KexiPart::Item *item = prj->itemForMimeType("kexi/table", tableName);
|
|
|
|
if (!item) {
|
|
|
|
showError(TQString("No such table '%1'").tqarg(tableName));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
bool openingCancelled;
|
|
|
|
KexiDialogBase* dlg = win->openObject(item, Kexi::DesignViewMode, openingCancelled);
|
|
|
|
if (!dlg) {
|
|
|
|
showError(TQString("Could not open table '%1'").tqarg(item->name()));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
KexiTableDesignerInterface* designerIface
|
|
|
|
= dynamic_cast<KexiTableDesignerInterface*>( dlg->selectedView() );
|
|
|
|
if (!designerIface)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
//dramatic speedup: temporary hide the window and propeditor
|
|
|
|
TQWidget * propeditor
|
|
|
|
= KexiUtils::findFirstChild<TQWidget>(tqApp->mainWidget(), "KexiPropertyEditorView");
|
|
|
|
if (propeditor)
|
|
|
|
propeditor->hide();
|
|
|
|
dlg->hide();
|
|
|
|
|
|
|
|
bool designTable = true;
|
|
|
|
while (!testFileStream.atEnd()) {
|
|
|
|
res = readLineFromTestFile();
|
|
|
|
if (true != res)
|
|
|
|
return ~res;
|
|
|
|
TQString command( testFileLine[0] );
|
|
|
|
if (designTable) {
|
|
|
|
//subcommands available within "designTable" commands
|
|
|
|
if (command=="endDesign") {
|
|
|
|
if (!checkItemsNumber(1))
|
|
|
|
return false;
|
|
|
|
//end of the design session: unhide the window and propeditor
|
|
|
|
dlg->show();
|
|
|
|
if (propeditor)
|
|
|
|
propeditor->show();
|
|
|
|
designTable = false;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
else if (command=="removeField") {
|
|
|
|
if (!checkItemsNumber(2))
|
|
|
|
return false;
|
|
|
|
bool ok;
|
|
|
|
int row = getNumber(testFileLine[1], ok)-1;
|
|
|
|
if (!ok)
|
|
|
|
return false;
|
|
|
|
designerIface->deleteRow( row, true );
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
else if (command=="insertField") {
|
|
|
|
if (!checkItemsNumber(3))
|
|
|
|
return false;
|
|
|
|
bool ok;
|
|
|
|
int row = getNumber(testFileLine[1], ok)-1;
|
|
|
|
if (!ok)
|
|
|
|
return false;
|
|
|
|
designerIface->insertField( row, testFileLine[2], true );
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
else if (command=="insertEmptyRow") {
|
|
|
|
if (!checkItemsNumber(2))
|
|
|
|
return false;
|
|
|
|
bool ok;
|
|
|
|
int row = getNumber(testFileLine[1], ok)-1;
|
|
|
|
if (!ok)
|
|
|
|
return false;
|
|
|
|
designerIface->insertEmptyRow( row, true );
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
else if (command=="changeFieldProperty") {
|
|
|
|
if (!checkItemsNumber(5) || !changeFieldProperty(designerIface))
|
|
|
|
return false;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
else if (command.startsWith("i=")) {
|
|
|
|
bool ok;
|
|
|
|
variableI = command.mid(2).toInt(&ok);
|
|
|
|
if (!ok) {
|
|
|
|
showError(TQString("Invalid variable initialization '%1'").tqarg(command));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
else if (command.startsWith("i++")) {
|
|
|
|
variableI++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
//top-level commands available outside of "designTable"
|
|
|
|
if (command=="showSchema") {
|
|
|
|
if (!checkItemsNumber(1, 2) || !showSchema(dlg, testFileLine[1]=="clipboard"))
|
|
|
|
return false;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
else if (command=="checkSchema") {
|
|
|
|
if (!checkItemsNumber(1) || !checkSchema(dlg))
|
|
|
|
return false;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
else if (command=="showActions") {
|
|
|
|
if (!checkItemsNumber(1, 2) || !showActions(dlg, testFileLine[1]=="clipboard"))
|
|
|
|
return false;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
else if (command=="checkActions") {
|
|
|
|
if (!checkItemsNumber(1) || !checkActions(dlg))
|
|
|
|
return false;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
else if (command=="saveTableDesign") {
|
|
|
|
if (!checkItemsNumber(1) || !saveTableDesign(dlg))
|
|
|
|
return false;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
else if (command=="showTableData") {
|
|
|
|
if (!checkItemsNumber(1, 2) || !showTableData(dlg, testFileLine[1]=="clipboard"))
|
|
|
|
return false;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
else if (command=="checkTableData") {
|
|
|
|
if (!checkItemsNumber(1) || !checkTableData(dlg))
|
|
|
|
return false;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//common commands
|
|
|
|
if (command=="stop") {
|
|
|
|
if (!checkItemsNumber(1))
|
|
|
|
return false;
|
|
|
|
kdDebug() << TQString("Test STOPPED at line %1.").tqarg(testLineNumber) << endl;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else if (command=="closeWindow") {
|
|
|
|
if (!checkItemsNumber(1) || !closeWindow(dlg))
|
|
|
|
return false;
|
|
|
|
else
|
|
|
|
dlg = 0;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
else if (command=="quit") {
|
|
|
|
if (!checkItemsNumber(1) || !closeWindow(dlg))
|
|
|
|
return false;
|
|
|
|
closeAppRequested = true;
|
|
|
|
kdDebug() << TQString("Quitting the application...") << endl;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
showError( TQString("No such command '%1'").tqarg(command) );
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------
|
|
|
|
|
|
|
|
int quit(int result)
|
|
|
|
{
|
|
|
|
testFile.close();
|
|
|
|
delete tqApp;
|
|
|
|
if (newArgv)
|
|
|
|
delete [] newArgv;
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
int main(int argc, char *argv[])
|
|
|
|
{
|
|
|
|
// args: <.altertable test filename>
|
|
|
|
if (argc < 2) {
|
|
|
|
kdWarning() << "Please specify test filename.\nOptions: \n"
|
|
|
|
"\t-close - closes the main window when test finishes" << endl;
|
|
|
|
return quit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
// options:
|
|
|
|
const bool closeOnFinish = argc > 2 && 0==qstrcmp(argv[1], "-close");
|
|
|
|
|
|
|
|
// open test file
|
|
|
|
testFilename = argv[argc-1];
|
|
|
|
testFile.setName(testFilename);
|
|
|
|
if (!testFile.open(IO_ReadOnly)) {
|
|
|
|
kdWarning() << TQString("Opening test file %1 failed.").tqarg(testFilename) << endl;
|
|
|
|
return quit(1);
|
|
|
|
}
|
|
|
|
//load db name
|
|
|
|
testFileStream.setDevice( &testFile );
|
|
|
|
tristate res = readLineFromTestFile("openDatabase");
|
|
|
|
if (true != res)
|
|
|
|
return quit( ~res ? 0 : 1 );
|
|
|
|
origDbFilename = testFileLine[1];
|
|
|
|
dbFilename = origDbFilename + ".tmp";
|
|
|
|
|
|
|
|
newArgc = 2;
|
|
|
|
newArgv = new char*[newArgc];
|
|
|
|
newArgv[0] = qstrdup(argv[0]);
|
|
|
|
newArgv[1] = qstrdup( "--skip-startup-dialog" );
|
|
|
|
|
|
|
|
KAboutData* aboutdata = Kexi::createAboutData();
|
|
|
|
aboutdata->setProgramName( "Kexi Alter Table Test" );
|
|
|
|
int result = KexiMainWindowImpl::create(newArgc, newArgv, aboutdata);
|
|
|
|
if (!tqApp)
|
|
|
|
return quit(result);
|
|
|
|
|
|
|
|
win = KexiMainWindowImpl::self();
|
|
|
|
AlterTableTester tester;
|
|
|
|
//TQObject::connect(win, TQT_SIGNAL(projectOpened()), &tester, TQT_SLOT(run()));
|
|
|
|
|
|
|
|
bool closeAppRequested;
|
|
|
|
res = tester.run(closeAppRequested);
|
|
|
|
if (true != res) {
|
|
|
|
if (false == res)
|
|
|
|
kdWarning() << TQString("Running test for file '%1' failed.").tqarg(testFilename) << endl;
|
|
|
|
return quit(res==false ? 1 : 0);
|
|
|
|
}
|
|
|
|
kdDebug() << TQString("Tests from file '%1': OK").tqarg(testFilename) << endl;
|
|
|
|
result = (closeOnFinish || closeAppRequested) ? 0 : tqApp->exec();
|
|
|
|
quit(result);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
#include "altertable.moc"
|