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 { |