/****************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the Qt Linguist of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** No Commercial Usage
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the Technology Preview License Agreement accompanying
** this package.
**
** 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.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
**
**
**
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#if CHECK_SIMTEXTH
#include "../shared/simtexth.h"
#endif
#include <QtCore/QDir>
#include <QtCore/QDebug>
#include <QtCore/QFile>
#include <QtCore/QByteArray>
#include <QtTest/QtTest>
class tst_lupdate : public QObject
{
Q_OBJECT
public:
tst_lupdate();
private slots:
void good_data();
void good();
void commandline_data();
void commandline();
#if CHECK_SIMTEXTH
void simtexth();
void simtexth_data();
#endif
private:
QString m_cmdLupdate;
QString m_basePath;
void doCompare(const QStringList &actual, const QString &expectedFn, bool err);
void doCompare(const QString &actualFn, const QString &expectedFn, bool err);
};
tst_lupdate::tst_lupdate()
{
QString binPath = QLibraryInfo::location(QLibraryInfo::BinariesPath);
m_cmdLupdate = binPath + QLatin1String("/lupdate");
m_basePath = QDir::currentPath() + QLatin1String("/testdata/");
}
static bool prepareMatch(const QString &expect, QString *tmpl, int *require, int *accept)
{
if (expect.startsWith(QLatin1Char('\\'))) {
*tmpl = expect.mid(1);
*require = *accept = 1;
} else if (expect.startsWith(QLatin1Char('?'))) {
*tmpl = expect.mid(1);
*require = 0;
*accept = 1;
} else if (expect.startsWith(QLatin1Char('*'))) {
*tmpl = expect.mid(1);
*require = 0;
*accept = INT_MAX;
} else if (expect.startsWith(QLatin1Char('+'))) {
*tmpl = expect.mid(1);
*require = 1;
*accept = INT_MAX;
} else if (expect.startsWith(QLatin1Char('{'))) {
int brc = expect.indexOf(QLatin1Char('}'), 1);
if (brc < 0)
return false;
*tmpl = expect.mid(brc + 1);
QString sub = expect.mid(1, brc - 1);
int com = sub.indexOf(QLatin1Char(','));
bool ok;
if (com < 0) {
*require = *accept = sub.toInt(&ok);
return ok;
} else {
*require = sub.left(com).toInt();
*accept = sub.mid(com + 1).toInt(&ok);
if (!ok)
*accept = INT_MAX;
return *accept >= *require;
}
} else {
*tmpl = expect;
*require = *accept = 1;
}
return true;
}
void tst_lupdate::doCompare(const QStringList &actual, const QString &expectedFn, bool err)
{
QFile file(expectedFn);
QVERIFY2(file.open(QIODevice::ReadOnly | QIODevice::Text), qPrintable(expectedFn));
QStringList expected = QString(file.readAll()).split('\n');
int ei = 0, ai = 0, em = expected.size(), am = actual.size();
int oei = 0, oai = 0, oem = em, oam = am;
int require = 0, accept = 0;
QString tmpl;
forever {
if (!accept) {
oei = ei, oai = ai;
if (ei == em) {
if (ai == am)
return;
break;
}
if (!prepareMatch(expected.at(ei++), &tmpl, &require, &accept))
QFAIL(qPrintable(QString("Malformed expected %1 at %3:%2")
.arg(err ? "output" : "result").arg(ei).arg(expectedFn)));
}
if (ai == am) {
if (require <= 0) {
accept = 0;
continue;
}
break;
}
if (err ? !QRegExp(tmpl).exactMatch(actual.at(ai)) : (actual.at(ai) != tmpl)) {
if (require <= 0) {
accept = 0;
continue;
}
ei--;
require = accept = 0;
forever {
if (!accept) {
oem = em, oam = am;
if (ei == em)
break;
if (!prepareMatch(expected.at(--em), &tmpl, &require, &accept))
QFAIL(qPrintable(QString("Malformed expected %1 at %3:%2")
.arg(err ? "output" : "result")
.arg(em + 1).arg(expectedFn)));
}
if (ai == am || (err ? !QRegExp(tmpl).exactMatch(actual.at(am - 1)) :
(actual.at(am - 1) != tmpl))) {
if (require <= 0) {
accept = 0;
continue;
}
break;
}
accept--;
require--;
am--;
}
break;
}
accept--;
require--;
ai++;
}
QByteArray diff;
for (int j = qMax(0, oai - 3); j < oai; j++)
diff += actual.at(j) + '\n';
diff += "<<<<<<< got\n";
for (int j = oai; j < oam; j++) {
diff += actual.at(j) + '\n';
if (j >= oai + 5) {
diff += "...\n";
break;
}
}
diff += "=========\n";
for (int j = oei; j < oem; j++) {
diff += expected.at(j) + '\n';
if (j >= oei + 5) {
diff += "...\n";
break;
}
}
diff += ">>>>>>> expected\n";
for (int j = oam; j < qMin(oam + 3, actual.size()); j++)
diff += actual.at(j) + '\n';
QFAIL(qPrintable((err ? "Output for " : "Result for ") + expectedFn + " does not meet expectations:\n" + diff));
}
void tst_lupdate::doCompare(const QString &actualFn, const QString &expectedFn, bool err)
{
QFile afile(actualFn);
QVERIFY2(afile.open(QIODevice::ReadOnly | QIODevice::Text), qPrintable(actualFn));
QStringList actual = QString(afile.readAll()).split('\n');
doCompare(actual, expectedFn, err);
}
void tst_lupdate::good_data()
{
QTest::addColumn<QString>("directory");
QDir parsingDir(m_basePath + "good");
QStringList dirs = parsingDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot, QDir::Name);
#ifndef Q_OS_WIN
dirs.removeAll(QLatin1String("backslashes"));
#endif
foreach (const QString &dir, dirs)
QTest::newRow(dir.toLocal8Bit()) << dir;
}
void tst_lupdate::good()
{
QFETCH(QString, directory);
QString dir = m_basePath + "good/" + directory;
qDebug() << "Checking...";
QString generatedtsfile(dir + QLatin1String("/project.ts"));
// look for a command
QString lupdatecmd;
QFile file(dir + "/lupdatecmd");
if (file.exists()) {
QVERIFY2(file.open(QIODevice::ReadOnly | QIODevice::Text), qPrintable(file.fileName()));
while (!file.atEnd()) {
QByteArray cmdstring = file.readLine().simplified();
if (cmdstring.startsWith('#'))
continue;
if (cmdstring.startsWith("lupdate")) {
cmdstring.remove(0, 8);
lupdatecmd.append(cmdstring);
break;
} else if (cmdstring.startsWith("TRANSLATION:")) {
cmdstring.remove(0, 12);
generatedtsfile = dir + QLatin1Char('/') + cmdstring.trimmed();
}
}
file.close();
}
QFile::remove(generatedtsfile);
QString beforetsfile = generatedtsfile + QLatin1String(".before");
if (QFile::exists(beforetsfile))
QVERIFY2(QFile::copy(beforetsfile, generatedtsfile), qPrintable(beforetsfile));
if (lupdatecmd.isEmpty())
lupdatecmd = QLatin1String("project.pro");
lupdatecmd.prepend("-silent ");
QProcess proc;
proc.setWorkingDirectory(dir);
proc.setProcessChannelMode(QProcess::MergedChannels);
proc.start(m_cmdLupdate + ' ' + lupdatecmd, QIODevice::ReadWrite | QIODevice::Text);
QVERIFY2(proc.waitForFinished(5000), qPrintable(lupdatecmd));
QByteArray output = proc.readAll();
QVERIFY2(proc.exitStatus() == QProcess::NormalExit,
"\"lupdate " + lupdatecmd.toLatin1() + "\" crashed\n" + output);
QVERIFY2(!proc.exitCode(),
"\"lupdate " + lupdatecmd.toLatin1() + "\" exited with code " +
QByteArray::number(proc.exitCode()) + "\n" + proc.readAll());
// If the file expectedoutput.txt exists, compare the
// console output with the content of that file
QFile outfile(dir + "/expectedoutput.txt");
if (outfile.exists()) {
QStringList errslist = QString::fromLatin1(output).split(QLatin1Char('\n'));
doCompare(errslist, outfile.fileName(), true);
if (QTest::currentTestFailed())
return;
}
QString expectedFile = generatedtsfile + QLatin1String(".result");
doCompare(generatedtsfile, expectedFile, false);
}
void tst_lupdate::commandline_data()
{
QTest::addColumn<QString>("currentPath");
QTest::addColumn<QString>("commandline");
QTest::addColumn<QString>("generatedtsfile");
QTest::addColumn<QString>("expectedtsfile");
QTest::newRow("Recursive scan") << QString("recursivescan")
<< QString(". -ts foo.ts") << QString("foo.ts") << QString("foo.ts.result");
QTest::newRow("Deep path argument") << QString("recursivescan")
<< QString("sub/finddialog.cpp -ts bar.ts") << QString("bar.ts") << QString("bar.ts.result");
}
void tst_lupdate::commandline()
{
QFETCH(QString, currentPath);
QFETCH(QString, commandline);
QFETCH(QString, generatedtsfile);
QFETCH(QString, expectedtsfile);
QString generated =
m_basePath + currentPath + QLatin1Char('/') + generatedtsfile;
QFile gen(generated);
if (gen.exists())
QVERIFY(gen.remove());
QProcess proc;
proc.setWorkingDirectory(m_basePath + currentPath);
proc.setProcessChannelMode(QProcess::MergedChannels);
proc.start(m_cmdLupdate + " -silent " + commandline, QIODevice::ReadWrite | QIODevice::Text);
QVERIFY2(proc.waitForFinished(5000), qPrintable(commandline));
QVERIFY2(proc.exitStatus() == QProcess::NormalExit,
"\"lupdate -silent " + commandline.toLatin1() + "\" crashed\n" + proc.readAll());
QVERIFY2(!proc.exitCode(),
"\"lupdate -silent " + commandline.toLatin1() + "\" exited with code " +
QByteArray::number(proc.exitCode()) + "\n" + proc.readAll());
doCompare(generated, m_basePath + currentPath + QLatin1Char('/') + expectedtsfile, false);
}
#if CHECK_SIMTEXTH
void tst_lupdate::simtexth()
{
QFETCH(QString, one);
QFETCH(QString, two);
QFETCH(int, expected);
int measured = getSimilarityScore(one, two.toLatin1());
QCOMPARE(measured, expected);
}
void tst_lupdate::simtexth_data()
{
using namespace QTest;
addColumn<QString>("one");
addColumn<QString>("two");
addColumn<int>("expected");
newRow("00") << "" << "" << 1024;
newRow("01") << "a" << "a" << 1024;
newRow("02") << "ab" << "ab" << 1024;
newRow("03") << "abc" << "abc" << 1024;
newRow("04") << "abcd" << "abcd" << 1024;
}
#endif
QTEST_MAIN(tst_lupdate)
#include "tst_lupdate.moc"