From 4ca24df8145f2ca36ee6b581806fc1d408f5f18e Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 19 Oct 2017 00:16:34 +0200 Subject: [PATCH] Package manager enhancements * marked icon * multiple selection * hidden flag for repository * background color of package list black always * consolidation of package list - identical packages are reduced to the latest one --- src/lay/lay/SaltManagerDialog.ui | 24 +++- src/lay/lay/images/marked_24.png | Bin 424 -> 1077 bytes src/lay/lay/images/marked_64.png | Bin 1001 -> 1222 bytes src/lay/lay/laySalt.cc | 13 ++- src/lay/lay/laySalt.h | 7 ++ src/lay/lay/laySaltGrain.cc | 9 ++ src/lay/lay/laySaltGrain.h | 17 +++ src/lay/lay/laySaltGrains.cc | 65 +++++++++++ src/lay/lay/laySaltGrains.h | 13 +++ src/lay/lay/laySaltManagerDialog.cc | 112 +++++++++++++----- src/lay/lay/laySaltManagerDialog.h | 9 +- src/lay/lay/laySaltModel.cc | 15 ++- src/lay/unit_tests/laySalt.cc | 173 +++++++++++++++++++++++++++- 13 files changed, 416 insertions(+), 41 deletions(-) diff --git a/src/lay/lay/SaltManagerDialog.ui b/src/lay/lay/SaltManagerDialog.ui index cf5d59037..b37c0c65e 100644 --- a/src/lay/lay/SaltManagerDialog.ui +++ b/src/lay/lay/SaltManagerDialog.ui @@ -17,7 +17,7 @@ - 2 + 0 @@ -143,9 +143,15 @@ + + Qt::WheelFocus + true + + QAbstractItemView::ExtendedSelection + 64 @@ -436,9 +442,15 @@ + + Qt::WheelFocus + true + + QAbstractItemView::ExtendedSelection + 64 @@ -722,9 +734,15 @@ + + Qt::WheelFocus + true + + QAbstractItemView::ExtendedSelection + 64 @@ -786,8 +804,8 @@ 0 0 - 343 - 207 + 537 + 284 diff --git a/src/lay/lay/images/marked_24.png b/src/lay/lay/images/marked_24.png index cf50321a8848bbd8351aeec0a78931d83ee004a2..b5ffcbd6b5f6788ffe55f52a68694e7022dc1a10 100644 GIT binary patch delta 1032 zcmZ3%yp=<-Gr-TCmrII^fq{Y7)59eQNIQTq2OE%Fm05UiqM{Tx`vnFD_6v+VyPwaW z=%!xJyvftWF{EP7+gb4*!GSWz>!0s>`>M!$@|jZsLB*FhnV57>@u=Bg&nEbH?#x7& zS(z?3=d+u3sTi!_(3AZiQY5o1pKfC9<-`<(KnOnhdt0FVMjZ}p6vW^>Hvg_Z@V_-0P?#9&8bMNneSF=3_ z7jAr((9zMl=i=){pNrYNQ#L7NG4T7CKkJMi%E$d zH_ix1ioc(JN3Ue>N2Lq~nSzwyc@|%~ourMU}9_Ty8y6Wg%lk5v^Q4DkY%qnNu z*{weM&60^BVOi3)j}9{gE<#-a)avAq=brZzg_n8-1^%#=C9A`m|f6YV&ePnhi&}Z z{THXa+5a-m{{HcOuPXNB)%u>;*tK5VNox1&5Xr6D{9C`yYMT3-y{dJOy+G6-zkuXZ zJU0cE98P@r3yPK~*;%)fOtb3EKhEpfbo*H3m2-b>I@Wv-`ZD=Y>ON0dr3(KUjei^L z>eu$N7Cc^Vz|2#x_UT(G>*JfBcX#s0+b}pRx_`@ka_xu353eoFyZGgYd^B_MsTkS$ zdb2M^NfhP=>6INj^fvqN!3~>B6YmA)yICH2&vkD9(rLfH1%46gEnLfX-ZeB-!#2d( z?wM5Rm)Eh5?z3KGbspcr#PMZT{$25+Pg064>k6o$pQ)Y?6`3lQ=X6&e)|KR_*2iE64 q@8M@SAsBn4MY-iqv3V)iA$mzR3j^o? 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 O;OXk;vX0p)p$Pz?3a_33 diff --git a/src/lay/lay/laySalt.cc b/src/lay/lay/laySalt.cc index c327062ff..8c86d3f85 100644 --- a/src/lay/lay/laySalt.cc +++ b/src/lay/lay/laySalt.cc @@ -172,7 +172,12 @@ struct NameAndTopoIndexCompare } } - // TODO: UTF-8 support? + // The hidden after non-hidden + if (a->is_hidden () != b->is_hidden ()) { + return a->is_hidden () < b->is_hidden (); + } + + // Finally the name (TODO: UTF-8 support?) return a->name () < b->name (); } @@ -240,6 +245,12 @@ Salt::invalidate () emit collections_changed (); } +void +Salt::consolidate () +{ + m_root.consolidate (); + invalidate (); +} static bool remove_from_collection (SaltGrains &collection, const std::string &name) diff --git a/src/lay/lay/laySalt.h b/src/lay/lay/laySalt.h index aef3da0ac..3ab3c3a7e 100644 --- a/src/lay/lay/laySalt.h +++ b/src/lay/lay/laySalt.h @@ -185,6 +185,13 @@ public: */ bool create_grain (const SaltGrain &templ, SaltGrain &target); + /** + * @brief Removes redundant entries with same names + * + * This method will keep the first entry or the one with the higher version. + */ + void consolidate (); + /** * @brief Gets the root collection * diff --git a/src/lay/lay/laySaltGrain.cc b/src/lay/lay/laySaltGrain.cc index e91c1ccef..33620737e 100644 --- a/src/lay/lay/laySaltGrain.cc +++ b/src/lay/lay/laySaltGrain.cc @@ -41,6 +41,7 @@ namespace lay static const std::string grain_filename = "grain.xml"; SaltGrain::SaltGrain () + : m_hidden (false) { // .. nothing yet .. } @@ -62,6 +63,7 @@ SaltGrain::operator== (const SaltGrain &other) const m_author == other.m_author && m_author_contact == other.m_author_contact && m_license == other.m_license && + m_hidden == other.m_hidden && m_authored_time == other.m_authored_time && m_installed_time == other.m_installed_time; } @@ -78,6 +80,12 @@ SaltGrain::set_token (const std::string &t) m_token = t; } +void +SaltGrain::set_hidden (bool f) +{ + m_hidden = f; +} + void SaltGrain::set_version (const std::string &v) { @@ -374,6 +382,7 @@ SaltGrain::xml_elements () sp_xml_elements = new tl::XMLElementList ( tl::make_member (&SaltGrain::name, &SaltGrain::set_name, "name") + tl::make_member (&SaltGrain::token, &SaltGrain::set_token, "token") + + tl::make_member (&SaltGrain::is_hidden, &SaltGrain::set_hidden, "hidden") + tl::make_member (&SaltGrain::version, &SaltGrain::set_version, "version") + tl::make_member (&SaltGrain::api_version, &SaltGrain::set_api_version, "api-version") + tl::make_member (&SaltGrain::title, &SaltGrain::set_title, "title") + diff --git a/src/lay/lay/laySaltGrain.h b/src/lay/lay/laySaltGrain.h index c53ea6b67..54fc90ec0 100644 --- a/src/lay/lay/laySaltGrain.h +++ b/src/lay/lay/laySaltGrain.h @@ -326,6 +326,22 @@ public: */ void set_url (const std::string &u); + /** + * @brief Gets a value indicating whether the grain is hidden + * A grain can be hidden (in Salt.Mine) if it's a pure dependency package + * which is only there because others need it. Such packages are listed + * as dependencies, but they are not shown by default. + */ + bool is_hidden () const + { + return m_hidden; + } + + /** + * @brief Sets a value indicating whether the grain is hidden + */ + void set_hidden (bool f); + /** * @brief Gets the dependencies of the grain * Grains this grain depends on are installed automatically when the grain @@ -455,6 +471,7 @@ private: std::string m_author; std::string m_author_contact; std::string m_license; + bool m_hidden; QDateTime m_authored_time, m_installed_time; QImage m_icon, m_screenshot; std::vector m_dependencies; diff --git a/src/lay/lay/laySaltGrains.cc b/src/lay/lay/laySaltGrains.cc index fa97d32e9..4482635ba 100644 --- a/src/lay/lay/laySaltGrains.cc +++ b/src/lay/lay/laySaltGrains.cc @@ -235,6 +235,71 @@ SaltGrains::from_path (const std::string &path, const std::string &prefix) return grains; } +void +SaltGrains::merge_with (const lay::SaltGrains &other) +{ + for (lay::SaltGrains::collection_iterator c = other.begin_collections (); c != other.end_collections (); ++c) { + add_collection (*c); + } + for (lay::SaltGrains::grain_iterator g = other.begin_grains (); g != other.end_grains (); ++g) { + add_grain (*g); + } + consolidate (); +} + +void +SaltGrains::consolidate () +{ + std::vector collection_to_delete; + + std::map collection_by_name; + for (collections_type::iterator c = m_collections.begin (); c != m_collections.end (); ++c) { + + std::map::iterator cn = collection_by_name.find (c->name ()); + if (cn != collection_by_name.end ()) { + cn->second->merge_with (*c); + collection_to_delete.push_back (c); + } else { + c->consolidate (); + collection_by_name.insert (std::make_pair (c->name (), c)); + } + + } + + // actually delete the additional collections + for (std::vector::reverse_iterator i = collection_to_delete.rbegin (); i != collection_to_delete.rend (); ++i) { + remove_collection (*i); + } + + + std::vector to_delete; + + std::map grain_by_name; + for (lay::SaltGrains::grain_iterator g = begin_grains (); g != end_grains (); ++g) { + + std::map::iterator gn = grain_by_name.find (g->name ()); + if (gn != grain_by_name.end ()) { + + // take the one with the higher version. On equal version use the first one. + if (lay::SaltGrain::compare_versions (gn->second->version (), g->version ()) < 0) { + to_delete.push_back (gn->second); + gn->second = g; + } else { + to_delete.push_back (g); + } + + } else { + grain_by_name.insert (std::make_pair (g->name (), g)); + } + + } + + // actually delete the additional elements + for (std::vector::reverse_iterator i = to_delete.rbegin (); i != to_delete.rend (); ++i) { + remove_grain (*i); + } +} + static tl::XMLElementList s_group_struct = tl::make_member (&SaltGrains::name, &SaltGrains::set_name, "name") + tl::make_member (&SaltGrains::include, "include") + diff --git a/src/lay/lay/laySaltGrains.h b/src/lay/lay/laySaltGrains.h index 77605fcb7..f9ba6746f 100644 --- a/src/lay/lay/laySaltGrains.h +++ b/src/lay/lay/laySaltGrains.h @@ -171,6 +171,19 @@ public: */ bool remove_grain (grain_iterator iter, bool with_files = false); + /** + * @brief Merges the other collection into this one + * This method will apply the rules of "consolidate" for grains and will merge + * grain collections with the same name into one. + */ + void merge_with (const lay::SaltGrains &other); + + /** + * @brief Removes redundant entries with same names + * This method will keep the first entry or the one with the higher version. + */ + void consolidate (); + /** * @brief Gets a value indicating whether the collection is empty */ diff --git a/src/lay/lay/laySaltManagerDialog.cc b/src/lay/lay/laySaltManagerDialog.cc index 4adaa603c..32bd50259 100644 --- a/src/lay/lay/laySaltManagerDialog.cc +++ b/src/lay/lay/laySaltManagerDialog.cc @@ -132,6 +132,7 @@ SaltManagerDialog::SaltManagerDialog (QWidget *parent, lay::Salt *salt, const st tl::log << tl::to_string (tr ("Downloading package repository from %1").arg (tl::to_qstring (m_salt_mine_url))); m_salt_mine.load (m_salt_mine_url); } + m_salt_mine.consolidate (); } catch (tl::Exception &ex) { tl::error << ex.msg (); } @@ -168,10 +169,10 @@ SaltManagerDialog::SaltManagerDialog (QWidget *parent, lay::Salt *salt, const st update_models (); - connect (salt_view->selectionModel (), SIGNAL (currentChanged (const QModelIndex &, const QModelIndex &)), this, SLOT (current_changed ())); + connect (salt_view->selectionModel (), SIGNAL (selectionChanged (const QItemSelection &, const QItemSelection &)), this, SLOT (selected_changed ())); connect (salt_view, SIGNAL (doubleClicked (const QModelIndex &)), this, SLOT (edit_properties ())); - connect (salt_mine_view_new->selectionModel (), SIGNAL (currentChanged (const QModelIndex &, const QModelIndex &)), this, SLOT (mine_new_current_changed ()), Qt::QueuedConnection); - connect (salt_mine_view_update->selectionModel (), SIGNAL (currentChanged (const QModelIndex &, const QModelIndex &)), this, SLOT (mine_update_current_changed ()), Qt::QueuedConnection); + connect (salt_mine_view_new->selectionModel (), SIGNAL (selectionChanged (const QItemSelection &, const QItemSelection &)), this, SLOT (mine_new_selected_changed ()), Qt::QueuedConnection); + connect (salt_mine_view_update->selectionModel (), SIGNAL (selectionChanged (const QItemSelection &, const QItemSelection &)), this, SLOT (mine_update_selected_changed ()), Qt::QueuedConnection); connect (salt_mine_view_new, SIGNAL (doubleClicked (const QModelIndex &)), this, SLOT (mark_clicked ())); connect (salt_mine_view_update, SIGNAL (doubleClicked (const QModelIndex &)), this, SLOT (mark_clicked ())); @@ -235,9 +236,9 @@ SaltManagerDialog::SaltManagerDialog (QWidget *parent, lay::Salt *salt, const st connect (actionMarkForUpdate, SIGNAL (triggered ()), this, SLOT (mark_clicked ())); connect (actionUnmarkForUpdate, SIGNAL (triggered ()), this, SLOT (mark_clicked ())); - mine_update_current_changed (); - mine_new_current_changed (); - current_changed (); + mine_update_selected_changed (); + mine_new_selected_changed (); + selected_changed (); } void @@ -285,13 +286,13 @@ SaltManagerDialog::show_marked_only_new () return; } - salt_mine_view_new->setCurrentIndex (QModelIndex ()); + salt_mine_view_new->clearSelection (); for (int i = model->rowCount (QModelIndex ()); i > 0; ) { --i; SaltGrain *g = model->grain_from_index (model->index (i, 0, QModelIndex ())); salt_mine_view_new->setRowHidden (i, show_marked_only && !(g && model->is_marked (g->name ()))); - mine_new_current_changed (); + mine_new_selected_changed (); } } @@ -307,13 +308,13 @@ SaltManagerDialog::show_marked_only_update () return; } - salt_mine_view_update->setCurrentIndex (QModelIndex ()); + salt_mine_view_new->clearSelection (); for (int i = model->rowCount (QModelIndex ()); i > 0; ) { --i; SaltGrain *g = model->grain_from_index (model->index (i, 0, QModelIndex ())); salt_mine_view_update->setRowHidden (i, show_marked_only && !(g && model->is_marked (g->name ()))); - mine_update_current_changed (); + mine_update_selected_changed (); } } @@ -424,11 +425,15 @@ SaltManagerDialog::mark_clicked () return; } - SaltGrain *g = model->grain_from_index (view->currentIndex ()); - if (g) { - model->set_marked (g->name (), toggle ? ! model->is_marked (g->name ()) : set); - update_apply_state (); + QModelIndexList indexes = view->selectionModel ()->selectedIndexes (); + for (QModelIndexList::const_iterator i = indexes.begin (); i != indexes.end (); ++i) { + SaltGrain *g = model->grain_from_index (*i); + if (g) { + model->set_marked (g->name (), toggle ? ! model->is_marked (g->name ()) : set); + } } + + update_apply_state (); } void @@ -461,7 +466,7 @@ SaltManagerDialog::update_apply_state () } - model = dynamic_cast (salt_mine_view_update->model ()); + model = dynamic_cast (salt_mine_view_update->model ()); if (model) { int marked = 0; @@ -548,7 +553,7 @@ SaltManagerDialog::edit_properties () QMessageBox::critical (this, tr ("Package is not Editable"), tr ("This package cannot be edited.\n\nEither you don't have write permissions on the directory or the package was installed from a repository.")); } else if (mp_properties_dialog->exec_dialog (g, mp_salt)) { - current_changed (); + selected_changed (); } } } @@ -566,6 +571,7 @@ SaltManagerDialog::set_current_grain_by_name (const std::string ¤t) QModelIndex index = model->index (i, 0, QModelIndex ()); SaltGrain *g = model->grain_from_index (index); if (g && g->name () == current) { + salt_view->clearSelection (); salt_view->setCurrentIndex (index); break; } @@ -611,13 +617,22 @@ SaltManagerDialog::delete_grain () { BEGIN_PROTECTED - SaltGrain *g = current_grain (); - if (! g) { + std::vector gg = current_grains (); + if (gg.empty ()) { throw tl::Exception (tl::to_string (tr ("No package selected to delete"))); } - if (QMessageBox::question (this, tr ("Delete Package"), tr ("Are you sure to delete package '%1'?").arg (tl::to_qstring (g->name ())), QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes) { - mp_salt->remove_grain (*g); + if (gg.size () == 1) { + SaltGrain *g = gg.front (); + if (QMessageBox::question (this, tr ("Delete Package"), tr ("Are you sure to delete package '%1'?").arg (tl::to_qstring (g->name ())), QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes) { + mp_salt->remove_grain (*g); + } + } else { + if (QMessageBox::question (this, tr ("Delete Packages"), tr ("Are you sure to delete the selected %1 packages?").arg (int (gg.size ())), QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes) { + for (std::vector::const_iterator i = gg.begin (); i != gg.end (); ++i) { + mp_salt->remove_grain (**i); + } + } } END_PROTECTED @@ -782,6 +797,7 @@ SaltManagerDialog::update_models () // select the first grain if (mine_model->rowCount (QModelIndex ()) > 0) { + salt_mine_view_update->clearSelection (); salt_mine_view_update->setCurrentIndex (mine_model->index (0, 0, QModelIndex ())); } @@ -804,17 +820,18 @@ SaltManagerDialog::update_models () // select the first grain if (mine_model->rowCount (QModelIndex ()) > 0) { + salt_mine_view_new->clearSelection (); salt_mine_view_new->setCurrentIndex (mine_model->index (0, 0, QModelIndex ())); } - mine_new_current_changed (); - mine_update_current_changed (); - current_changed (); + mine_new_selected_changed (); + mine_update_selected_changed (); + selected_changed (); update_apply_state (); } void -SaltManagerDialog::current_changed () +SaltManagerDialog::selected_changed () { SaltGrain *g = current_grain (); details_text->set_grain (g); @@ -832,17 +849,49 @@ lay::SaltGrain * SaltManagerDialog::current_grain () { SaltModel *model = dynamic_cast (salt_view->model ()); - return model ? model->grain_from_index (salt_view->currentIndex ()) : 0; + + QModelIndexList indexes = salt_view->selectionModel ()->selectedIndexes (); + if (indexes.size () == 1 && model) { + return model->grain_from_index (indexes.front ()); + } else { + return 0; + } +} + +std::vector +SaltManagerDialog::current_grains () +{ + std::vector res; + + SaltModel *model = dynamic_cast (salt_view->model ()); + if (model) { + + QModelIndexList indexes = salt_view->selectionModel ()->selectedIndexes (); + for (QModelIndexList::const_iterator i = indexes.begin (); i != indexes.end (); ++i) { + lay::SaltGrain *g = model->grain_from_index (*i); + if (g) { + res.push_back (g); + } + } + + } + + return res; } void -SaltManagerDialog::mine_update_current_changed () +SaltManagerDialog::mine_update_selected_changed () { BEGIN_PROTECTED SaltModel *model = dynamic_cast (salt_mine_view_update->model ()); tl_assert (model != 0); - SaltGrain *g = model->grain_from_index (salt_mine_view_update->currentIndex ()); + + SaltGrain *g = 0; + QModelIndexList indexes = salt_mine_view_update->selectionModel ()->selectedIndexes(); + if (indexes.size () == 1) { + g = model->grain_from_index (indexes.front ()); + } details_update_frame->setEnabled (g != 0); @@ -852,13 +901,18 @@ END_PROTECTED } void -SaltManagerDialog::mine_new_current_changed () +SaltManagerDialog::mine_new_selected_changed () { BEGIN_PROTECTED SaltModel *model = dynamic_cast (salt_mine_view_new->model ()); tl_assert (model != 0); - SaltGrain *g = model->grain_from_index (salt_mine_view_new->currentIndex ()); + + SaltGrain *g = 0; + QModelIndexList indexes = salt_mine_view_new->selectionModel ()->selectedIndexes(); + if (indexes.size () == 1) { + g = model->grain_from_index (indexes.front ()); + } details_new_frame->setEnabled (g != 0); diff --git a/src/lay/lay/laySaltManagerDialog.h b/src/lay/lay/laySaltManagerDialog.h index 9505adab0..a2d64ad6e 100644 --- a/src/lay/lay/laySaltManagerDialog.h +++ b/src/lay/lay/laySaltManagerDialog.h @@ -29,6 +29,7 @@ #include #include +#include namespace lay { @@ -81,17 +82,17 @@ private slots: /** * @brief Called when the currently selected package (grain) has changed */ - void current_changed (); + void selected_changed (); /** * @brief Called when the currently selected package from the update page has changed */ - void mine_update_current_changed (); + void mine_update_selected_changed (); /** * @brief Called when the currently selected package from the new installation page has changed */ - void mine_new_current_changed (); + void mine_new_selected_changed (); /** * @brief Called when the "edit" button is pressed @@ -172,10 +173,12 @@ private: int m_current_tab; SaltGrain *current_grain (); + std::vector current_grains (); void set_current_grain_by_name (const std::string ¤t); void update_models (); void update_apply_state (); void get_remote_grain_info (lay::SaltGrain *g, SaltGrainDetailsTextWidget *details); + void consolidate_salt_mine_entries (); }; } diff --git a/src/lay/lay/laySaltModel.cc b/src/lay/lay/laySaltModel.cc index b1686c27f..456894b10 100644 --- a/src/lay/lay/laySaltModel.cc +++ b/src/lay/lay/laySaltModel.cc @@ -135,10 +135,16 @@ SaltModel::data (const QModelIndex &index, int role) const } bool en = is_enabled (g->name ()); + bool hidden = g->is_hidden (); std::string text = ""; - if (! en) { + if (! en || hidden) { text += ""; + } else { + text += ""; + } + if (hidden) { + text += ""; } text += "

"; text += tl::escaped_to_html (g->name ()); @@ -168,9 +174,12 @@ SaltModel::data (const QModelIndex &index, int role) const } } - if (! en) { - text += ""; + if (hidden) { + text += "

"; + text += tl::to_string (tr ("This package is an auxiliary package for use with other packages.")); + text += "

"; } + text += "
"; text += ""; return tl::to_qstring (text); diff --git a/src/lay/unit_tests/laySalt.cc b/src/lay/unit_tests/laySalt.cc index 05d97e80f..746695ac7 100644 --- a/src/lay/unit_tests/laySalt.cc +++ b/src/lay/unit_tests/laySalt.cc @@ -30,7 +30,7 @@ #include #include -static std::string grains_to_string (const lay::SaltGrains &gg) +static std::string grains_to_string (const lay::SaltGrains &gg, bool with_version = false) { std::string res; res += "["; @@ -41,6 +41,15 @@ static std::string grains_to_string (const lay::SaltGrains &gg) } first = false; res += g->name (); + if (with_version) { + res += "("; + res += g->version (); + if (!g->url ().empty ()) { + res += ":"; + res += g->url (); + } + res += ")"; + } } for (lay::SaltGrains::collection_iterator gc = gg.begin_collections (); gc != gg.end_collections (); ++gc) { if (! first) { @@ -48,7 +57,7 @@ static std::string grains_to_string (const lay::SaltGrains &gg) } first = false; res += gc->name (); - res += grains_to_string (*gc); + res += grains_to_string (*gc, with_version); } res += "]"; return res; @@ -403,3 +412,163 @@ TEST (5) EXPECT_EQ (tl::join (names, ","), "g3,g2,g1,g4"); } + +TEST (6) +{ + lay::SaltGrains gg1; + lay::SaltGrains gg2; + + lay::SaltGrain ga1; + ga1.set_name ("a"); + ga1.set_url ("url1"); + ga1.set_version ("1.0"); + + lay::SaltGrain ga2; + ga2.set_name ("a"); + ga2.set_url ("url2"); + ga2.set_version ("1.1"); + + lay::SaltGrain gb; + gb.set_name ("b"); + + lay::SaltGrain gc; + gc.set_name ("c"); + + gg1.add_grain (ga1); + gg1.add_grain (gb); + + gg2.add_grain (gc); + gg2.add_grain (ga2); + + // higher version wins + gg1.merge_with (gg2); + EXPECT_EQ (grains_to_string (gg1, true), "[b(),c(),a(1.1:url2)]"); + + gg1 = lay::SaltGrains (); + gg2 = lay::SaltGrains (); + + gg2.add_grain (gc); + gg1.add_grain (ga2); + gg1.add_grain (gb); + + gg2.add_grain (ga1); + + // higher version wins - also in different order + gg1.merge_with (gg2); + EXPECT_EQ (grains_to_string (gg1, true), "[a(1.1:url2),b(),c()]"); + + gg1 = lay::SaltGrains (); + gg2 = lay::SaltGrains (); + + gg2.add_grain (gc); + ga2.set_version ("1.0"); + gg1.add_grain (ga2); + gg1.add_grain (gb); + + gg2.add_grain (ga1); + + // first one wins on same version + gg1.merge_with (gg2); + EXPECT_EQ (grains_to_string (gg1, true), "[a(1.0:url2),b(),c()]"); + + gg1 = lay::SaltGrains (); + + gg1.add_grain (gc); + gg1.add_grain (ga2); + gg1.add_grain (ga1); + gg1.add_grain (gb); + + // consolidate does the same on one list + gg1.consolidate (); + EXPECT_EQ (grains_to_string (gg1, true), "[c(),a(1.0:url2),b()]"); + + gg1 = lay::SaltGrains (); + + gg1.add_grain (ga1); + gg1.add_grain (ga2); + gg1.add_grain (gb); + gg1.add_grain (gc); + + // consolidate does the same on one list + gg1.consolidate (); + EXPECT_EQ (grains_to_string (gg1, true), "[a(1.0:url1),b(),c()]"); + + gg1 = lay::SaltGrains (); + + ga1.set_version ("1.1"); + gg1.add_grain (ga1); + gg1.add_grain (ga2); + gg1.add_grain (gb); + + // consolidate does the same on one list + gg1.consolidate (); + EXPECT_EQ (grains_to_string (gg1, true), "[a(1.1:url1),b()]"); + + + // merging of sub-collections + + gg1 = lay::SaltGrains (); + gg2 = lay::SaltGrains (); + + lay::SaltGrains gga1; + gga1.set_name ("a"); + + { + lay::SaltGrain g; + g.set_name ("a"); + g.set_version ("1.0"); + g.set_url ("url1"); + gga1.add_grain (g); + } + + { + lay::SaltGrain g; + g.set_name ("b"); + gga1.add_grain (g); + } + + lay::SaltGrains ggb; + ggb.set_name ("b"); + + { + lay::SaltGrain g; + g.set_name ("x"); + ggb.add_grain (g); + } + + gg1.add_collection (gga1); + gg1.add_collection (ggb); + + lay::SaltGrains gga2; + gga2.set_name ("a"); + + { + lay::SaltGrain g; + g.set_name ("a"); + g.set_version ("1.1"); + g.set_url ("url2"); + gga2.add_grain (g); + } + + { + lay::SaltGrain g; + g.set_name ("c"); + gga2.add_grain (g); + } + + lay::SaltGrains ggc; + ggc.set_name ("c"); + + { + lay::SaltGrain g; + g.set_name ("y"); + ggc.add_grain (g); + } + + gg2.add_collection (gga2); + gg2.add_collection (ggc); + + // gg2:a collection is merged into gg1:a, gg2:c is copied. + gg1.merge_with (gg2); + EXPECT_EQ (grains_to_string (gg1, true), "[a[b(),a(1.1:url2),c()],b[x()],c[y()]]"); +}