tests/auto/linguist/lupdate/tst_lupdate.cpp
changeset 18 2f34d5167611
parent 3 41300fa6a67c
child 33 3e2da88830cd
equal deleted inserted replaced
3:41300fa6a67c 18:2f34d5167611
     1 /****************************************************************************
     1 /****************************************************************************
     2 **
     2 **
     3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
     3 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
     4 ** All rights reserved.
     4 ** All rights reserved.
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
     6 **
     6 **
     7 ** This file is part of the Qt Linguist of the Qt Toolkit.
     7 ** This file is part of the Qt Linguist of the Qt Toolkit.
     8 **
     8 **
    37 **
    37 **
    38 ** $QT_END_LICENSE$
    38 ** $QT_END_LICENSE$
    39 **
    39 **
    40 ****************************************************************************/
    40 ****************************************************************************/
    41 
    41 
    42 #include "testlupdate.h"
       
    43 #if CHECK_SIMTEXTH
    42 #if CHECK_SIMTEXTH
    44 #include "../shared/simtexth.h"
    43 #include "../shared/simtexth.h"
    45 #endif
    44 #endif
    46 
    45 
    47 #include <QtCore/QDir>
    46 #include <QtCore/QDir>
    53 
    52 
    54 class tst_lupdate : public QObject
    53 class tst_lupdate : public QObject
    55 {
    54 {
    56     Q_OBJECT
    55     Q_OBJECT
    57 public:
    56 public:
    58     tst_lupdate() { m_basePath = QDir::currentPath() + QLatin1String("/testdata/"); }
    57     tst_lupdate();
    59 
    58 
    60 private slots:
    59 private slots:
    61     void good_data();
    60     void good_data();
    62     void good();
    61     void good();
    63     void output_ts();
       
    64     void commandline_data();
       
    65     void commandline();
       
    66 #if CHECK_SIMTEXTH
    62 #if CHECK_SIMTEXTH
    67     void simtexth();
    63     void simtexth();
    68     void simtexth_data();
    64     void simtexth_data();
    69 #endif
    65 #endif
    70 
    66 
    71 private:
    67 private:
    72     TestLUpdate m_lupdate;
    68     QString m_cmdLupdate;
    73     QString m_basePath;
    69     QString m_basePath;
    74 
    70 
    75     void doCompare(const QStringList &actual, const QString &expectedFn, bool err);
    71     void doCompare(const QStringList &actual, const QString &expectedFn, bool err);
    76     void doCompare(const QString &actualFn, const QString &expectedFn, bool err);
    72     void doCompare(const QString &actualFn, const QString &expectedFn, bool err);
    77 };
    73 };
    78 
    74 
    79 
    75 
       
    76 tst_lupdate::tst_lupdate()
       
    77 {
       
    78     QString binPath = QLibraryInfo::location(QLibraryInfo::BinariesPath);
       
    79     m_cmdLupdate = binPath + QLatin1String("/lupdate");
       
    80     m_basePath = QDir::currentPath() + QLatin1String("/testdata/");
       
    81 }
       
    82 
       
    83 static bool prepareMatch(const QString &expect, QString *tmpl, int *require, int *accept)
       
    84 {
       
    85     if (expect.startsWith(QLatin1Char('\\'))) {
       
    86         *tmpl = expect.mid(1);
       
    87         *require = *accept = 1;
       
    88     } else if (expect.startsWith(QLatin1Char('?'))) {
       
    89         *tmpl = expect.mid(1);
       
    90         *require = 0;
       
    91         *accept = 1;
       
    92     } else if (expect.startsWith(QLatin1Char('*'))) {
       
    93         *tmpl = expect.mid(1);
       
    94         *require = 0;
       
    95         *accept = INT_MAX;
       
    96     } else if (expect.startsWith(QLatin1Char('+'))) {
       
    97         *tmpl = expect.mid(1);
       
    98         *require = 1;
       
    99         *accept = INT_MAX;
       
   100     } else if (expect.startsWith(QLatin1Char('{'))) {
       
   101         int brc = expect.indexOf(QLatin1Char('}'), 1);
       
   102         if (brc < 0)
       
   103             return false;
       
   104         *tmpl = expect.mid(brc + 1);
       
   105         QString sub = expect.mid(1, brc - 1);
       
   106         int com = sub.indexOf(QLatin1Char(','));
       
   107         bool ok;
       
   108         if (com < 0) {
       
   109             *require = *accept = sub.toInt(&ok);
       
   110             return ok;
       
   111         } else {
       
   112             *require = sub.left(com).toInt();
       
   113             *accept = sub.mid(com + 1).toInt(&ok);
       
   114             if (!ok)
       
   115                 *accept = INT_MAX;
       
   116             return *accept >= *require;
       
   117         }
       
   118     } else {
       
   119         *tmpl = expect;
       
   120         *require = *accept = 1;
       
   121     }
       
   122     return true;
       
   123 }
       
   124 
    80 void tst_lupdate::doCompare(const QStringList &actual, const QString &expectedFn, bool err)
   125 void tst_lupdate::doCompare(const QStringList &actual, const QString &expectedFn, bool err)
    81 {
   126 {
    82     QFile file(expectedFn);
   127     QFile file(expectedFn);
    83     QVERIFY(file.open(QIODevice::ReadOnly | QIODevice::Text));
   128     QVERIFY2(file.open(QIODevice::ReadOnly | QIODevice::Text), qPrintable(expectedFn));
    84     QStringList expected = QString(file.readAll()).trimmed().split('\n');
   129     QStringList expected = QString(file.readAll()).split('\n');
    85 
   130 
    86     int i = 0, ei = expected.size(), gi = actual.size();
   131     int ei = 0, ai = 0, em = expected.size(), am = actual.size();
    87     for (; ; i++) {
   132     int oei = 0, oai = 0, oem = em, oam = am;
    88         if (i == gi) {
   133     int require = 0, accept = 0;
    89             if (i == ei)
   134     QString tmpl;
    90                 return;
   135     forever {
    91             gi = 0;
   136         if (!accept) {
       
   137             oei = ei, oai = ai;
       
   138             if (ei == em) {
       
   139                 if (ai == am)
       
   140                     return;
       
   141                 break;
       
   142             }
       
   143             if (!prepareMatch(expected.at(ei++), &tmpl, &require, &accept))
       
   144                 QFAIL(qPrintable(QString("Malformed expected %1 at %3:%2")
       
   145                                  .arg(err ? "output" : "result").arg(ei).arg(expectedFn)));
       
   146         }
       
   147         if (ai == am) {
       
   148             if (require <= 0) {
       
   149                 accept = 0;
       
   150                 continue;
       
   151             }
    92             break;
   152             break;
    93         } else if (i == ei) {
   153         }
    94             ei = 0;
   154         if (err ? !QRegExp(tmpl).exactMatch(actual.at(ai)) : (actual.at(ai) != tmpl)) {
       
   155             if (require <= 0) {
       
   156                 accept = 0;
       
   157                 continue;
       
   158             }
       
   159             ei--;
       
   160             require = accept = 0;
       
   161             forever {
       
   162                 if (!accept) {
       
   163                     oem = em, oam = am;
       
   164                     if (ei == em)
       
   165                         break;
       
   166                     if (!prepareMatch(expected.at(--em), &tmpl, &require, &accept))
       
   167                         QFAIL(qPrintable(QString("Malformed expected %1 at %3:%2")
       
   168                                          .arg(err ? "output" : "result")
       
   169                                          .arg(em + 1).arg(expectedFn)));
       
   170                 }
       
   171                 if (ai == am || (err ? !QRegExp(tmpl).exactMatch(actual.at(am - 1)) :
       
   172                                        (actual.at(am - 1) != tmpl))) {
       
   173                     if (require <= 0) {
       
   174                         accept = 0;
       
   175                         continue;
       
   176                     }
       
   177                     break;
       
   178                 }
       
   179                 accept--;
       
   180                 require--;
       
   181                 am--;
       
   182             }
    95             break;
   183             break;
    96         } else {
   184         }
    97             QString act = actual.at(i);
   185         accept--;
    98             act.remove('\r');
   186         require--;
    99             if (err ? !QRegExp(expected.at(i)).exactMatch(act) :
   187         ai++;
   100                          (act != expected.at(i))) {
       
   101                 bool cond = true;
       
   102                 while (cond) {
       
   103                     act = actual.at(gi - 1);
       
   104                     act.remove('\r');
       
   105                     cond = (ei - 1) >= i && (gi - 1) >= i &&
       
   106                          (err ? QRegExp(expected.at(ei - 1)).exactMatch(act) :
       
   107                                 (act == expected.at(ei - 1)));
       
   108                     if (cond) {
       
   109                         ei--, gi--;
       
   110                     }
       
   111                 }
       
   112                 break;
       
   113             }
       
   114         }
       
   115     }
   188     }
   116     QByteArray diff;
   189     QByteArray diff;
   117     for (int j = qMax(0, i - 3); j < i; j++)
   190     for (int j = qMax(0, oai - 3); j < oai; j++)
   118         diff += expected.at(j) + '\n';
   191         diff += actual.at(j) + '\n';
   119     diff += "<<<<<<< got\n";
   192     diff += "<<<<<<< got\n";
   120     for (int j = i; j < gi; j++) {
   193     for (int j = oai; j < oam; j++) {
   121         diff += actual.at(j) + '\n';
   194         diff += actual.at(j) + '\n';
   122         if (j >= i + 5) {
   195         if (j >= oai + 5) {
   123             diff += "...\n";
   196             diff += "...\n";
   124             break;
   197             break;
   125         }
   198         }
   126     }
   199     }
   127     diff += "=========\n";
   200     diff += "=========\n";
   128     for (int j = i; j < ei; j++) {
   201     for (int j = oei; j < oem; j++) {
   129         diff += expected.at(j) + '\n';
   202         diff += expected.at(j) + '\n';
   130         if (j >= i + 5) {
   203         if (j >= oei + 5) {
   131             diff += "...\n";
   204             diff += "...\n";
   132             break;
   205             break;
   133         }
   206         }
   134     }
   207     }
   135     diff += ">>>>>>> expected\n";
   208     diff += ">>>>>>> expected\n";
   136     for (int j = ei; j < qMin(ei + 3, expected.size()); j++)
   209     for (int j = oam; j < qMin(oam + 3, actual.size()); j++)
   137         diff += expected.at(j) + '\n';
   210         diff += actual.at(j) + '\n';
   138     QFAIL(qPrintable((err ? "Output for " : "Result for ") + expectedFn + " does not meet expectations:\n" + diff));
   211     QFAIL(qPrintable((err ? "Output for " : "Result for ") + expectedFn + " does not meet expectations:\n" + diff));
   139 }
   212 }
   140 
   213 
   141 void tst_lupdate::doCompare(const QString &actualFn, const QString &expectedFn, bool err)
   214 void tst_lupdate::doCompare(const QString &actualFn, const QString &expectedFn, bool err)
   142 {
   215 {
   143     QFile afile(actualFn);
   216     QFile afile(actualFn);
   144     QVERIFY(afile.open(QIODevice::ReadOnly | QIODevice::Text));
   217     QVERIFY2(afile.open(QIODevice::ReadOnly | QIODevice::Text), qPrintable(actualFn));
   145     QStringList actual = QString(afile.readAll()).trimmed().split('\n');
   218     QStringList actual = QString(afile.readAll()).split('\n');
   146 
   219 
   147     doCompare(actual, expectedFn, err);
   220     doCompare(actual, expectedFn, err);
   148 }
   221 }
   149 
   222 
   150 void tst_lupdate::good_data()
   223 void tst_lupdate::good_data()
   165 void tst_lupdate::good()
   238 void tst_lupdate::good()
   166 {
   239 {
   167     QFETCH(QString, directory);
   240     QFETCH(QString, directory);
   168 
   241 
   169     QString dir = m_basePath + "good/" + directory;
   242     QString dir = m_basePath + "good/" + directory;
   170     QString expectedFile = dir + QLatin1String("/project.ts.result");
       
   171 
   243 
   172     qDebug() << "Checking...";
   244     qDebug() << "Checking...";
   173 
   245 
   174     // qmake will delete the previous one, to ensure that we don't do any merging....
   246     QString workDir = dir;
   175     QString generatedtsfile(QLatin1String("project.ts"));
   247     QStringList generatedtsfiles(QLatin1String("project.ts"));
   176 
       
   177     m_lupdate.setWorkingDirectory(dir);
       
   178     m_lupdate.qmake();
       
   179     // look for a command
       
   180     QString lupdatecmd;
   248     QString lupdatecmd;
       
   249 
   181     QFile file(dir + "/lupdatecmd");
   250     QFile file(dir + "/lupdatecmd");
   182     if (file.exists()) {
   251     if (file.exists()) {
   183         QVERIFY(file.open(QIODevice::ReadOnly | QIODevice::Text));
   252         QVERIFY2(file.open(QIODevice::ReadOnly | QIODevice::Text), qPrintable(file.fileName()));
   184         while (!file.atEnd()) {
   253         while (!file.atEnd()) {
   185             QByteArray cmdstring = file.readLine().simplified();
   254             QByteArray cmdstring = file.readLine().simplified();
   186             if (cmdstring.startsWith('#'))
   255             if (cmdstring.startsWith('#'))
   187                 continue;
   256                 continue;
   188             if (cmdstring.startsWith("lupdate")) {
   257             if (cmdstring.startsWith("lupdate")) {
   189                 cmdstring.remove(0, 8);
   258                 cmdstring.remove(0, 8);
   190                 lupdatecmd.append(cmdstring);
   259                 lupdatecmd.append(cmdstring);
   191                 break;
   260                 break;
   192             } else if (cmdstring.startsWith("TRANSLATION:")) {
   261             } else if (cmdstring.startsWith("TRANSLATION:")) {
   193                 cmdstring.remove(0, 12);
   262                 cmdstring.remove(0, 12);
   194                 generatedtsfile = dir + QLatin1Char('/') + cmdstring.trimmed();
   263                 generatedtsfiles.clear();
       
   264                 foreach (const QByteArray &s, cmdstring.split(' '))
       
   265                     if (!s.isEmpty())
       
   266                         generatedtsfiles << s;
       
   267             } else if (cmdstring.startsWith("cd ")) {
       
   268                 cmdstring.remove(0, 3);
       
   269                 workDir = QDir::cleanPath(dir + QLatin1Char('/') + cmdstring);
   195             }
   270             }
   196         }
   271         }
   197         file.close();
   272         file.close();
   198     }
   273     }
   199 
   274 
   200     if (lupdatecmd.isEmpty()) {
   275     foreach (const QString &ts, generatedtsfiles) {
   201         lupdatecmd = QLatin1String("project.pro -ts project.ts");
   276         QString genTs = workDir + QLatin1Char('/') + ts;
   202     }
   277         QFile::remove(genTs);
       
   278         QString beforetsfile = dir + QLatin1Char('/') + ts + QLatin1String(".before");
       
   279         if (QFile::exists(beforetsfile))
       
   280             QVERIFY2(QFile::copy(beforetsfile, genTs), qPrintable(beforetsfile));
       
   281     }
       
   282 
       
   283     if (lupdatecmd.isEmpty())
       
   284         lupdatecmd = QLatin1String("project.pro");
   203     lupdatecmd.prepend("-silent ");
   285     lupdatecmd.prepend("-silent ");
   204     m_lupdate.updateProFile(lupdatecmd);
   286 
       
   287     QProcess proc;
       
   288     proc.setWorkingDirectory(workDir);
       
   289     proc.setProcessChannelMode(QProcess::MergedChannels);
       
   290     proc.start(m_cmdLupdate + ' ' + lupdatecmd, QIODevice::ReadWrite | QIODevice::Text);
       
   291     QVERIFY2(proc.waitForFinished(5000), qPrintable(lupdatecmd));
       
   292     QByteArray output = proc.readAll();
       
   293     QVERIFY2(proc.exitStatus() == QProcess::NormalExit,
       
   294              "\"lupdate " + lupdatecmd.toLatin1() + "\" crashed\n" + output);
       
   295     QVERIFY2(!proc.exitCode(),
       
   296              "\"lupdate " + lupdatecmd.toLatin1() + "\" exited with code " +
       
   297              QByteArray::number(proc.exitCode()) + "\n" + output);
   205 
   298 
   206     // If the file expectedoutput.txt exists, compare the
   299     // If the file expectedoutput.txt exists, compare the
   207     // console output with the content of that file
   300     // console output with the content of that file
   208     QFile outfile(dir + "/expectedoutput.txt");
   301     QFile outfile(dir + "/expectedoutput.txt");
   209     if (outfile.exists()) {
   302     if (outfile.exists()) {
   210         QString errs = m_lupdate.getErrorMessages().at(1).trimmed();
   303         QStringList errslist = QString::fromLatin1(output).split(QLatin1Char('\n'));
   211         QStringList errslist = errs.split(QLatin1Char('\n'));
       
   212         doCompare(errslist, outfile.fileName(), true);
   304         doCompare(errslist, outfile.fileName(), true);
   213         if (QTest::currentTestFailed())
   305         if (QTest::currentTestFailed())
   214             return;
   306             return;
   215     }
   307     }
   216 
   308 
   217     doCompare(generatedtsfile, expectedFile, false);
   309     foreach (const QString &ts, generatedtsfiles)
   218 }
   310         doCompare(workDir + QLatin1Char('/') + ts,
   219 
   311                   dir + QLatin1Char('/') + ts + QLatin1String(".result"), false);
   220 void tst_lupdate::output_ts()
       
   221 {
       
   222     QString dir = m_basePath + "output_ts";
       
   223     m_lupdate.setWorkingDirectory(dir);
       
   224 
       
   225     // look for a command
       
   226     QString lupdatecmd;
       
   227     QFile file(dir + "/lupdatecmd");
       
   228     if (file.exists()) {
       
   229         QVERIFY(file.open(QIODevice::ReadOnly | QIODevice::Text));
       
   230         while (!file.atEnd()) {
       
   231         QByteArray cmdstring = file.readLine().simplified();
       
   232             if (cmdstring.startsWith('#'))
       
   233                 continue;
       
   234             if (cmdstring.startsWith("lupdate")) {
       
   235                 cmdstring.remove(0, 8);
       
   236                 lupdatecmd.append(cmdstring);
       
   237                 break;
       
   238             }
       
   239         }
       
   240         file.close();
       
   241     }
       
   242 
       
   243     QDir parsingDir(m_basePath + "output_ts");
       
   244 
       
   245     QString generatedtsfile =
       
   246         dir + QLatin1String("/toplevel/library/tools/translations/project.ts");
       
   247 
       
   248     QFile::remove(generatedtsfile);
       
   249 
       
   250     lupdatecmd.prepend("-silent ");
       
   251     m_lupdate.qmake();
       
   252     m_lupdate.updateProFile(lupdatecmd);
       
   253 
       
   254     // If the file expectedoutput.txt exists, compare the
       
   255     // console output with the content of that file
       
   256     QFile outfile(dir + "/expectedoutput.txt");
       
   257     if (outfile.exists()) {
       
   258         QString errs = m_lupdate.getErrorMessages().at(1).trimmed();
       
   259         QStringList errslist = errs.split(QLatin1Char('\n'));
       
   260         doCompare(errslist, outfile.fileName(), true);
       
   261         if (QTest::currentTestFailed())
       
   262             return;
       
   263     }
       
   264 
       
   265     doCompare(generatedtsfile, dir + QLatin1String("/project.ts.result"), false);
       
   266 }
       
   267 
       
   268 void tst_lupdate::commandline_data()
       
   269 {
       
   270     QTest::addColumn<QString>("currentPath");
       
   271     QTest::addColumn<QString>("commandline");
       
   272     QTest::addColumn<QString>("generatedtsfile");
       
   273     QTest::addColumn<QString>("expectedtsfile");
       
   274 
       
   275     QTest::newRow("Recursive scan") << QString("recursivescan")
       
   276        << QString(". -ts foo.ts") << QString("foo.ts") << QString("foo.ts.result");
       
   277     QTest::newRow("Deep path argument") << QString("recursivescan")
       
   278        << QString("sub/finddialog.cpp -ts bar.ts") << QString("bar.ts") << QString("bar.ts.result");
       
   279 }
       
   280 
       
   281 void tst_lupdate::commandline()
       
   282 {
       
   283     QFETCH(QString, currentPath);
       
   284     QFETCH(QString, commandline);
       
   285     QFETCH(QString, generatedtsfile);
       
   286     QFETCH(QString, expectedtsfile);
       
   287 
       
   288     m_lupdate.setWorkingDirectory(m_basePath + currentPath);
       
   289     QString generated =
       
   290         m_basePath + currentPath + QLatin1Char('/') + generatedtsfile;
       
   291     QFile gen(generated);
       
   292     if (gen.exists())
       
   293         QVERIFY(gen.remove());
       
   294     if (!m_lupdate.run("-silent " + commandline))
       
   295         qDebug() << m_lupdate.getErrorMessages().last();
       
   296 
       
   297     doCompare(generated, m_basePath + currentPath + QLatin1Char('/') + expectedtsfile, false);
       
   298 }
   312 }
   299 
   313 
   300 #if CHECK_SIMTEXTH
   314 #if CHECK_SIMTEXTH
   301 void tst_lupdate::simtexth()
   315 void tst_lupdate::simtexth()
   302 {
   316 {