Blame SOURCES/0001-history-Fix-dnf-history-rollback-when-a-package-was-removed-RhBug1683134.patch

9fc8e2
From a96d701f7f55ff475e11ac9cda63b81c31c54e7a Mon Sep 17 00:00:00 2001
9fc8e2
From: Daniel Mach <dmach@redhat.com>
9fc8e2
Date: Wed, 6 May 2020 08:34:46 +0200
9fc8e2
Subject: [PATCH] history: Fix dnf history rollback when a package was removed
9fc8e2
 (RhBug:1683134)
9fc8e2
9fc8e2
---
9fc8e2
 libdnf/transaction/MergedTransaction.cpp      |  25 +++-
9fc8e2
 .../transaction/MergedTransactionTest.cpp     | 120 ++++++++++++++++--
9fc8e2
 .../transaction/MergedTransactionTest.hpp     |   6 +-
9fc8e2
 3 files changed, 137 insertions(+), 14 deletions(-)
9fc8e2
9fc8e2
diff --git a/libdnf/transaction/MergedTransaction.cpp b/libdnf/transaction/MergedTransaction.cpp
9fc8e2
index a7c06ffa9..a8d878cb5 100644
9fc8e2
--- a/libdnf/transaction/MergedTransaction.cpp
9fc8e2
+++ b/libdnf/transaction/MergedTransaction.cpp
9fc8e2
@@ -19,6 +19,7 @@
9fc8e2
  */
9fc8e2
 
9fc8e2
 #include "MergedTransaction.hpp"
9fc8e2
+#include <algorithm>
9fc8e2
 #include <vector>
9fc8e2
 
9fc8e2
 namespace libdnf {
9fc8e2
@@ -171,6 +172,21 @@ MergedTransaction::getConsoleOutput()
9fc8e2
     return output;
9fc8e2
 }
9fc8e2
 
9fc8e2
+
9fc8e2
+static bool transaction_item_sort_function(const std::shared_ptr<TransactionItemBase> lhs, const std::shared_ptr<TransactionItemBase> rhs) {
9fc8e2
+    if (lhs->isForwardAction() && rhs->isForwardAction()) {
9fc8e2
+        return false;
9fc8e2
+    }
9fc8e2
+    if (lhs->isBackwardAction() && rhs->isBackwardAction()) {
9fc8e2
+        return false;
9fc8e2
+    }
9fc8e2
+    if (lhs->isBackwardAction()) {
9fc8e2
+        return true;
9fc8e2
+    }
9fc8e2
+    return false;
9fc8e2
+}
9fc8e2
+
9fc8e2
+
9fc8e2
 /**
9fc8e2
  * Get list of transaction items involved in the merged transaction
9fc8e2
  * Actions are merged using following rules:
9fc8e2
@@ -203,6 +219,9 @@ MergedTransaction::getItems()
9fc8e2
     // iterate over transaction
9fc8e2
     for (auto t : transactions) {
9fc8e2
         auto transItems = t->getItems();
9fc8e2
+        // sort transaction items by their action type - forward/backward
9fc8e2
+        // this fixes behavior of the merging algorithm in several edge cases
9fc8e2
+        std::sort(transItems.begin(), transItems.end(), transaction_item_sort_function);
9fc8e2
         // iterate over transaction items
9fc8e2
         for (auto transItem : transItems) {
9fc8e2
             // get item and its type
9fc8e2
@@ -383,10 +402,6 @@ MergedTransaction::mergeItem(ItemPairMap &itemPairMap, TransactionItemBasePtr mT
9fc8e2
     auto firstState = previousItemPair.first->getAction();
9fc8e2
     auto newState = mTransItem->getAction();
9fc8e2
 
9fc8e2
-    if (firstState == TransactionItemAction::INSTALL && mTransItem->isBackwardAction()) {
9fc8e2
-        return;
9fc8e2
-    }
9fc8e2
-
9fc8e2
     switch (firstState) {
9fc8e2
         case TransactionItemAction::REMOVE:
9fc8e2
         case TransactionItemAction::OBSOLETED:
9fc8e2
@@ -399,6 +414,8 @@ MergedTransaction::mergeItem(ItemPairMap &itemPairMap, TransactionItemBasePtr mT
9fc8e2
                 // Install -> Remove = (nothing)
9fc8e2
                 itemPairMap.erase(name);
9fc8e2
                 break;
9fc8e2
+            } else if (mTransItem->isBackwardAction()) {
9fc8e2
+                break;
9fc8e2
             }
9fc8e2
             // altered -> transfer install to the altered package
9fc8e2
             mTransItem->setAction(TransactionItemAction::INSTALL);
9fc8e2
diff --git a/tests/libdnf/transaction/MergedTransactionTest.cpp b/tests/libdnf/transaction/MergedTransactionTest.cpp
9fc8e2
index 90ad182cf..52507700b 100644
9fc8e2
--- a/tests/libdnf/transaction/MergedTransactionTest.cpp
9fc8e2
+++ b/tests/libdnf/transaction/MergedTransactionTest.cpp
9fc8e2
@@ -700,7 +700,7 @@ MergedTransactionTest::test_downgrade()
9fc8e2
 }
9fc8e2
 
9fc8e2
 void
9fc8e2
-MergedTransactionTest::test_install_downgrade()
9fc8e2
+MergedTransactionTest::test_install_downgrade_upgrade_remove()
9fc8e2
 {
9fc8e2
     auto trans1 = std::make_shared< libdnf::swdb_private::Transaction >(conn);
9fc8e2
     trans1->addItem(
9fc8e2
@@ -724,19 +724,123 @@ MergedTransactionTest::test_install_downgrade()
9fc8e2
         TransactionItemReason::USER
9fc8e2
     );
9fc8e2
 
9fc8e2
+    auto trans3 = std::make_shared< libdnf::swdb_private::Transaction >(conn);
9fc8e2
+    trans3->addItem(
9fc8e2
+        nevraToRPMItem(conn, "tour-0:4.6-1.noarch"),
9fc8e2
+        "repo2",
9fc8e2
+        TransactionItemAction::UPGRADED,
9fc8e2
+        TransactionItemReason::USER
9fc8e2
+    );
9fc8e2
+    trans3->addItem(
9fc8e2
+        nevraToRPMItem(conn, "tour-0:4.8-1.noarch"),
9fc8e2
+        "repo1",
9fc8e2
+        TransactionItemAction::UPGRADE,
9fc8e2
+        TransactionItemReason::USER
9fc8e2
+    );
9fc8e2
+
9fc8e2
+    auto trans4 = std::make_shared< libdnf::swdb_private::Transaction >(conn);
9fc8e2
+    trans4->addItem(
9fc8e2
+        nevraToRPMItem(conn, "tour-0:4.8-1.noarch"),
9fc8e2
+        "repo1",
9fc8e2
+        TransactionItemAction::REMOVE,
9fc8e2
+        TransactionItemReason::USER
9fc8e2
+    );
9fc8e2
+
9fc8e2
     MergedTransaction merged(trans1);
9fc8e2
+
9fc8e2
+    // test merging trans1, trans2
9fc8e2
     merged.merge(trans2);
9fc8e2
+    auto items2 = merged.getItems();
9fc8e2
+    CPPUNIT_ASSERT_EQUAL(1, (int)items2.size());
9fc8e2
+    auto item2 = items2.at(0);
9fc8e2
+    CPPUNIT_ASSERT_EQUAL(std::string("tour-4.6-1.noarch"), item2->getItem()->toStr());
9fc8e2
+    CPPUNIT_ASSERT_EQUAL(std::string("repo2"), item2->getRepoid());
9fc8e2
+    CPPUNIT_ASSERT_EQUAL(TransactionItemAction::INSTALL, item2->getAction());
9fc8e2
+    CPPUNIT_ASSERT_EQUAL(TransactionItemReason::USER, item2->getReason());
9fc8e2
 
9fc8e2
-    auto items = merged.getItems();
9fc8e2
-    CPPUNIT_ASSERT_EQUAL(1, (int)items.size());
9fc8e2
+    // test merging trans1, trans2, trans3
9fc8e2
+    merged.merge(trans3);
9fc8e2
+    auto items3 = merged.getItems();
9fc8e2
+    CPPUNIT_ASSERT_EQUAL(1, (int)items3.size());
9fc8e2
+    auto item3 = items3.at(0);
9fc8e2
+    CPPUNIT_ASSERT_EQUAL(std::string("tour-4.8-1.noarch"), item3->getItem()->toStr());
9fc8e2
+    CPPUNIT_ASSERT_EQUAL(std::string("repo1"), item3->getRepoid());
9fc8e2
+    CPPUNIT_ASSERT_EQUAL(TransactionItemAction::INSTALL, item3->getAction());
9fc8e2
+    CPPUNIT_ASSERT_EQUAL(TransactionItemReason::USER, item3->getReason());
9fc8e2
 
9fc8e2
-    auto item = items.at(0);
9fc8e2
-    CPPUNIT_ASSERT_EQUAL(std::string("tour-4.6-1.noarch"), item->getItem()->toStr());
9fc8e2
-    CPPUNIT_ASSERT_EQUAL(std::string("repo2"), item->getRepoid());
9fc8e2
-    CPPUNIT_ASSERT_EQUAL(TransactionItemAction::INSTALL, item->getAction());
9fc8e2
-    CPPUNIT_ASSERT_EQUAL(TransactionItemReason::USER, item->getReason());
9fc8e2
+    // test merging trans1, trans2, trans3, trans4
9fc8e2
+    merged.merge(trans4);
9fc8e2
+    auto items4 = merged.getItems();
9fc8e2
+    CPPUNIT_ASSERT_EQUAL(0, (int)items4.size());
9fc8e2
+    // trans4 removes the package, empty output is expected
9fc8e2
 }
9fc8e2
 
9fc8e2
+
9fc8e2
+void
9fc8e2
+MergedTransactionTest::test_downgrade_upgrade_remove()
9fc8e2
+{
9fc8e2
+    auto trans1 = std::make_shared< libdnf::swdb_private::Transaction >(conn);
9fc8e2
+    trans1->addItem(
9fc8e2
+        nevraToRPMItem(conn, "tour-0:4.6-1.noarch"),
9fc8e2
+        "repo2",
9fc8e2
+        TransactionItemAction::DOWNGRADE,
9fc8e2
+        TransactionItemReason::USER
9fc8e2
+    );
9fc8e2
+    trans1->addItem(
9fc8e2
+        nevraToRPMItem(conn, "tour-0:4.8-1.noarch"),
9fc8e2
+        "repo1",
9fc8e2
+        TransactionItemAction::DOWNGRADED,
9fc8e2
+        TransactionItemReason::USER
9fc8e2
+    );
9fc8e2
+
9fc8e2
+    // items are in reversed order than in test_install_downgrade_upgrade_remove()
9fc8e2
+    // fixing this required ordering transaction items by forward/backward action
9fc8e2
+    auto trans2 = std::make_shared< libdnf::swdb_private::Transaction >(conn);
9fc8e2
+    trans2->addItem(
9fc8e2
+        nevraToRPMItem(conn, "tour-0:4.8-1.noarch"),
9fc8e2
+        "repo1",
9fc8e2
+        TransactionItemAction::UPGRADE,
9fc8e2
+        TransactionItemReason::USER
9fc8e2
+    );
9fc8e2
+    trans2->addItem(
9fc8e2
+        nevraToRPMItem(conn, "tour-0:4.6-1.noarch"),
9fc8e2
+        "repo2",
9fc8e2
+        TransactionItemAction::UPGRADED,
9fc8e2
+        TransactionItemReason::USER
9fc8e2
+    );
9fc8e2
+
9fc8e2
+    auto trans3 = std::make_shared< libdnf::swdb_private::Transaction >(conn);
9fc8e2
+    trans3->addItem(
9fc8e2
+        nevraToRPMItem(conn, "tour-0:4.8-1.noarch"),
9fc8e2
+        "repo1",
9fc8e2
+        TransactionItemAction::REMOVE,
9fc8e2
+        TransactionItemReason::USER
9fc8e2
+    );
9fc8e2
+
9fc8e2
+    MergedTransaction merged(trans1);
9fc8e2
+
9fc8e2
+    // test merging trans1, trans2
9fc8e2
+    merged.merge(trans2);
9fc8e2
+    auto items2 = merged.getItems();
9fc8e2
+    CPPUNIT_ASSERT_EQUAL(1, (int)items2.size());
9fc8e2
+    auto item2 = items2.at(0);
9fc8e2
+    CPPUNIT_ASSERT_EQUAL(std::string("tour-4.8-1.noarch"), item2->getItem()->toStr());
9fc8e2
+    CPPUNIT_ASSERT_EQUAL(std::string("repo1"), item2->getRepoid());
9fc8e2
+    CPPUNIT_ASSERT_EQUAL(TransactionItemAction::REINSTALL, item2->getAction());
9fc8e2
+    CPPUNIT_ASSERT_EQUAL(TransactionItemReason::USER, item2->getReason());
9fc8e2
+
9fc8e2
+    // test merging trans1, trans2, trans3
9fc8e2
+    merged.merge(trans3);
9fc8e2
+    auto items3 = merged.getItems();
9fc8e2
+    CPPUNIT_ASSERT_EQUAL(1, (int)items3.size());
9fc8e2
+    auto item3 = items3.at(0);
9fc8e2
+    CPPUNIT_ASSERT_EQUAL(std::string("tour-4.8-1.noarch"), item3->getItem()->toStr());
9fc8e2
+    CPPUNIT_ASSERT_EQUAL(std::string("repo1"), item3->getRepoid());
9fc8e2
+    CPPUNIT_ASSERT_EQUAL(TransactionItemAction::REMOVE, item3->getAction());
9fc8e2
+    CPPUNIT_ASSERT_EQUAL(TransactionItemReason::USER, item3->getReason());
9fc8e2
+}
9fc8e2
+
9fc8e2
+
9fc8e2
 void
9fc8e2
 MergedTransactionTest::test_multilib_identity()
9fc8e2
 {
9fc8e2
diff --git a/tests/libdnf/transaction/MergedTransactionTest.hpp b/tests/libdnf/transaction/MergedTransactionTest.hpp
9fc8e2
index 9f1ed660a..77585e865 100644
9fc8e2
--- a/tests/libdnf/transaction/MergedTransactionTest.hpp
9fc8e2
+++ b/tests/libdnf/transaction/MergedTransactionTest.hpp
9fc8e2
@@ -26,7 +26,8 @@ class MergedTransactionTest : public CppUnit::TestCase {
9fc8e2
     CPPUNIT_TEST(test_add_obsoleted_obsoleted);
9fc8e2
 
9fc8e2
     CPPUNIT_TEST(test_downgrade);
9fc8e2
-    CPPUNIT_TEST(test_install_downgrade);
9fc8e2
+    CPPUNIT_TEST(test_install_downgrade_upgrade_remove);
9fc8e2
+    CPPUNIT_TEST(test_downgrade_upgrade_remove);
9fc8e2
 
9fc8e2
     CPPUNIT_TEST(test_multilib_identity);
9fc8e2
 
9fc8e2
@@ -56,7 +57,8 @@ class MergedTransactionTest : public CppUnit::TestCase {
9fc8e2
     // END: tests ported from DNF unit tests
9fc8e2
 
9fc8e2
     void test_downgrade();
9fc8e2
-    void test_install_downgrade();
9fc8e2
+    void test_install_downgrade_upgrade_remove();
9fc8e2
+    void test_downgrade_upgrade_remove();
9fc8e2
 
9fc8e2
     void test_multilib_identity();
9fc8e2
 private: