include duplication check
works now on all platforms without fdupes installed
This commit is contained in:
parent
b971378872
commit
4038960c82
@ -12,10 +12,6 @@ if(BUILD_TESTING)
|
|||||||
TEST_NAME "newline"
|
TEST_NAME "newline"
|
||||||
LINK_LIBRARIES Qt6::Test
|
LINK_LIBRARIES Qt6::Test
|
||||||
)
|
)
|
||||||
ecm_add_test(dupetest.cpp
|
|
||||||
TEST_NAME "dupe"
|
|
||||||
LINK_LIBRARIES Qt6::Test
|
|
||||||
)
|
|
||||||
ecm_add_test(scalabletest.cpp
|
ecm_add_test(scalabletest.cpp
|
||||||
TEST_NAME "scalable"
|
TEST_NAME "scalable"
|
||||||
LINK_LIBRARIES Qt6::Test
|
LINK_LIBRARIES Qt6::Test
|
||||||
|
|||||||
@ -1,94 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2016 Harald Sitter <sitter@kde.org>
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Lesser General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2.1 of the License, or (at your option) version 3, or any
|
|
||||||
later version accepted by the membership of KDE e.V. (or its
|
|
||||||
successor approved by the membership of KDE e.V.), which shall
|
|
||||||
act as a proxy defined in Section 6 of version 3 of the license.
|
|
||||||
|
|
||||||
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
|
|
||||||
Lesser General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public
|
|
||||||
License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <QObject>
|
|
||||||
#include <QProcess>
|
|
||||||
#include <QStandardPaths>
|
|
||||||
#include <QTest>
|
|
||||||
|
|
||||||
#include "testhelpers.h"
|
|
||||||
|
|
||||||
class DupeTest : public QObject
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
QStringList splitOnUnescapedSpace(const QString &line)
|
|
||||||
{
|
|
||||||
QStringList ret;
|
|
||||||
const int lineLength = line.length();
|
|
||||||
int start = 0;
|
|
||||||
for (int pos = 0; pos < lineLength; ++pos) {
|
|
||||||
const QChar ch = line[pos];
|
|
||||||
if (ch == QLatin1Char('\\')) {
|
|
||||||
++pos;
|
|
||||||
continue;
|
|
||||||
} else if (ch == QLatin1Char(' ')) {
|
|
||||||
ret.append(line.mid(start, pos - start));
|
|
||||||
start = pos + 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (start < lineLength) {
|
|
||||||
ret.append(line.mid(start));
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
void readLines(QProcess &proc)
|
|
||||||
{
|
|
||||||
QString line;
|
|
||||||
while (proc.canReadLine() || proc.waitForReadyRead()) {
|
|
||||||
line = QString::fromUtf8(proc.readLine());
|
|
||||||
failListContent(splitOnUnescapedSpace(line.simplified()), QStringLiteral("The following files are duplicates but not links:\n"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void dupesForDirectory(const QString &path)
|
|
||||||
{
|
|
||||||
const QString exec = QStandardPaths::findExecutable(QStringLiteral("fdupes"));
|
|
||||||
QVERIFY(!exec.isEmpty());
|
|
||||||
QProcess proc;
|
|
||||||
proc.setProgram(exec);
|
|
||||||
proc.setArguments(QStringList() << QStringLiteral("--recurse") << QStringLiteral("--sameline") << QStringLiteral("--nohidden") << path);
|
|
||||||
proc.start();
|
|
||||||
proc.waitForStarted();
|
|
||||||
readLines(proc);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Q_SLOTS:
|
|
||||||
void test_duplicates()
|
|
||||||
{
|
|
||||||
if (QStandardPaths::findExecutable(QStringLiteral("fdupes")).isEmpty()) {
|
|
||||||
#ifdef Q_OS_UNIX
|
|
||||||
// Fail and skip. This is a fairly relevant test, so it not running is a warning really.
|
|
||||||
QFAIL("this test needs the fdupes binary (1.51+) to run");
|
|
||||||
#else
|
|
||||||
// On Windows let's just skip it
|
|
||||||
QSKIP("this test needs the fdupes binary (1.51+) to run");
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
for (auto dir : ICON_DIRS) {
|
|
||||||
dupesForDirectory(PROJECT_SOURCE_DIR + QStringLiteral("/") + dir);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
QTEST_GUILESS_MAIN(DupeTest)
|
|
||||||
|
|
||||||
#include "dupetest.moc"
|
|
||||||
@ -12,24 +12,39 @@
|
|||||||
#include <QDirIterator>
|
#include <QDirIterator>
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
#include <QFileInfo>
|
#include <QFileInfo>
|
||||||
|
#include <QHash>
|
||||||
#include <QRegularExpression>
|
#include <QRegularExpression>
|
||||||
#include <QSet>
|
#include <QSet>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QXmlStreamReader>
|
#include <QXmlStreamReader>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if this file is a duplicate of an other on, dies then.
|
||||||
|
* @param fileName file to check
|
||||||
|
*/
|
||||||
|
static void checkForDuplicates(const QString &fileName)
|
||||||
|
{
|
||||||
|
// get full content for dupe checking
|
||||||
|
QFile in(fileName);
|
||||||
|
if (!in.open(QIODevice::ReadOnly)) {
|
||||||
|
qFatal() << "failed to open" << in.fileName() << "for XML validation";
|
||||||
|
}
|
||||||
|
const auto fullContent = in.readAll();
|
||||||
|
|
||||||
|
// see if we did have this content already and die
|
||||||
|
static QHash<QByteArray, QString> contentToFileName;
|
||||||
|
if (const auto it = contentToFileName.find(fullContent); it != contentToFileName.end()) {
|
||||||
|
qFatal() << "file" << fileName << "is a duplicate of file" << it.value();
|
||||||
|
}
|
||||||
|
contentToFileName.insert(fullContent, fileName);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validate the XML, dies on errors.
|
* Validate the XML, dies on errors.
|
||||||
* @param fileName file to validate
|
* @param fileName file to validate
|
||||||
*/
|
*/
|
||||||
static void validateXml(const QString &fileName)
|
static void validateXml(const QString &fileName)
|
||||||
{
|
{
|
||||||
// do checks just once, if we encounter this multiple times because of aliasing
|
|
||||||
static QSet<QString> seenFiles;
|
|
||||||
if (seenFiles.contains(fileName)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
seenFiles.insert(fileName);
|
|
||||||
|
|
||||||
// read once and bail out on errors
|
// read once and bail out on errors
|
||||||
QFile in(fileName);
|
QFile in(fileName);
|
||||||
if (!in.open(QIODevice::ReadOnly)) {
|
if (!in.open(QIODevice::ReadOnly)) {
|
||||||
@ -96,6 +111,9 @@ static void generateQRCAndCheckInputs(const QStringList &indirs, const QString &
|
|||||||
out.write("<!DOCTYPE RCC><RCC version=\"1.0\">\n");
|
out.write("<!DOCTYPE RCC><RCC version=\"1.0\">\n");
|
||||||
out.write("<qresource>\n");
|
out.write("<qresource>\n");
|
||||||
|
|
||||||
|
// loop over the inputs, remember if we do look at generated stuff for checks
|
||||||
|
bool generatedIcons = false;
|
||||||
|
QSet<QString> checkedFiles;
|
||||||
for (const auto &indir : indirs) {
|
for (const auto &indir : indirs) {
|
||||||
// go to input dir to have proper relative paths
|
// go to input dir to have proper relative paths
|
||||||
if (!QDir::setCurrent(indir)) {
|
if (!QDir::setCurrent(indir)) {
|
||||||
@ -127,14 +145,27 @@ static void generateQRCAndCheckInputs(const QStringList &indirs, const QString &
|
|||||||
fullPath = QFileInfo(aliasLink).absoluteFilePath();
|
fullPath = QFileInfo(aliasLink).absoluteFilePath();
|
||||||
}
|
}
|
||||||
|
|
||||||
// validate it as XML if it is an SVG
|
// do some checks for SVGs
|
||||||
if (fullPath.endsWith(QLatin1String(".svg"))) {
|
// do checks just once, if we encounter this multiple times because of aliasing
|
||||||
|
if (fullPath.endsWith(QLatin1String(".svg")) && !checkedFiles.contains(fullPath)) {
|
||||||
|
// fill our guard
|
||||||
|
checkedFiles.insert(fullPath);
|
||||||
|
|
||||||
|
// validate it as XML if it is an SVG
|
||||||
validateXml(fullPath);
|
validateXml(fullPath);
|
||||||
|
|
||||||
|
// do duplicate check for non-generated icons
|
||||||
|
if (!generatedIcons) {
|
||||||
|
checkForDuplicates(fullPath);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// write the one alias to file entry
|
// write the one alias to file entry
|
||||||
out.write(QStringLiteral(" <file alias=\"%1\">%2</file>\n").arg(file, fullPath).toUtf8());
|
out.write(QStringLiteral(" <file alias=\"%1\">%2</file>\n").arg(file, fullPath).toUtf8());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// starting with the second directory we look at generated icons
|
||||||
|
generatedIcons = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
out.write("</qresource>\n");
|
out.write("</qresource>\n");
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user