Skip to content
Permalink
Browse files
[diskusage] Add unit tests for size calculation
  • Loading branch information
thp committed Mar 17, 2015
1 parent ed2aa36 commit 41739db23b5c5260c80709b5941cddb0664720ad
Showing with 462 additions and 103 deletions.
  1. +12 −0 rpm/nemo-qml-plugin-systemsettings.spec
  2. +4 −101 src/diskusage.cpp
  3. +141 −0 src/diskusage_impl.cpp
  4. +7 −0 src/diskusage_p.h
  5. +2 −1 src/src.pro
  6. +1 −1 systemsettings.pro
  7. +40 −0 tests/tests.pro
  8. +31 −0 tests/tests.xml.template
  9. +172 −0 tests/ut_diskusage.cpp
  10. +52 −0 tests/ut_diskusage.h
@@ -27,6 +27,13 @@ Requires: %{name} = %{version}-%{release}
%description devel
%{summary}.

%package tests
Summary: System settings C++ library (unit tests)
Group: System/Libraries

%description tests
%{summary}.

%prep
%setup -q -n %{name}-%{version}

@@ -53,3 +60,8 @@ rm -rf %{buildroot}
%{_libdir}/pkgconfig/systemsettings.pc
%{_includedir}/systemsettings/*
%{_libdir}/libsystemsettings.so

%files tests
%defattr(-,root,root,-)
%{_libdir}/%{name}-tests/ut_diskusage
%{_datadir}/%{name}-tests/tests.xml
@@ -34,15 +34,9 @@
#include "diskusage_p.h"

#include <QThread>
#include <QDir>
#include <QProcess>
#include <QDebug>
#include <QJSEngine>
#include <QDBusConnection>
#include <QDBusMessage>
#include <QDBusReply>

#include <sys/statvfs.h>

DiskUsageWorker::DiskUsageWorker(QObject *parent)
: QObject(parent)
@@ -54,89 +48,12 @@ DiskUsageWorker::~DiskUsageWorker()
{
}

static quint64 calculateSize(QString directory, QString *expandedPath)
{
// In lieu of wordexp(3) support in Qt, fake it
if (directory.startsWith("~/")) {
directory = QDir::homePath() + '/' + directory.mid(2);
}

if (expandedPath) {
*expandedPath = directory;
}

// "/data/media/" is mounted in "/home/nemo/android_storage/" with read
// access for the "nemo" user ("/data/media/" itself isn't readable);
// Mounted via FUSE and /system/bin/sdcard, see here:
// https://source.android.com/devices/storage/config.html
if (directory == "/data/media/") {
directory = "/home/nemo/android_storage/";
}

QDir d(directory);
if (!d.exists() || !d.isReadable()) {
return 0L;
}

QProcess du;
du.start("du", QStringList() << "-sxb" << directory, QIODevice::ReadOnly);
du.waitForFinished();
if (du.exitStatus() != QProcess::NormalExit) {
qWarning() << "Could not determine size of:" << directory;
return 0L;
}
QStringList size_directory = QString::fromUtf8(du.readAll()).split('\t');

if (size_directory.size() > 1) {
return size_directory[0].toULongLong();
}

return 0L;
}

static quint64 calculateRpmSize(const QString &glob)
{
QProcess rpm;
rpm.start("rpm", QStringList() << "-qa" << "--queryformat=%{name}|%{size}\\n" << glob, QIODevice::ReadOnly);
rpm.waitForFinished();
if (rpm.exitStatus() != QProcess::NormalExit) {
qWarning() << "Could not determine size of RPM packages matching:" << glob;
return 0L;
}

quint64 result = 0L;

QStringList lines = QString::fromUtf8(rpm.readAll()).split('\n', QString::SkipEmptyParts);
foreach (const QString &line, lines) {
int index = line.indexOf('|');
if (index == -1) {
qWarning() << "Could not parse RPM output line:" << line;
continue;
}

QString package = line.left(index);
result += line.mid(index+1).toULongLong();
}

return result;
}

static quint64 calculateApkdSize(const QString &rest)
void DiskUsageWorker::submit(QStringList paths, QJSValue *callback)
{
Q_UNUSED(rest)
QDBusMessage msg = QDBusMessage::createMethodCall("com.jolla.apkd",
"/com/jolla/apkd", "com.jolla.apkd", "getAndroidAppDataUsage");

QDBusReply<qulonglong> reply = QDBusConnection::systemBus().call(msg);
if (reply.isValid()) {
return quint64(reply.value());
}

qWarning() << "Could not determine Android app data usage";
return 0L;
emit finished(calculate(paths), callback);
}

void DiskUsageWorker::submit(QStringList paths, QJSValue *callback)
QVariantMap DiskUsageWorker::calculate(QStringList paths)
{
QVariantMap usage;
QMap<QString, QString> expandedPaths; // input path -> expanded path
@@ -154,19 +71,6 @@ void DiskUsageWorker::submit(QStringList paths, QJSValue *callback)
// Pseudo-path for querying Android apps' data usage
QString rest = path.mid(6);
usage[path] = calculateApkdSize(rest);
} else if (path == "/") {
// Shortcut for getting usage of rootfs
// TODO: Once we have Qt 5.4, use QStorageInfo
struct statvfs stv;
memset(&stv, 0, sizeof(stv));
if (statvfs(path.toUtf8().constData(), &stv) != 0) {
// Do not make an entry for the usage here
qWarning() << "statvfs() failed on:" << path;
continue;
}
quint64 fsSize = float(stv.f_frsize) * float(stv.f_blocks);
quint64 freeSpace = float(stv.f_frsize) * float(stv.f_bfree);
usage[path] = fsSize - freeSpace;
} else {
QString expandedPath;
quint64 size = calculateSize(path, &expandedPath);
@@ -221,10 +125,9 @@ void DiskUsageWorker::submit(QStringList paths, QJSValue *callback)
}
}

emit finished(usage, callback);
return usage;
}


class DiskUsagePrivate
{
Q_DISABLE_COPY(DiskUsagePrivate)
@@ -0,0 +1,141 @@
/*
* Copyright (C) 2015 Jolla Ltd.
* Contact: Thomas Perl <thomas.perl@jolla.com>
*
* You may use this file under the terms of the BSD license as follows:
*
* "Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Nemo Mobile nor the names of its contributors
* may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
*/

#include "diskusage.h"
#include "diskusage_p.h"

#include <QDir>
#include <QProcess>
#include <QDebug>
#include <QDBusConnection>
#include <QDBusMessage>
#include <QDBusReply>

#include <sys/statvfs.h>


quint64 DiskUsageWorker::calculateSize(QString directory, QString *expandedPath)
{
// In lieu of wordexp(3) support in Qt, fake it
if (directory.startsWith("~/")) {
directory = QDir::homePath() + '/' + directory.mid(2);
}

if (expandedPath) {
*expandedPath = directory;
}

if (directory == "/") {
// Shortcut for getting usage of rootfs
// TODO: Once we have Qt 5.4, use QStorageInfo
struct statvfs stv;
memset(&stv, 0, sizeof(stv));
if (statvfs(directory.toUtf8().constData(), &stv) != 0) {
// Do not make an entry for the usage here
qWarning() << "statvfs() failed on:" << directory;
return 0L;
}
quint64 fsSize = float(stv.f_frsize) * float(stv.f_blocks);
quint64 freeSpace = float(stv.f_frsize) * float(stv.f_bfree);
return fsSize - freeSpace;
}

// "/data/media/" is mounted in "/home/nemo/android_storage/" with read
// access for the "nemo" user ("/data/media/" itself isn't readable);
// Mounted via FUSE and /system/bin/sdcard, see here:
// https://source.android.com/devices/storage/config.html
if (directory == "/data/media/") {
directory = "/home/nemo/android_storage/";
}

QDir d(directory);
if (!d.exists() || !d.isReadable()) {
return 0L;
}

QProcess du;
du.start("du", QStringList() << "-sxb" << directory, QIODevice::ReadOnly);
du.waitForFinished();
if (du.exitStatus() != QProcess::NormalExit) {
qWarning() << "Could not determine size of:" << directory;
return 0L;
}
QStringList size_directory = QString::fromUtf8(du.readAll()).split('\t');

if (size_directory.size() > 1) {
return size_directory[0].toULongLong();
}

return 0L;
}

quint64 DiskUsageWorker::calculateRpmSize(const QString &glob)
{
QProcess rpm;
rpm.start("rpm", QStringList() << "-qa" << "--queryformat=%{name}|%{size}\\n" << glob, QIODevice::ReadOnly);
rpm.waitForFinished();
if (rpm.exitStatus() != QProcess::NormalExit) {
qWarning() << "Could not determine size of RPM packages matching:" << glob;
return 0L;
}

quint64 result = 0L;

QStringList lines = QString::fromUtf8(rpm.readAll()).split('\n', QString::SkipEmptyParts);
foreach (const QString &line, lines) {
int index = line.indexOf('|');
if (index == -1) {
qWarning() << "Could not parse RPM output line:" << line;
continue;
}

QString package = line.left(index);
result += line.mid(index+1).toULongLong();
}

return result;
}

quint64 DiskUsageWorker::calculateApkdSize(const QString &rest)
{
Q_UNUSED(rest)
QDBusMessage msg = QDBusMessage::createMethodCall("com.jolla.apkd",
"/com/jolla/apkd", "com.jolla.apkd", "getAndroidAppDataUsage");

QDBusReply<qulonglong> reply = QDBusConnection::systemBus().call(msg);
if (reply.isValid()) {
return quint64(reply.value());
}

qWarning() << "Could not determine Android app data usage";
return 0L;
}
@@ -54,7 +54,14 @@ public slots:
void finished(QVariantMap usage, QJSValue *callback);

private:
QVariantMap calculate(QStringList paths);
quint64 calculateSize(QString directory, QString *expandedPath);
quint64 calculateRpmSize(const QString &glob);
quint64 calculateApkdSize(const QString &rest);

bool m_quit;

friend class Ut_DiskUsage;
};

#endif /* DISKUSAGE_P_H */
@@ -22,7 +22,8 @@ SOURCES += \
aboutsettings.cpp \
devicelockiface.cpp \
developermodesettings.cpp \
diskusage.cpp
diskusage.cpp \
diskusage_impl.cpp

HEADERS += \
languagemodel.h \
@@ -4,4 +4,4 @@ src_plugins.subdir = src/plugin
src_plugins.target = sub-plugins
src_plugins.depends = src

SUBDIRS = src src_plugins
SUBDIRS = src src_plugins tests
@@ -0,0 +1,40 @@
# based on tests.pro from libprofile-qt

PACKAGENAME = nemo-qml-plugin-systemsettings

QT += testlib qml dbus systeminfo
QT -= gui

system(sed -e s/@PACKAGENAME@/$${PACKAGENAME}/g tests.xml.template > tests.xml)

TEMPLATE = app
TARGET = ut_diskusage

target.path = /usr/lib/$${PACKAGENAME}-tests

xml.path = /usr/share/$${PACKAGENAME}-tests
xml.files = tests.xml

contains(cov, true) {
message("Coverage options enabled")
QMAKE_CXXFLAGS += --coverage
QMAKE_LFLAGS += --coverage
}

CONFIG += link_prl
DEFINES += UNIT_TEST
QMAKE_EXTRA_TARGETS = check

check.depends = $$TARGET
check.commands = LD_LIBRARY_PATH=../../lib ./$$TARGET

INCLUDEPATH += ../src/

SOURCES += ut_diskusage.cpp
HEADERS += ut_diskusage.h

SOURCES += ../src/diskusage.cpp
HEADERS += ../src/diskusage.h
HEADERS += ../src/diskusage_p.h

INSTALLS += target xml

0 comments on commit 41739db

Please sign in to comment.