WIP: Salt class and unit tests

This commit is contained in:
Matthias Koefferlein 2017-03-15 22:56:47 +01:00
parent 134534adca
commit 695a5d3169
6 changed files with 347 additions and 27 deletions

View File

@ -21,11 +21,110 @@
*/
#include "laySalt.h"
#include "tlString.h"
#include <QFileInfo>
namespace lay
{
Salt::Salt ()
{
// .. nothing yet ..
}
Salt::Salt (const Salt &other)
: QObject ()
{
operator= (other);
}
Salt &Salt::operator= (const Salt &other)
{
if (this != &other) {
m_root = other.m_root;
}
return *this;
}
void
Salt::add_location (const std::string &path)
{
// do nothing if the collection is already there
QFileInfo fi (tl::to_qstring (path));
for (lay::SaltGrains::collection_iterator g = m_root.begin_collections (); g != m_root.end_collections (); ++g) {
if (QFileInfo (tl::to_qstring (g->path ())) == fi) {
return;
}
}
lay::SaltGrains gg = lay::SaltGrains::from_path (path);
m_root.add_collection (gg);
mp_flat_grains.clear ();
emit collections_changed ();
}
void
Salt::remove_location (const std::string &path)
{
QFileInfo fi (tl::to_qstring (path));
for (lay::SaltGrains::collection_iterator g = m_root.begin_collections (); g != m_root.end_collections (); ++g) {
if (QFileInfo (tl::to_qstring (g->path ())) == fi) {
m_root.remove_collection (g, false);
mp_flat_grains.clear ();
emit collections_changed ();
return;
}
}
}
void
Salt::refresh ()
{
lay::SaltGrains new_root;
for (lay::SaltGrains::collection_iterator g = m_root.begin_collections (); g != m_root.end_collections (); ++g) {
new_root.add_collection (lay::SaltGrains::from_path (g->path ()));
}
if (new_root != m_root) {
m_root = new_root;
mp_flat_grains.clear ();
emit collections_changed ();
}
}
void
Salt::add_collection_to_flat (SaltGrains &gg)
{
for (lay::SaltGrains::grain_iterator g = gg.begin_grains (); g != gg.end_grains (); ++g) {
// TODO: get rid of the const cast - would require a non-const grain iterator
mp_flat_grains.push_back (const_cast <SaltGrain *> (g.operator-> ()));
}
for (lay::SaltGrains::collection_iterator g = gg.begin_collections (); g != gg.end_collections (); ++g) {
// TODO: get rid of the const cast - would require a non-const grain collection iterator
add_collection_to_flat (const_cast <SaltGrains &> (*g));
}
}
namespace {
struct NameCompare
{
bool operator () (lay::SaltGrain *a, lay::SaltGrain *b) const
{
// TODO: UTF-8 support?
return a->name () < b->name ();
}
};
}
void
Salt::ensure_flat_present ()
{
if (mp_flat_grains.empty ()) {
add_collection_to_flat (m_root);
std::sort (mp_flat_grains.begin (), mp_flat_grains.end (), NameCompare ());
}
}
}

View File

@ -23,11 +23,110 @@
#ifndef HDR_laySalt
#define HDR_laySalt
#include "layCommon.h"
#include "laySaltGrain.h"
#include "laySaltGrains.h"
#include <QObject>
namespace lay
{
/**
* @brief The global salt (package manager) object
* This object can be configured to represent a couple of locations.
* It will provide a collection of grains for these locations.
*/
class LAY_PUBLIC Salt
: public QObject
{
Q_OBJECT
public:
typedef SaltGrains::collection_iterator iterator;
typedef std::vector<SaltGrain *>::const_iterator flat_iterator;
/**
* @brief Default constructor
*/
Salt ();
/**
* @brief Copy constructor
*/
Salt (const Salt &other);
/**
* @brief assignment
*/
Salt &operator= (const Salt &other);
/**
* @brief Adds the given location to the ones the package manager uses
* Adding a location will scan the folder and make the contents available
* as a new collection.
*/
void add_location (const std::string &path);
/**
* @brief Removes a given location
* This will remove the collection from the package locations.
*/
void remove_location (const std::string &path);
/**
* @brief Refreshes the collections
* This method rescans all registered locations.
*/
void refresh ();
/**
* @brief Iterates the collections (begin)
*/
iterator begin () const
{
return m_root.begin_collections ();
}
/**
* @brief Iterates the collections (end)
*/
iterator end () const
{
return m_root.end_collections ();
}
/**
* @brief A flat iterator of (sorted) grains (begin)
*/
flat_iterator begin_flat ()
{
ensure_flat_present ();
return mp_flat_grains.begin ();
}
/**
* @brief A flat iterator of (sorted) grains (end)
*/
flat_iterator end_flat ()
{
ensure_flat_present ();
return mp_flat_grains.end ();
}
signals:
/**
* @brief A signal triggered when one of the collections changed
*/
void collections_changed ();
private:
SaltGrains m_root;
std::vector<SaltGrain *> mp_flat_grains;
void ensure_flat_present ();
void add_collection_to_flat (lay::SaltGrains &gg);
};
}

View File

@ -75,7 +75,7 @@ SaltGrains::remove_collection (collection_iterator iter, bool with_files)
// NOTE: this is kind of inefficient, but in order to maintain the const iterator semantics this approach is required
for (collections_type::iterator i = m_collections.begin (); i != m_collections.end (); ++i) {
if (i == iter) {
if (with_files && !tl::rm_dir_recursive (tl::to_qstring (path ()))) {
if (with_files && !tl::rm_dir_recursive (tl::to_qstring (i->path ()))) {
return false;
}
m_collections.erase (i);
@ -98,7 +98,7 @@ SaltGrains::remove_grain (grain_iterator iter, bool with_files)
// NOTE: this is kind of inefficient, but in order to maintain the const iterator semantics this approach is required
for (grains_type::iterator i = m_grains.begin (); i != m_grains.end (); ++i) {
if (i == iter) {
if (with_files && !tl::rm_dir_recursive (tl::to_qstring (path ()))) {
if (with_files && !tl::rm_dir_recursive (tl::to_qstring (i->path ()))) {
return false;
}
m_grains.erase (i);

View File

@ -23,6 +23,7 @@
#ifndef HDR_laySaltGrains
#define HDR_laySaltGrains
#include "layCommon.h"
#include "laySaltGrain.h"
#include <list>

View File

@ -23,10 +23,52 @@
#include "laySaltGrain.h"
#include "laySaltGrains.h"
#include "laySalt.h"
#include "tlFileUtils.h"
#include "utHead.h"
#include <QDir>
#include <QSignalSpy>
static std::string grains_to_string (const lay::SaltGrains &gg)
{
std::string res;
res += "[";
bool first = true;
for (lay::SaltGrains::grain_iterator g = gg.begin_grains (); g != gg.end_grains (); ++g) {
if (! first) {
res += ",";
}
first = false;
res += g->name ();
}
for (lay::SaltGrains::collection_iterator gc = gg.begin_collections (); gc != gg.end_collections (); ++gc) {
if (! first) {
res += ",";
}
first = false;
res += gc->name ();
res += grains_to_string (*gc);
}
res += "]";
return res;
}
static std::string salt_to_string (lay::Salt &salt)
{
std::string res;
res += "[";
bool first = true;
for (lay::Salt::flat_iterator i = salt.begin_flat (); i != salt.end_flat (); ++i) {
if (! first) {
res += ",";
}
first = false;
res += (*i)->name ();
}
res += "]";
return res;
}
TEST (1)
{
@ -111,30 +153,6 @@ TEST (2)
}
static std::string grains_to_string (const lay::SaltGrains &gg)
{
std::string res;
res += "[";
bool first = true;
for (lay::SaltGrains::grain_iterator g = gg.begin_grains (); g != gg.end_grains (); ++g) {
if (! first) {
res += ",";
}
first = false;
res += g->name ();
}
for (lay::SaltGrains::collection_iterator gc = gg.begin_collections (); gc != gg.end_collections (); ++gc) {
if (! first) {
res += ",";
}
first = false;
res += gc->name ();
res += grains_to_string (*gc);
}
res += "]";
return res;
}
TEST (3)
{
const QString grain_spec_file = QString::fromUtf8 ("grain.xml");
@ -184,4 +202,107 @@ TEST (3)
EXPECT_EQ (gg.is_empty (), false);
EXPECT_EQ (grains_to_string (gg), "[a,b,c[c/u,c/c[c/c/v]]]");
EXPECT_EQ (gg.begin_collections ()->path (), tl::to_string (dir_c.absolutePath ()));
gg.remove_grain (gg.begin_grains (), false);
EXPECT_EQ (grains_to_string (gg), "[b,c[c/u,c/c[c/c/v]]]");
gg = lay::SaltGrains::from_path (tl::to_string (tmp_dir.path ()));
EXPECT_EQ (grains_to_string (gg), "[a,b,c[c/u,c/c[c/c/v]]]");
gg.remove_grain (gg.begin_grains (), true);
gg = lay::SaltGrains::from_path (tl::to_string (tmp_dir.path ()));
EXPECT_EQ (grains_to_string (gg), "[b,c[c/u,c/c[c/c/v]]]");
gg.remove_collection (gg.begin_collections (), false);
EXPECT_EQ (grains_to_string (gg), "[b]");
gg = lay::SaltGrains::from_path (tl::to_string (tmp_dir.path ()));
EXPECT_EQ (grains_to_string (gg), "[b,c[c/u,c/c[c/c/v]]]");
gg.remove_collection (gg.begin_collections (), true);
EXPECT_EQ (grains_to_string (gg), "[b]");
gg = lay::SaltGrains::from_path (tl::to_string (tmp_dir.path ()));
EXPECT_EQ (grains_to_string (gg), "[b]");
}
TEST (4)
{
const QString grain_spec_file = QString::fromUtf8 ("grain.xml");
// That's just preparation ...
lay::SaltGrain g;
g.set_name ("x");
QDir tmp_dir (QFileInfo (tl::to_qstring (tmp_file ())).absolutePath ());
QDir dir_a (tmp_dir.filePath (QString::fromUtf8 ("a")));
QDir dir_b (tmp_dir.filePath (QString::fromUtf8 ("b")));
QDir dir_c (tmp_dir.filePath (QString::fromUtf8 ("c")));
QDir dir_cu (dir_c.filePath (QString::fromUtf8 ("u")));
QDir dir_cc (dir_c.filePath (QString::fromUtf8 ("c")));
QDir dir_ccv (dir_cc.filePath (QString::fromUtf8 ("v")));
tl_assert (tl::rm_dir_recursive (tmp_dir.path ()));
lay::SaltGrains gg;
gg = lay::SaltGrains::from_path (tl::to_string (tmp_dir.path ()));
EXPECT_EQ (gg.is_empty (), true);
EXPECT_EQ (grains_to_string (gg), "[]");
tmp_dir.mkdir (dir_a.dirName ());
tmp_dir.mkdir (dir_b.dirName ());
tmp_dir.mkdir (dir_c.dirName ());
dir_c.mkdir (dir_cu.dirName ());
dir_c.mkdir (dir_cc.dirName ());
dir_cc.mkdir (dir_ccv.dirName ());
gg = lay::SaltGrains::from_path (tl::to_string (tmp_dir.path ()));
EXPECT_EQ (gg.is_empty (), true);
EXPECT_EQ (grains_to_string (gg), "[]");
EXPECT_EQ (gg.path (), tl::to_string (tmp_dir.path ()));
g.save (tl::to_string (dir_a.absoluteFilePath (grain_spec_file)));
g.save (tl::to_string (dir_b.absoluteFilePath (grain_spec_file)));
g.save (tl::to_string (dir_cu.absoluteFilePath (grain_spec_file)));
g.save (tl::to_string (dir_ccv.absoluteFilePath (grain_spec_file)));
// That's the main test part
lay::Salt salt;
QSignalSpy spy (&salt, SIGNAL (collections_changed ()));
EXPECT_EQ (salt_to_string (salt), "[]");
spy.clear ();
salt.add_location (tl::to_string (tmp_dir.path ()));
EXPECT_EQ (spy.count (), 1);
EXPECT_EQ (salt_to_string (salt), "[a,b,c/c/v,c/u]");
spy.clear ();
salt.add_location (tl::to_string (tmp_dir.path ()));
EXPECT_EQ (spy.count (), 0);
EXPECT_EQ (salt_to_string (salt), "[a,b,c/c/v,c/u]");
spy.clear ();
salt.add_location (tl::to_string (dir_c.path ()));
EXPECT_EQ (spy.count (), 1);
EXPECT_EQ (salt_to_string (salt), "[a,b,c/c/v,c/u,c/v,u]");
lay::Salt salt_copy = salt;
(const_cast<lay::SaltGrains &> (*salt_copy.begin ())).remove_grain (salt_copy.begin ()->begin_grains (), true);
spy.clear ();
salt.refresh ();
EXPECT_EQ (spy.count (), 1);
EXPECT_EQ (salt_to_string (salt), "[b,c/c/v,c/u,c/v,u]");
spy.clear ();
salt.remove_location (tl::to_string (dir_c.path ()));
EXPECT_EQ (spy.count (), 1);
EXPECT_EQ (salt_to_string (salt), "[b,c/c/v,c/u]");
spy.clear ();
// location already removed
salt.remove_location (tl::to_string (dir_c.path ()));
EXPECT_EQ (spy.count (), 0);
EXPECT_EQ (salt_to_string (salt), "[b,c/c/v,c/u]");
}

View File

@ -96,7 +96,7 @@ SOURCES = \
tlXMLParser.cc \
gsiTest.cc \
tlFileSystemWatcher.cc \
laySaltGrain.cc
laySalt.cc
# main components:
SOURCES += \