From 695a5d3169800a9824abe6805cda8720155bde3f Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Wed, 15 Mar 2017 22:56:47 +0100 Subject: [PATCH] WIP: Salt class and unit tests --- src/lay/laySalt.cc | 99 ++++++++++ src/lay/laySalt.h | 99 ++++++++++ src/lay/laySaltGrains.cc | 4 +- src/lay/laySaltGrains.h | 1 + .../{laySaltGrain.cc => laySalt.cc} | 169 +++++++++++++++--- src/unit_tests/unit_tests.pro | 2 +- 6 files changed, 347 insertions(+), 27 deletions(-) rename src/unit_tests/{laySaltGrain.cc => laySalt.cc} (60%) diff --git a/src/lay/laySalt.cc b/src/lay/laySalt.cc index 897fc0457..24559a2d1 100644 --- a/src/lay/laySalt.cc +++ b/src/lay/laySalt.cc @@ -21,11 +21,110 @@ */ #include "laySalt.h" +#include "tlString.h" + +#include 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 (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 (*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 ()); + } +} } diff --git a/src/lay/laySalt.h b/src/lay/laySalt.h index b4bf3aa2a..39d5c2f02 100644 --- a/src/lay/laySalt.h +++ b/src/lay/laySalt.h @@ -23,11 +23,110 @@ #ifndef HDR_laySalt #define HDR_laySalt +#include "layCommon.h" +#include "laySaltGrain.h" +#include "laySaltGrains.h" + +#include + 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::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 mp_flat_grains; + + void ensure_flat_present (); + void add_collection_to_flat (lay::SaltGrains &gg); +}; } diff --git a/src/lay/laySaltGrains.cc b/src/lay/laySaltGrains.cc index 1d8306176..a40bcea64 100644 --- a/src/lay/laySaltGrains.cc +++ b/src/lay/laySaltGrains.cc @@ -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); diff --git a/src/lay/laySaltGrains.h b/src/lay/laySaltGrains.h index f47b897fb..0387000a1 100644 --- a/src/lay/laySaltGrains.h +++ b/src/lay/laySaltGrains.h @@ -23,6 +23,7 @@ #ifndef HDR_laySaltGrains #define HDR_laySaltGrains +#include "layCommon.h" #include "laySaltGrain.h" #include diff --git a/src/unit_tests/laySaltGrain.cc b/src/unit_tests/laySalt.cc similarity index 60% rename from src/unit_tests/laySaltGrain.cc rename to src/unit_tests/laySalt.cc index fabfab2fd..2d761ed62 100644 --- a/src/unit_tests/laySaltGrain.cc +++ b/src/unit_tests/laySalt.cc @@ -23,10 +23,52 @@ #include "laySaltGrain.h" #include "laySaltGrains.h" +#include "laySalt.h" #include "tlFileUtils.h" #include "utHead.h" #include +#include + +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 (*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]"); } diff --git a/src/unit_tests/unit_tests.pro b/src/unit_tests/unit_tests.pro index aba6c9daf..547d7616c 100644 --- a/src/unit_tests/unit_tests.pro +++ b/src/unit_tests/unit_tests.pro @@ -96,7 +96,7 @@ SOURCES = \ tlXMLParser.cc \ gsiTest.cc \ tlFileSystemWatcher.cc \ - laySaltGrain.cc + laySalt.cc # main components: SOURCES += \