From 6c7b2d1c8718a964f42c40aab33b2dc4b289d7ec Mon Sep 17 00:00:00 2001 From: Oswald Buddenhagen <oswald.buddenhagen@nokia.com> Date: Thu, 14 Jan 2010 22:58:55 +0100 Subject: [PATCH] actually edit pro files instead of rewriting them from the "AST" that way the file formatting is better preserved. --- .gitignore | 1 + src/plugins/qt4projectmanager/qt4nodes.cpp | 127 +++----- src/plugins/qt4projectmanager/qt4nodes.h | 2 +- src/shared/proparser/prowriter.cpp | 335 +++++++++++---------- src/shared/proparser/prowriter.h | 32 +- tests/auto/profilewriter/main.cpp | 299 ++++++++++++++++++ tests/auto/profilewriter/profilewriter.pro | 6 + 7 files changed, 533 insertions(+), 269 deletions(-) create mode 100644 tests/auto/profilewriter/main.cpp create mode 100644 tests/auto/profilewriter/profilewriter.pro diff --git a/.gitignore b/.gitignore index 15a20a779f4..47abad57d64 100644 --- a/.gitignore +++ b/.gitignore @@ -76,3 +76,4 @@ tests/manual/cplusplus/cplusplus0 tests/auto/qml/qmldesigner/bauhaustests/tst_bauhaus tests/auto/qml/qmldesigner/coretests/tst_qmldesigner_core tests/auto/qml/qmldesigner/propertyeditortests/tst_propertyeditor +tests/auto/profilewriter/tst_profilewriter diff --git a/src/plugins/qt4projectmanager/qt4nodes.cpp b/src/plugins/qt4projectmanager/qt4nodes.cpp index 8c41d626821..d8715d55e73 100644 --- a/src/plugins/qt4projectmanager/qt4nodes.cpp +++ b/src/plugins/qt4projectmanager/qt4nodes.cpp @@ -606,23 +606,6 @@ bool Qt4PriFileNode::saveModifiedEditors(const QString &path) return true; } -static void findProVariables(ProBlock *block, const QStringList &vars, - QList<ProVariable *> *proVars) -{ - foreach (ProItem *item, block->items()) { - if (item->kind() == ProItem::BlockKind) { - ProBlock *subBlock = static_cast<ProBlock *>(item); - if (subBlock->blockKind() == ProBlock::VariableKind) { - ProVariable *proVar = static_cast<ProVariable*>(subBlock); - if (vars.contains(proVar->variable())) - *proVars << proVar; - } else { - findProVariables(subBlock, vars, proVars); - } - } - } -} - void Qt4PriFileNode::changeFiles(const FileType fileType, const QStringList &filePaths, QStringList *notChanged, @@ -637,101 +620,67 @@ void Qt4PriFileNode::changeFiles(const FileType fileType, if (!saveModifiedEditors(m_projectFilePath)) return; - ProFileReader *reader = m_project->createProFileReader(m_qt4ProFileNode); - ProFile *includeFile = reader->parsedProFile(m_projectFilePath); - m_project->destroyProFileReader(reader); - if (!includeFile) { - m_project->proFileParseError(tr("Error while changing pro file %1.").arg(m_projectFilePath)); - return; + QStringList lines; + ProFile *includeFile; + { + QString contents; + { + QFile qfile(m_projectFilePath); + if (qfile.open(QIODevice::ReadOnly | QIODevice::Text)) { + contents = QString::fromLatin1(qfile.readAll()); // yes, really latin1 + qfile.close(); + lines = contents.split(QLatin1Char('\n')); + while (lines.last().isEmpty()) + lines.removeLast(); + } else { + m_project->proFileParseError(tr("Error while reading PRO file %1: %2") + .arg(m_projectFilePath, qfile.errorString())); + return; + } + } + + ProFileReader *reader = m_project->createProFileReader(m_qt4ProFileNode); + includeFile = reader->parsedProFile(m_projectFilePath, contents); + m_project->destroyProFileReader(reader); } const QStringList vars = varNames(fileType); QDir priFileDir = QDir(m_qt4ProFileNode->m_projectDir); if (change == AddToProFile) { - ProVariable *proVar = 0; - - // Check if variable item exists as child of root item - foreach (ProItem *item, includeFile->items()) { - if (item->kind() == ProItem::BlockKind) { - ProBlock *block = static_cast<ProBlock *>(item); - if (block->blockKind() == ProBlock::VariableKind) { - proVar = static_cast<ProVariable*>(block); - if (vars.contains(proVar->variable()) - && proVar->variableOperator() != ProVariable::RemoveOperator - && proVar->variableOperator() != ProVariable::ReplaceOperator) - break; - proVar = 0; - } - } - } - - if (!proVar) { - // Create & append new variable item - - // TODO: This will always store e.g. a source file in SOURCES and not OBJECTIVE_SOURCES - proVar = new ProVariable(vars.first(), includeFile); - proVar->setVariableOperator(ProVariable::AddOperator); - includeFile->appendItem(proVar); - } - - const QString &proFilePath = includeFile->fileName(); - foreach (const QString &filePath, filePaths) { - if (filePath == proFilePath) - continue; - const QString &relativeFilePath = priFileDir.relativeFilePath(filePath); - proVar->appendItem(new ProValue(relativeFilePath, proVar)); - notChanged->removeOne(filePath); - } + ProWriter::addFiles(includeFile, &lines, priFileDir, filePaths, vars); + notChanged->clear(); } else { // RemoveFromProFile - QList<ProVariable *> proVars; - findProVariables(includeFile, vars, &proVars); - - QStringList relativeFilePaths; - foreach (const QString &absoluteFilePath, filePaths) - relativeFilePaths << priFileDir.relativeFilePath(absoluteFilePath); - - foreach (ProVariable *proVar, proVars) { - if (proVar->variableOperator() != ProVariable::RemoveOperator - && proVar->variableOperator() != ProVariable::ReplaceOperator) { - QList<ProItem *> values = proVar->items(); - for (int i = values.count(); --i >= 0; ) { - ProItem *item = values.at(i); - if (item->kind() == ProItem::ValueKind) { - ProValue *val = static_cast<ProValue *>(item); - if (relativeFilePaths.contains(val->value())) { - notChanged->removeOne(priFileDir.absoluteFilePath(val->value())); - delete values.takeAt(i); - } - } - } - proVar->setItems(values); - } - } + *notChanged = ProWriter::removeFiles(includeFile, &lines, priFileDir, filePaths, vars); } // save file - save(includeFile); + save(lines); includeFile->deref(); } -void Qt4PriFileNode::save(ProFile *includeFile) +void Qt4PriFileNode::save(const QStringList &lines) { Core::ICore *core = Core::ICore::instance(); Core::FileManager *fileManager = core->fileManager(); - QList<Core::IFile *> allFileHandles = fileManager->managedFiles(includeFile->fileName()); + QList<Core::IFile *> allFileHandles = fileManager->managedFiles(m_projectFilePath); Core::IFile *modifiedFileHandle = 0; foreach (Core::IFile *file, allFileHandles) - if (file->fileName() == includeFile->fileName()) + if (file->fileName() == m_projectFilePath) modifiedFileHandle = file; if (modifiedFileHandle) fileManager->blockFileChange(modifiedFileHandle); - ProWriter pw; - const bool ok = pw.write(includeFile, includeFile->fileName()); - Q_UNUSED(ok) - m_project->qt4ProjectManager()->notifyChanged(includeFile->fileName()); + QFile qfile(m_projectFilePath); + if (qfile.open(QIODevice::WriteOnly | QIODevice::Text)) { + foreach (const QString &str, lines) { + qfile.write(str.toLatin1()); // yes, really latin1 + qfile.write("\n"); + } + qfile.close(); + } + m_project->qt4ProjectManager()->notifyChanged(m_projectFilePath); if (modifiedFileHandle) fileManager->unblockFileChange(modifiedFileHandle); diff --git a/src/plugins/qt4projectmanager/qt4nodes.h b/src/plugins/qt4projectmanager/qt4nodes.h index e03a4bf24ed..d26787faedf 100644 --- a/src/plugins/qt4projectmanager/qt4nodes.h +++ b/src/plugins/qt4projectmanager/qt4nodes.h @@ -174,7 +174,7 @@ private slots: void scheduleUpdate(); private: - void save(ProFile *includeFile); + void save(const QStringList &lines); bool priFileWritable(const QString &path); bool saveModifiedEditors(const QString &path); diff --git a/src/shared/proparser/prowriter.cpp b/src/shared/proparser/prowriter.cpp index 39236c0656d..5e0539a85aa 100644 --- a/src/shared/proparser/prowriter.cpp +++ b/src/shared/proparser/prowriter.cpp @@ -30,175 +30,200 @@ #include "proitems.h" #include "prowriter.h" -#include <QtCore/QFile> +#include <QtCore/QDir> +#include <QtCore/QPair> using namespace Qt4ProjectManager::Internal; -bool ProWriter::write(ProFile *profile, const QString &fileName) +void ProWriter::addFiles(ProFile *profile, QStringList *lines, + const QDir &proFileDir, const QStringList &filePaths, + const QStringList &vars) { - QFile data(fileName); - if (!data.open(QFile::WriteOnly|QFile::Text)) - return false; - - m_writeState = 0; - m_comment.clear(); - m_out.setDevice(&data); - writeItem(profile, QString()); - data.close(); - - return true; -} - -QString ProWriter::contents(ProFile *profile) -{ - QString result; - - m_writeState = 0; - m_comment.clear(); - m_out.setString(&result, QIODevice::WriteOnly); - writeItem(profile, QString()); - - return result; -} - -QString ProWriter::fixComment(const QString &comment, const QString &indent) const -{ - QString result = comment; - result = result.replace(QLatin1Char('\n'), - QLatin1Char('\n') + indent + QLatin1String("# ")); - return QLatin1String("# ") + result; -} - -void ProWriter::writeValue(ProValue *value, const QString &indent) -{ - if (m_writeState & NewLine) { - m_out << indent << QLatin1String(" "); - m_writeState &= ~NewLine; - } - - m_out << value->value(); - - if (!(m_writeState & LastItem)) - m_out << QLatin1String(" \\"); - - if (!value->comment().isEmpty()) - m_out << QLatin1Char(' ') << fixComment(value->comment(), indent); - - m_out << endl; - m_writeState |= NewLine; -} - -void ProWriter::writeOther(ProItem *item, const QString &indent) -{ - if (m_writeState & NewLine) { - m_out << indent; - m_writeState &= ~NewLine; - } - - if (item->kind() == ProItem::FunctionKind) { - ProFunction *v = static_cast<ProFunction*>(item); - m_out << v->text(); - } else if (item->kind() == ProItem::ConditionKind) { - ProCondition *v = static_cast<ProCondition*>(item); - m_out << v->text(); - } else if (item->kind() == ProItem::OperatorKind) { - ProOperator *v = static_cast<ProOperator*>(item); - if (v->operatorKind() == ProOperator::OrOperator) - m_out << QLatin1Char('|'); - else - m_out << QLatin1Char('!'); + // Check if variable item exists as child of root item + foreach (ProItem *item, profile->items()) { + if (item->kind() == ProItem::BlockKind) { + ProBlock *block = static_cast<ProBlock *>(item); + if (block->blockKind() == ProBlock::VariableKind) { + ProVariable *proVar = static_cast<ProVariable*>(block); + if (vars.contains(proVar->variable()) + && proVar->variableOperator() != ProVariable::RemoveOperator + && proVar->variableOperator() != ProVariable::ReplaceOperator) { + + int lineNo = proVar->lineNumber() - 1; + for (; lineNo < lines->count(); lineNo++) { + QString line = lines->at(lineNo); + int idx = line.indexOf(QLatin1Char('#')); + if (idx >= 0) + line.truncate(idx); + while (line.endsWith(QLatin1Char(' ')) || line.endsWith(QLatin1Char('\t'))) + line.chop(1); + if (line.isEmpty()) { + if (idx >= 0) + continue; + break; + } + if (!line.endsWith(QLatin1Char('\\'))) { + (*lines)[lineNo].insert(line.length(), QLatin1String(" \\")); + lineNo++; + break; + } + } + QString added; + foreach (const QString &filePath, filePaths) + added += QLatin1String(" ") + proFileDir.relativeFilePath(filePath) + + QLatin1String(" \\\n"); + added.chop(3); + lines->insert(lineNo, added); + return; + } + } + } } - if (!item->comment().isEmpty()) { - if (!m_comment.isEmpty()) - m_comment += QLatin1Char('\n'); - m_comment += item->comment(); - } + // Create & append new variable item + QString added = QLatin1Char('\n') + vars.first() + QLatin1String(" +="); + foreach (const QString &filePath, filePaths) + added += QLatin1String(" \\\n ") + proFileDir.relativeFilePath(filePath); + *lines << added; } -void ProWriter::writeBlock(ProBlock *block, const QString &indent) +static void findProVariables(ProBlock *block, const QStringList &vars, + QList<ProVariable *> *proVars) { - if (m_writeState & NewLine) { - m_out << indent; - m_writeState &= ~NewLine; - } - - if (!block->comment().isEmpty()) { - if (!(m_writeState & FirstItem)) - m_out << endl << indent; - m_out << fixComment(block->comment(), indent) << endl << indent; - } - - QString newindent = indent; - if (block->blockKind() & ProBlock::VariableKind) { - ProVariable *v = static_cast<ProVariable*>(block); - m_out << v->variable(); - switch (v->variableOperator()) { - case ProVariable::AddOperator: - m_out << QLatin1String(" += "); break; - case ProVariable::RemoveOperator: - m_out << QLatin1String(" -= "); break; - case ProVariable::ReplaceOperator: - m_out << QLatin1String(" ~= "); break; - case ProVariable::SetOperator: - m_out << QLatin1String(" = "); break; - case ProVariable::UniqueAddOperator: - m_out << QLatin1String(" *= "); break; - } - } else if (block->blockKind() & ProBlock::ScopeContentsKind) { - if (block->items().count() > 1) { - newindent = indent + QLatin1String(" "); - m_out << QLatin1String(" { "); - if (!m_comment.isEmpty()) { - m_out << fixComment(m_comment, indent); - m_comment.clear(); + foreach (ProItem *item, block->items()) { + if (item->kind() == ProItem::BlockKind) { + ProBlock *subBlock = static_cast<ProBlock *>(item); + if (subBlock->blockKind() == ProBlock::VariableKind) { + ProVariable *proVar = static_cast<ProVariable*>(subBlock); + if (vars.contains(proVar->variable())) + *proVars << proVar; + } else { + findProVariables(subBlock, vars, proVars); } - m_out << endl; - m_writeState |= NewLine; - } else { - m_out << QLatin1Char(':'); - } - } - - QList<ProItem*> items = block->items(); - for (int i = 0; i < items.count(); ++i) { - m_writeState &= ~LastItem; - m_writeState &= ~FirstItem; - if (i == 0) - m_writeState |= FirstItem; - if (i == items.count() - 1) - m_writeState |= LastItem; - writeItem(items.at(i), newindent); - } - - if ((block->blockKind() & ProBlock::ScopeContentsKind) && (block->items().count() > 1)) { - if (m_writeState & NewLine) { - m_out << indent; - m_writeState &= ~NewLine; } - m_out << QLatin1Char('}'); - } - - if (!m_comment.isEmpty()) { - m_out << fixComment(m_comment, indent); - m_out << endl; - m_writeState |= NewLine; - m_comment.clear(); - } - - if (!(m_writeState & NewLine)) { - m_out << endl; - m_writeState |= NewLine; } } -void ProWriter::writeItem(ProItem *item, const QString &indent) +QStringList ProWriter::removeFiles(ProFile *profile, QStringList *lines, + const QDir &proFileDir, const QStringList &filePaths, + const QStringList &vars) { - if (item->kind() == ProItem::ValueKind) { - writeValue(static_cast<ProValue*>(item), indent); - } else if (item->kind() == ProItem::BlockKind) { - writeBlock(static_cast<ProBlock*>(item), indent); - } else { - writeOther(item, indent); + QStringList notChanged = filePaths; + + QList<ProVariable *> proVars; + findProVariables(profile, vars, &proVars); + + // This is a tad stupid - basically, it can remove only entries which + // the above code added. + QStringList relativeFilePaths; + foreach (const QString &absoluteFilePath, filePaths) + relativeFilePaths << proFileDir.relativeFilePath(absoluteFilePath); + + // This code expects proVars to be sorted by the variables' appearance in the file. + int delta = 1; + foreach (ProVariable *proVar, proVars) { + if (proVar->variableOperator() != ProVariable::RemoveOperator + && proVar->variableOperator() != ProVariable::ReplaceOperator) { + + bool first = true; + int lineNo = proVar->lineNumber() - delta; + typedef QPair<int, int> ContPos; + QList<ContPos> contPos; + while (lineNo < lines->count()) { + QString &line = (*lines)[lineNo]; + int lineLen = line.length(); + bool killed = false; + bool saved = false; + int idx = line.indexOf(QLatin1Char('#')); + if (idx >= 0) + lineLen = idx; + QChar *chars = line.data(); + forever { + if (!lineLen) { + if (idx >= 0) + goto nextLine; + goto nextVar; + } + QChar c = chars[lineLen - 1]; + if (c != QLatin1Char(' ') && c != QLatin1Char('\t')) + break; + lineLen--; + } + { + int contCol = -1; + if (chars[lineLen - 1] == QLatin1Char('\\')) + contCol = --lineLen; + int colNo = 0; + if (first) { + colNo = line.indexOf(QLatin1Char('=')) + 1; + first = false; + saved = true; + } + while (colNo < lineLen) { + QChar c = chars[colNo]; + if (c == QLatin1Char(' ') || c == QLatin1Char('\t')) { + colNo++; + continue; + } + int varCol = colNo; + while (colNo < lineLen) { + QChar c = chars[colNo]; + if (c == QLatin1Char(' ') || c == QLatin1Char('\t')) + break; + colNo++; + } + QString fn = line.mid(varCol, colNo - varCol); + if (relativeFilePaths.contains(fn)) { + notChanged.removeOne(QDir::cleanPath(proFileDir.absoluteFilePath(fn))); + if (colNo < lineLen) + colNo++; + else if (varCol) + varCol--; + int len = colNo - varCol; + colNo = varCol; + line.remove(varCol, len); + lineLen -= len; + contCol -= len; + idx -= len; + if (idx >= 0) + line.insert(idx, QLatin1String("# ") + fn + QLatin1Char(' ')); + killed = true; + } else { + saved = true; + } + } + if (saved) { + // Entries remained + contPos.clear(); + } else if (killed) { + // Entries existed, but were all removed + if (contCol < 0) { + // This is the last line, so clear continuations leading to it + foreach (const ContPos &pos, contPos) { + QString &bline = (*lines)[pos.first]; + bline.remove(pos.second, 1); + if (pos.second == bline.length()) + while (bline.endsWith(QLatin1Char(' ')) + || bline.endsWith(QLatin1Char('\t'))) + bline.chop(1); + } + contPos.clear(); + } + if (idx < 0) { + // Not even a comment stayed behind, so zap the line + lines->removeAt(lineNo); + delta++; + continue; + } + } + if (contCol >= 0) + contPos.append(qMakePair(lineNo, contCol)); + } + nextLine: + lineNo++; + } + nextVar: ; + } } + return notChanged; } diff --git a/src/shared/proparser/prowriter.h b/src/shared/proparser/prowriter.h index 27a2bea92f8..804c11e1d5e 100644 --- a/src/shared/proparser/prowriter.h +++ b/src/shared/proparser/prowriter.h @@ -32,13 +32,11 @@ #include "namespace_global.h" -#include <QtCore/QTextStream> +#include <QStringList> QT_BEGIN_NAMESPACE +class QDir; class ProFile; -class ProValue; -class ProItem; -class ProBlock; QT_END_NAMESPACE namespace Qt4ProjectManager { @@ -47,26 +45,12 @@ namespace Internal { class ProWriter { public: - bool write(ProFile *profile, const QString &fileName); - QString contents(ProFile *profile); - -protected: - QString fixComment(const QString &comment, const QString &indent) const; - void writeValue(ProValue *value, const QString &indent); - void writeOther(ProItem *item, const QString &indent); - void writeBlock(ProBlock *block, const QString &indent); - void writeItem(ProItem *item, const QString &indent); - -private: - enum ProWriteState { - NewLine = 0x01, - FirstItem = 0x02, - LastItem = 0x04 - }; - - QTextStream m_out; - int m_writeState; - QString m_comment; + static void addFiles(ProFile *profile, QStringList *lines, + const QDir &proFileDir, const QStringList &filePaths, + const QStringList &vars); + static QStringList removeFiles(ProFile *profile, QStringList *lines, + const QDir &proFileDir, const QStringList &filePaths, + const QStringList &vars); }; } // namespace Internal diff --git a/tests/auto/profilewriter/main.cpp b/tests/auto/profilewriter/main.cpp new file mode 100644 index 00000000000..f37eb761f38 --- /dev/null +++ b/tests/auto/profilewriter/main.cpp @@ -0,0 +1,299 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "profileevaluator.h" +#include "prowriter.h" + +#include <QtTest/QtTest> +//#include <QtCore/QSet> + +#define BASE_DIR "/some/stuff" + +class tst_ProFileWriter : public QObject +{ + Q_OBJECT + +private slots: + void edit_data(); + void edit(); + void multiVar(); +}; + +void tst_ProFileWriter::edit_data() +{ + QTest::addColumn<bool>("add"); + QTest::addColumn<QStringList>("files"); + QTest::addColumn<QString>("input"); + QTest::addColumn<QString>("output"); + + struct Case { + bool add; + const char *title; + const char * const *files; + const char *input; + const char *output; + }; + + static const char *f_foo[] = { "foo", 0 }; + static const char *f_foo_bar[] = { "foo", "bar", 0 }; + static const Case cases[] = { + // Adding entries + { + true, "add new", f_foo, + "# test file", + "# test file\n" + "\n" + "SOURCES += \\\n" + " foo" + }, + { + true, "add new ignoring scoped", f_foo, + "unix:SOURCES = some files", + "unix:SOURCES = some files\n" + "\n" + "SOURCES += \\\n" + " foo" + }, + { + true, "add to existing", f_foo, + "SOURCES = some files", + "SOURCES = some files \\\n" + " foo" + }, + { + true, "add to existing after comment", f_foo, + "SOURCES = some files # comment", + "SOURCES = some files \\ # comment\n" + " foo" + }, + { + true, "add to existing after comment line", f_foo, + "SOURCES = some \\\n" + " # comment\n" + " files", + "SOURCES = some \\\n" + " # comment\n" + " files \\\n" + " foo" + }, + + // Removing entries + { + false, "remove fail", f_foo, + "SOURCES = bak bar", + "SOURCES = bak bar" + }, + { + false, "remove one-line middle", f_foo, + "SOURCES = bak foo bar", + "SOURCES = bak bar" + }, + { + false, "remove one-line trailing", f_foo, + "SOURCES = bak bar foo", + "SOURCES = bak bar" + }, + { + false, "remove multi-line single leading", f_foo, + "SOURCES = foo \\\n" + " bak \\\n" + " bar", + "SOURCES = \\\n" + " bak \\\n" + " bar" + }, + { + false, "remove multi-line single middle", f_foo, + "SOURCES = bak \\\n" + " foo \\\n" + " bar", + "SOURCES = bak \\\n" + " bar" + }, + { + false, "remove multi-line single trailing", f_foo, + "SOURCES = bak \\\n" + " bar \\\n" + " foo", + "SOURCES = bak \\\n" + " bar" + }, + { + false, "remove multi-line single leading with comment", f_foo, + "SOURCES = foo \\ # comment\n" + " bak \\\n" + " bar", + "SOURCES = \\ # foo # comment\n" + " bak \\\n" + " bar" + }, + { + false, "remove multi-line single middle with comment", f_foo, + "SOURCES = bak \\\n" + " foo \\ # comment\n" + " bar", + "SOURCES = bak \\\n" + " \\ # foo # comment\n" + " bar" + }, + { + false, "remove multi-line single trailing with comment", f_foo, + "SOURCES = bak \\\n" + " bar \\\n" + " foo # comment", + "SOURCES = bak \\\n" + " bar\n" + " # foo # comment" + }, + { + false, "remove multi-line single trailing after empty line", f_foo, + "SOURCES = bak \\\n" + " bar \\\n" + " \\\n" + " foo", + "SOURCES = bak \\\n" + " bar\n" + }, + { + false, "remove multi-line single trailing after comment line", f_foo, + "SOURCES = bak \\\n" + " bar \\\n" + " # just a comment\n" + " foo", + "SOURCES = bak \\\n" + " bar\n" + " # just a comment" + }, + { + false, "remove multi-line single trailing after empty line with comment", f_foo, + "SOURCES = bak \\\n" + " bar \\\n" + " \\ # just a comment\n" + " foo", + "SOURCES = bak \\\n" + " bar\n" + " # just a comment" + }, + { + false, "remove multiple one-line middle", f_foo_bar, + "SOURCES = bak foo bar baz", + "SOURCES = bak baz" + }, + { + false, "remove multiple one-line trailing", f_foo_bar, + "SOURCES = bak baz foo bar", + "SOURCES = bak baz" + }, + { + false, "remove multiple one-line interleaved", f_foo_bar, + "SOURCES = bak foo baz bar", + "SOURCES = bak baz" + }, + { + false, "remove multiple one-line middle with comment", f_foo_bar, + "SOURCES = bak foo bar baz # comment", + "SOURCES = bak baz # bar # foo # comment" + }, + { + false, "remove multi-line multiple trailing with empty line with comment", f_foo_bar, + "SOURCES = bak \\\n" + " bar \\\n" + " \\ # just a comment\n" + " foo", + "SOURCES = bak\n" + " # just a comment" + }, + }; + + for (uint i = 0; i < sizeof(cases) / sizeof(cases[0]); i++) { + const Case *_case = &cases[i]; + QStringList files; + for (const char * const *file = _case->files; *file; file++) + files << QString::fromLatin1(BASE_DIR "/") + QString::fromLatin1(*file); + QTest::newRow(_case->title) + << _case->add + << files + << QString::fromLatin1(_case->input) + << QString::fromLatin1(_case->output); + } +} + +void tst_ProFileWriter::edit() +{ + QFETCH(bool, add); + QFETCH(QStringList, files); + QFETCH(QString, input); + QFETCH(QString, output); + + QDir baseDir(BASE_DIR); + QStringList lines = input.split(QLatin1String("\n")); + QStringList vars; vars << QLatin1String("SOURCES"); + + ProFileOption option; + ProFileEvaluator reader(&option); + ProFile *proFile = reader.parsedProFile(BASE_DIR "/test.pro", input); + QVERIFY(proFile); + if (add) + Qt4ProjectManager::Internal::ProWriter::addFiles(proFile, &lines, baseDir, files, vars); + else + Qt4ProjectManager::Internal::ProWriter::removeFiles(proFile, &lines, baseDir, files, vars); + + QCOMPARE(lines.join(QLatin1String("\n")), output); +} + +void tst_ProFileWriter::multiVar() +{ + QDir baseDir(BASE_DIR); + QString input = QLatin1String( + "SOURCES = foo bar\n" + "# comment line\n" + "HEADERS = baz bak" + ); + QStringList lines = input.split(QLatin1String("\n")); + QString output = QLatin1String( + "SOURCES = bar\n" + "# comment line\n" + "HEADERS = baz" + ); + QStringList files; files + << QString::fromLatin1(BASE_DIR "/foo") + << QString::fromLatin1(BASE_DIR "/bak"); + QStringList vars; vars << QLatin1String("SOURCES") << QLatin1String("HEADERS"); + + ProFileOption option; + ProFileEvaluator reader(&option); + ProFile *proFile = reader.parsedProFile(BASE_DIR "/test.pro", input); + QVERIFY(proFile); + Qt4ProjectManager::Internal::ProWriter::removeFiles(proFile, &lines, baseDir, files, vars); + + QCOMPARE(lines.join(QLatin1String("\n")), output); +} + +QTEST_MAIN(tst_ProFileWriter) +#include "main.moc" diff --git a/tests/auto/profilewriter/profilewriter.pro b/tests/auto/profilewriter/profilewriter.pro new file mode 100644 index 00000000000..774e09f666b --- /dev/null +++ b/tests/auto/profilewriter/profilewriter.pro @@ -0,0 +1,6 @@ +load(qttest_p4) + +include(../../../src/shared/proparser/proparser.pri) + +SOURCES += main.cpp +QT -= gui -- GitLab