Imported Upstream version 17.17.0 upstream/17.17.0
authorDongHun Kwak <dh0128.kwak@samsung.com>
Fri, 27 Nov 2020 06:44:43 +0000 (15:44 +0900)
committerDongHun Kwak <dh0128.kwak@samsung.com>
Fri, 27 Nov 2020 06:44:43 +0000 (15:44 +0900)
40 files changed:
VERSION.cmake
libzypp.spec.cmake
package/libzypp.changes
tests/lib/TestSetup.h
tests/parser/ProductFileReader_test.cc
tests/repo/RepoLicense_test.cc
tests/repo/RepoSigcheck_test.cc
tests/sat/LookupAttr_test.cc
tests/sat/Pool_test.cc
tests/sat/Solvable_test.cc
tests/zypp/CMakeLists.txt
tests/zypp/Capabilities_test.cc
tests/zypp/Dup_test.cc
tests/zypp/ExtendedPool_test.cc
tests/zypp/InstanceId_test.cc
tests/zypp/Locks_test.cc
tests/zypp/PoolQueryCC_test.cc
tests/zypp/PoolQuery_test.cc
tests/zypp/PurgeKernels_test.cc [new file with mode: 0644]
tests/zypp/Resolver_test.cc
tests/zypp/RpmPkgSigCheck_test.cc
tests/zypp/Selectable_test.cc
tests/zypp/data/PurgeKernels/arch/solver-system.xml [new file with mode: 0644]
tests/zypp/data/PurgeKernels/arch/solver-test.xml [new file with mode: 0644]
tests/zypp/data/PurgeKernels/simple/solver-system.xml [new file with mode: 0644]
tests/zypp/data/PurgeKernels/simple/solver-test.xml [new file with mode: 0644]
tests/zypp/data/PurgeKernels/withdeps/solver-system.xml [new file with mode: 0644]
tests/zypp/data/PurgeKernels/withdeps/solver-test.xml [new file with mode: 0644]
tools/zypp-NameReqPrv.cc
zypp/CMakeLists.txt
zypp/Date.cc
zypp/PurgeKernels.cc [new file with mode: 0644]
zypp/PurgeKernels.h [new file with mode: 0644]
zypp/ZConfig.cc
zypp/ZConfig.h
zypp/base/Iterator.h
zypp/base/String.h
zypp/sat/detail/PoolImpl.cc
zypp/solver/detail/SATResolver.cc
zypp/target/RpmPostTransCollector.cc

index 877256e..e5e73d8 100644 (file)
@@ -60,9 +60,9 @@
 #
 SET(LIBZYPP_MAJOR "17")
 SET(LIBZYPP_COMPATMINOR "12")
-SET(LIBZYPP_MINOR "16")
+SET(LIBZYPP_MINOR "17")
 SET(LIBZYPP_PATCH "0")
 #
-# LAST RELEASED: 17.16.0 (12)
+# LAST RELEASED: 17.17.0 (12)
 # (The number in parenthesis is LIBZYPP_COMPATMINOR)
 #=======
index 8aae395..376b6a1 100644 (file)
@@ -71,7 +71,7 @@ BuildRequires:  pkgconfig
 BuildRequires:  pkg-config
 %endif
 
-BuildRequires:  libsolv-devel >= 0.7.7
+BuildRequires:  libsolv-devel >= 0.7.8
 %if 0%{?suse_version} >= 1100
 BuildRequires:  libsolv-tools
 %requires_eq    libsolv-tools
index 6daaaac..6aecacd 100644 (file)
@@ -1,4 +1,19 @@
 -------------------------------------------------------------------
+Thu Nov 28 18:20:04 CET 2019 - ma@suse.de
+
+- Introduce PurgeKernels class (bsc#1155198)
+  Adds libzypp API to mark all obsolete kernels according to the
+  existing purge-kernel script rules.
+- Add solver jobs for retracted packages and ptfs.
+  Support for ptf packages and retract ed patches.
+- Do not enforce 'en' being in RequestedLocales (bsc#1155678)
+  If the user decides to have a system without explicit language
+  support he may do so.
+- Pass correct posttrans script argument (fixes #190)
+- BuildRequires:  libsolv-devel >= 0.7.8.
+- version 17.17.0 (12)
+
+-------------------------------------------------------------------
 Tue Oct 29 14:54:51 CET 2019 - ma@suse.de
 
 - Expose new libsolv API via C++ counterparts
index 1a24bc0..eab7786 100644 (file)
@@ -78,322 +78,330 @@ ZYPP_DECLARE_FLAGS_AND_OPERATORS( TestSetupOptions, TestSetupOptionBits );
 */
 class TestSetup
 {
-  public:
-    typedef TestSetupOptions Options;
+public:
+  typedef TestSetupOptions Options;
 
-  public:
-    TestSetup( const Arch & sysarch_r = Arch_empty, const Options & options_r = Options() )
-    { _ctor( Pathname(), sysarch_r, options_r ); }
+public:
+  struct InitLaterType {};
+  static constexpr InitLaterType initLater = InitLaterType();
 
-    TestSetup( const Pathname & rootdir_r, const Arch & sysarch_r = Arch_empty, const Options & options_r = Options() )
-    { _ctor( rootdir_r, sysarch_r, options_r ); }
+  TestSetup( InitLaterType )
+  {}
 
-    TestSetup( const Pathname & rootdir_r, const Options & options_r )
-    { _ctor( rootdir_r, Arch_empty, options_r ); }
+  TestSetup( const Arch & sysarch_r = Arch_empty, const Options & options_r = Options() )
+    : _pimpl { new Impl( Pathname(), sysarch_r, options_r ) }
+  {}
 
-    ~TestSetup()
-    {
-      USR << (_tmprootdir.path() == _rootdir ? "DELETE" : "KEEP") << " TESTSETUP below " << _rootdir << endl;
-      ZConfig::instance().setRepoManagerRoot( Pathname() );
-    }
+  TestSetup( const Pathname & rootdir_r, const Arch & sysarch_r = Arch_empty, const Options & options_r = Options() )
+    : _pimpl { new Impl( rootdir_r, sysarch_r, options_r ) }
+  {}
 
-  public:
-    /** Whether directory \a path_r contains a solver testcase. */
-    static bool isTestcase( const Pathname & path_r )
-    {
-      return filesystem::PathInfo( path_r / "solver-test.xml" ).isFile();
-    }
+  TestSetup( const Pathname & rootdir_r, const Options & options_r )
+    : _pimpl { new Impl( rootdir_r, Arch_empty, options_r ) }
+  {}
 
-    /** Whether directory \a path_r contains a testsetup. */
-    static bool isTestSetup( const Pathname & path_r )
-    {
-      return filesystem::PathInfo( path_r / "repos.d" ).isDir() && filesystem::PathInfo( path_r / "raw" ).isDir();
-    }
+  void reset()
+  { _pimpl.reset(); }
 
-  public:
-    const Pathname & root() const { return _rootdir; }
-
-    Target &     target()      { if ( ! getZYpp()->getTarget() ) getZYpp()->initializeTarget( _rootdir ); return *getZYpp()->getTarget(); }
-    RepoManager  repomanager() { return RepoManager( RepoManagerOptions::makeTestSetup( _rootdir ) ); }
-    ResPool      pool()        { return ResPool::instance(); }
-    ResPoolProxy poolProxy()   { return pool().proxy(); }
-    sat::Pool    satpool()     { return sat::Pool::instance(); }
-    Resolver &   resolver()    { return *getZYpp()->resolver(); }
-
-  public:
-    /** Load target repo. */
-    void loadTarget()
-    { target().load(); }
-    /** Fake @System repo from url. */
-    void loadTargetRepo( const Url & url_r )
-    { loadRepo( url_r, sat::Pool::systemRepoAlias() ); }
-    /** Fake @System repo from Path. */
-    void loadTargetRepo( const Pathname & path_r )
-    { loadRepo( path_r, sat::Pool::systemRepoAlias() ); }
-    /** Fake @System repo from helix repo. */
-    void loadTargetHelix( const Pathname & path_r )
-    { loadHelix( path_r, sat::Pool::systemRepoAlias() ); }
-
-  public:
-    /** Directly load repoinfo to pool. */
-    void loadRepo( RepoInfo nrepo )
-    {
-      RepoManager rmanager( repomanager() );
-      if ( rmanager.hasRepo( nrepo ) )
-        nrepo.setAlias( RepoManager::makeStupidAlias( nrepo.url() ) );
-      rmanager.addRepository( nrepo );
-      rmanager.buildCache( nrepo );
-      rmanager.loadFromCache( nrepo );
-    }
-    /** Directly load repo from url to pool. */
-    void loadRepo( const Url & url_r, const std::string & alias_r = std::string() )
-    {
-      RepoInfo nrepo;
-      nrepo.setAlias( alias_r.empty() ? url_r.getHost()+":"+Pathname::basename(url_r.getPathName()) : alias_r );
-      nrepo.addBaseUrl( url_r );
-      if ( ! _options.testFlag( TSO_REPO_DEFAULT_GPG ) )
-       nrepo.setGpgCheck( false );
-      loadRepo( nrepo );
-    }
-    /** Directly load repo from metadata(dir) or solvfile(file) to pool.
+public:
+  /** Whether directory \a path_r contains a solver testcase. */
+  static bool isTestcase( const Pathname & path_r )
+  {
+    return filesystem::PathInfo( path_r / "solver-test.xml" ).isFile();
+  }
+
+  /** Whether directory \a path_r contains a testsetup. */
+  static bool isTestSetup( const Pathname & path_r )
+  {
+    return filesystem::PathInfo( path_r / "repos.d" ).isDir() && filesystem::PathInfo( path_r / "raw" ).isDir();
+  }
+
+public:
+  const Pathname & root() const { return _pimpl->_rootdir; }
+
+  Target &     target()      { if ( ! getZYpp()->getTarget() ) getZYpp()->initializeTarget( _pimpl->_rootdir ); return *getZYpp()->getTarget(); }
+  RepoManager  repomanager() { return RepoManager( RepoManagerOptions::makeTestSetup( _pimpl->_rootdir ) ); }
+  ResPool      pool()        { return ResPool::instance(); }
+  ResPoolProxy poolProxy()   { return pool().proxy(); }
+  sat::Pool    satpool()     { return sat::Pool::instance(); }
+  Resolver &   resolver()    { return *getZYpp()->resolver(); }
+
+public:
+  /** Load target repo. */
+  void loadTarget()
+  { target().load(); }
+  /** Fake @System repo from url. */
+  void loadTargetRepo( const Url & url_r )
+  { loadRepo( url_r, sat::Pool::systemRepoAlias() ); }
+  /** Fake @System repo from Path. */
+  void loadTargetRepo( const Pathname & path_r )
+  { loadRepo( path_r, sat::Pool::systemRepoAlias() ); }
+  /** Fake @System repo from helix repo. */
+  void loadTargetHelix( const Pathname & path_r )
+  { loadHelix( path_r, sat::Pool::systemRepoAlias() ); }
+
+public:
+  /** Directly load repoinfo to pool. */
+  void loadRepo( RepoInfo nrepo )
+  {
+    RepoManager rmanager( repomanager() );
+    if ( rmanager.hasRepo( nrepo ) )
+      nrepo.setAlias( RepoManager::makeStupidAlias( nrepo.url() ) );
+    rmanager.addRepository( nrepo );
+    rmanager.buildCache( nrepo );
+    rmanager.loadFromCache( nrepo );
+  }
+  /** Directly load repo from url to pool. */
+  void loadRepo( const Url & url_r, const std::string & alias_r = std::string() )
+  {
+    RepoInfo nrepo;
+    nrepo.setAlias( alias_r.empty() ? url_r.getHost()+":"+Pathname::basename(url_r.getPathName()) : alias_r );
+    nrepo.addBaseUrl( url_r );
+    if ( ! _pimpl->_options.testFlag( TSO_REPO_DEFAULT_GPG ) )
+      nrepo.setGpgCheck( false );
+    loadRepo( nrepo );
+  }
+  /** Directly load repo from metadata(dir) or solvfile(file) to pool.
      * An empty alias is guessed.
     */
-    void loadRepo( const Pathname & path_r, const std::string & alias_r = std::string() )
+  void loadRepo( const Pathname & path_r, const std::string & alias_r = std::string() )
+  {
+    if ( filesystem::PathInfo( path_r ).isDir() )
     {
-      if ( filesystem::PathInfo( path_r ).isDir() )
-      {
-        loadRepo( path_r.asUrl(), alias_r );
-        return;
-      }
-      // .solv file is loaded directly using a faked RepoInfo
-      RepoInfo nrepo;
-      nrepo.setAlias( alias_r.empty() ? path_r.basename() : alias_r );
-      satpool().addRepoSolv( path_r, nrepo );
+      loadRepo( path_r.asUrl(), alias_r );
+      return;
     }
-    /** Directly load repo from some location (url or absolute(!)path).
+    // .solv file is loaded directly using a faked RepoInfo
+    RepoInfo nrepo;
+    nrepo.setAlias( alias_r.empty() ? path_r.basename() : alias_r );
+    satpool().addRepoSolv( path_r, nrepo );
+  }
+  /** Directly load repo from some location (url or absolute(!)path).
      * An empty alias is guessed.
     */
-    void loadRepo( const std::string & loc_r, const std::string & alias_r = std::string() )
+  void loadRepo( const std::string & loc_r, const std::string & alias_r = std::string() )
+  {
+    if ( *loc_r.c_str() == '/' )
     {
-      if ( *loc_r.c_str() == '/' )
-      {
-        loadRepo( Pathname( loc_r ), alias_r );
-      }
-      else
-      {
-        loadRepo( Url( loc_r ), alias_r );
-      }
+      loadRepo( Pathname( loc_r ), alias_r );
+    }
+    else
+    {
+      loadRepo( Url( loc_r ), alias_r );
     }
-    /** Directly load repo from some location (url or absolute(!)path).
+  }
+  /** Directly load repo from some location (url or absolute(!)path).
      * An empty alias is guessed.
     */
-    void loadRepo( const char * loc_r, const std::string & alias_r = std::string() )
-    { loadRepo( std::string( loc_r ? loc_r : "" ), alias_r ); }
-
-  private:
-    // repo data from solver-test.xml
-    struct RepoD {
-      DefaultIntegral<unsigned,0> priority;
-      std::string alias;
-      Url url;
-    };
-
-  public:
-    /** Directly load a helix repo from some testcase.
+  void loadRepo( const char * loc_r, const std::string & alias_r = std::string() )
+  { loadRepo( std::string( loc_r ? loc_r : "" ), alias_r ); }
+
+private:
+  // repo data from solver-test.xml
+  struct RepoD {
+    DefaultIntegral<unsigned,0> priority;
+    std::string alias;
+    Url url;
+  };
+
+public:
+  /** Directly load a helix repo from some testcase.
      * An empty alias is guessed.
      */
-    void loadHelix( const Pathname & path_r, const std::string & alias_r = std::string() )
+  void loadHelix( const Pathname & path_r, const std::string & alias_r = std::string() )
+  {
+    // .solv file is loaded directly using a faked RepoInfo
+    RepoInfo nrepo;
+    nrepo.setAlias( alias_r.empty() ? path_r.basename() : alias_r );
+    satpool().addRepoHelix( path_r, nrepo );
+  }
+
+  // Load repos included in a solver testcase.
+  void loadTestcaseRepos( const Pathname & path_r )
+  {
+    filesystem::PathInfo pi( path_r / "solver-test.xml" );
+    if ( ! pi.isFile() )
     {
-      // .solv file is loaded directly using a faked RepoInfo
-      RepoInfo nrepo;
-      nrepo.setAlias( alias_r.empty() ? path_r.basename() : alias_r );
-      satpool().addRepoHelix( path_r, nrepo );
+      ERR << "No testcase in " << filesystem::PathInfo( path_r ) << endl;
+      return;
     }
-
-    // Load repos included in a solver testcase.
-    void loadTestcaseRepos( const Pathname & path_r )
+    // dumb parse
+    InputStream infile( pi.path() );
+    Arch sysarch( Arch_empty );
+    Url guessedUrl;
+    typedef std::map<std::string,RepoD> RepoI;
+    RepoI repoi;
+    for( iostr::EachLine in( infile ); in; in.next() )
     {
-      filesystem::PathInfo pi( path_r / "solver-test.xml" );
-      if ( ! pi.isFile() )
+      if ( str::hasPrefix( *in, "\t<channel" ) )
       {
-        ERR << "No testcase in " << filesystem::PathInfo( path_r ) << endl;
-        return;
+        RepoD & repod( repoi[getXmlNodeVal( *in, "file" )] );
+
+        repod.alias = getXmlNodeVal( *in, "name" );
+        repod.priority = str::strtonum<unsigned>( getXmlNodeVal( *in, "priority" ) );
+        repod.url = guessedUrl;
+        guessedUrl = Url();
       }
-      // dumb parse
-      InputStream infile( pi.path() );
-      Arch sysarch( Arch_empty );
-      Url guessedUrl;
-      typedef std::map<std::string,RepoD> RepoI;
-      RepoI repoi;
-      for( iostr::EachLine in( infile ); in; in.next() )
+      else if ( str::hasPrefix( *in, "\t- url " ) )
       {
-        if ( str::hasPrefix( *in, "\t<channel" ) )
-        {
-          RepoD & repod( repoi[getXmlNodeVal( *in, "file" )] );
-
-          repod.alias = getXmlNodeVal( *in, "name" );
-          repod.priority = str::strtonum<unsigned>( getXmlNodeVal( *in, "priority" ) );
-          repod.url = guessedUrl;
-          guessedUrl = Url();
-        }
-        else if ( str::hasPrefix( *in, "\t- url " ) )
-        {
-          std::string::size_type pos = in->find( ": " );
-          if ( pos != std::string::npos )
-          {
-            guessedUrl = Url( in->substr( pos+2 ) );
-          }
-        }
-        else if ( str::hasPrefix( *in, "\t<locale" ) )
-        {
-          satpool().addRequestedLocale( Locale( getXmlNodeVal( *in, "name" ) ) );
-        }
-       else if ( sysarch.empty() && str::hasPrefix( *in, "<setup" ) )
+        std::string::size_type pos = in->find( ": " );
+        if ( pos != std::string::npos )
         {
-          sysarch = Arch( getXmlNodeVal( *in, "arch" ) );
-          if ( ! sysarch.empty() )
-            ZConfig::instance().setSystemArchitecture( sysarch );
+          guessedUrl = Url( in->substr( pos+2 ) );
         }
       }
+      else if ( str::hasPrefix( *in, "\t<locale" ) )
+      {
+        satpool().addRequestedLocale( Locale( getXmlNodeVal( *in, "name" ) ) );
+      }
+      else if ( sysarch.empty() && str::hasPrefix( *in, "<setup" ) )
+      {
+        sysarch = Arch( getXmlNodeVal( *in, "arch" ) );
+        if ( ! sysarch.empty() )
+          ZConfig::instance().setSystemArchitecture( sysarch );
+      }
+    }
 
-      //
-      filesystem::Glob files( path_r/"*{.xml,.xml.gz}", filesystem::Glob::kBrace );
-      for_( it, files.begin(), files.end() )
+    //
+    filesystem::Glob files( path_r/"*{.xml,.xml.gz}", filesystem::Glob::kBrace );
+    for_( it, files.begin(), files.end() )
+    {
+      std::string basename( Pathname::basename( *it ) );
+      if ( str::hasPrefix( basename, "solver-test.xml" ) )
+        continue; // master index currently unevaluated
+      if ( str::hasPrefix( basename, "solver-system.xml" ) )
+        loadTargetHelix( *it );
+      else
       {
-        std::string basename( Pathname::basename( *it ) );
-        if ( str::hasPrefix( basename, "solver-test.xml" ) )
-          continue; // master index currently unevaluated
-        if ( str::hasPrefix( basename, "solver-system.xml" ) )
-          loadTargetHelix( *it );
-        else
-        {
-          const RepoD & repod( repoi[basename] );
+        const RepoD & repod( repoi[basename] );
 
-          RepoInfo nrepo;
-          nrepo.setAlias( repod.alias.empty() ? basename : repod.alias );
-          nrepo.setPriority( repod.priority );
-          nrepo.setBaseUrl( repod.url );
-          satpool().addRepoHelix( *it, nrepo );
-        }
+        RepoInfo nrepo;
+        nrepo.setAlias( repod.alias.empty() ? basename : repod.alias );
+        nrepo.setPriority( repod.priority );
+        nrepo.setBaseUrl( repod.url );
+        satpool().addRepoHelix( *it, nrepo );
       }
-
-      poolProxy(); // prepare
     }
 
-  public:
-    /** Load all enabled repos in repos.d to pool. */
-    void loadRepos()
+    poolProxy(); // prepare
+  }
+
+public:
+  /** Load all enabled repos in repos.d to pool. */
+  void loadRepos()
+  {
+    RepoManager repoManager( repomanager() );
+    RepoInfoList repos = repoManager.knownRepositories();
+    for ( RepoInfoList::iterator it = repos.begin(); it != repos.end(); ++it )
     {
-      RepoManager repoManager( repomanager() );
-      RepoInfoList repos = repoManager.knownRepositories();
-      for ( RepoInfoList::iterator it = repos.begin(); it != repos.end(); ++it )
-      {
-        RepoInfo & nrepo( *it );
-        USR << nrepo << endl;
+      RepoInfo & nrepo( *it );
+      USR << nrepo << endl;
 
-        if ( ! nrepo.enabled() )
-          continue;
+      if ( ! nrepo.enabled() )
+        continue;
 
-        if ( ! repoManager.isCached( nrepo ) || nrepo.type() == repo::RepoType::RPMPLAINDIR )
+      if ( ! repoManager.isCached( nrepo ) || nrepo.type() == repo::RepoType::RPMPLAINDIR )
+      {
+        if ( repoManager.isCached( nrepo ) )
         {
-          if ( repoManager.isCached( nrepo ) )
-          {
-            USR << "cleanCache" << endl;
-            repoManager.cleanCache( nrepo );
-          }
-          //USR << "refreshMetadata" << endl;
-          //repoManager.refreshMetadata( nrepo );
-          USR << "buildCache" << endl;
-          repoManager.buildCache( nrepo );
+          USR << "cleanCache" << endl;
+          repoManager.cleanCache( nrepo );
         }
-        USR << "Create from cache" << endl;
-        repoManager.loadFromCache( nrepo );
+        //USR << "refreshMetadata" << endl;
+        //repoManager.refreshMetadata( nrepo );
+        USR << "buildCache" << endl;
+        repoManager.buildCache( nrepo );
       }
+      USR << "Create from cache" << endl;
+      repoManager.loadFromCache( nrepo );
     }
+  }
 
-  public:
-    /** Detect and load the system located at \a sysRoot.
+public:
+  /** Detect and load the system located at \a sysRoot.
      *
      * \a sysRoot needs to be a directory containing either a SolverTestcase,
      * a TestSetup system or a real system. The  provided repostitories are
      * loaded into the pool (without refresh).
     */
-    static void LoadSystemAt( const Pathname & sysRoot, const Arch & _testSetupArch_r = Arch_x86_64 )
+  static void LoadSystemAt( const Pathname & sysRoot, const Arch & _testSetupArch_r = Arch_x86_64 )
+  {
+    if ( ! PathInfo( sysRoot ).isDir() )
+      ZYPP_THROW( Exception("sysRoot argument needs to be a directory") );
+
+    if ( TestSetup::isTestcase( sysRoot ) )
+    {
+      USR << str::form( "*** Load Testcase from '%s'", sysRoot.c_str() ) << endl;
+      TestSetup test;
+      test.loadTestcaseRepos( sysRoot );
+    }
+    else if ( TestSetup::isTestSetup( sysRoot ) )
     {
-      if ( ! PathInfo( sysRoot ).isDir() )
-        ZYPP_THROW( Exception("sysRoot argument needs to be a directory") );
+      USR << str::form( "*** Load TestSetup from '%s'", sysRoot.c_str() ) << endl;
+
+      TestSetup test( sysRoot, _testSetupArch_r );
+      test.loadRepos();
 
-      if ( TestSetup::isTestcase( sysRoot ) )
+      Pathname solvCachePath( RepoManagerOptions::makeTestSetup( test.root() ).repoSolvCachePath );
+      Pathname fakeTargetSolv( solvCachePath / sat::Pool::systemRepoAlias() / "solv" );
+      if ( PathInfo( fakeTargetSolv ).isFile() )
       {
-        USR << str::form( "*** Load Testcase from '%s'", sysRoot.c_str() ) << endl;
-        TestSetup test;
-        test.loadTestcaseRepos( sysRoot );
+        USR << str::form( "*** Fake TestSetup Target from '%s'", fakeTargetSolv.c_str() ) << endl;
+        test.target();
+        test.loadTargetRepo( fakeTargetSolv );
       }
-      else if ( TestSetup::isTestSetup( sysRoot ) )
+    }
+    else
+    {
+      sat::Pool satpool( sat::Pool::instance() );
+      // a system
+      USR << str::form( "*** Load system at '%s'", sysRoot.c_str() ) << endl;
+      if ( 1 )
       {
-        USR << str::form( "*** Load TestSetup from '%s'", sysRoot.c_str() ) << endl;
-
-        TestSetup test( sysRoot, _testSetupArch_r );
-        test.loadRepos();
-
-        Pathname solvCachePath( RepoManagerOptions::makeTestSetup( test.root() ).repoSolvCachePath );
-        Pathname fakeTargetSolv( solvCachePath / sat::Pool::systemRepoAlias() / "solv" );
-        if ( PathInfo( fakeTargetSolv ).isFile() )
-        {
-          USR << str::form( "*** Fake TestSetup Target from '%s'", fakeTargetSolv.c_str() ) << endl;
-          test.target();
-          test.loadTargetRepo( fakeTargetSolv );
-        }
+        USR << "*** load target '" << Repository::systemRepoAlias() << "'\t" << endl;
+        getZYpp()->initializeTarget( sysRoot );
+        getZYpp()->target()->load();
+        USR << satpool.systemRepo() << endl;
       }
-      else
+
+      if ( 1 )
       {
-        sat::Pool satpool( sat::Pool::instance() );
-        // a system
-        USR << str::form( "*** Load system at '%s'", sysRoot.c_str() ) << endl;
-        if ( 1 )
+        RepoManager repoManager( sysRoot );
+        RepoInfoList repos = repoManager.knownRepositories();
+        for_( it, repos.begin(), repos.end() )
         {
-          USR << "*** load target '" << Repository::systemRepoAlias() << "'\t" << endl;
-          getZYpp()->initializeTarget( sysRoot );
-          getZYpp()->target()->load();
-          USR << satpool.systemRepo() << endl;
-        }
+          RepoInfo & nrepo( *it );
 
-        if ( 1 )
-        {
-          RepoManager repoManager( sysRoot );
-          RepoInfoList repos = repoManager.knownRepositories();
-          for_( it, repos.begin(), repos.end() )
+          if ( ! nrepo.enabled() )
+            continue;
+
+          if ( ! repoManager.isCached( nrepo ) )
+          {
+            USR << str::form( "*** omit uncached repo '%s' (do 'zypper refresh')", nrepo.name().c_str() ) << endl;
+            continue;
+          }
+
+          USR << str::form( "*** load repo '%s'\t", nrepo.name().c_str() ) << flush;
+          try
+          {
+            repoManager.loadFromCache( nrepo );
+            USR << satpool.reposFind( nrepo.alias() ) << endl;
+          }
+          catch ( const Exception & exp )
           {
-            RepoInfo & nrepo( *it );
-
-            if ( ! nrepo.enabled() )
-              continue;
-
-            if ( ! repoManager.isCached( nrepo ) )
-            {
-              USR << str::form( "*** omit uncached repo '%s' (do 'zypper refresh')", nrepo.name().c_str() ) << endl;
-              continue;
-            }
-
-            USR << str::form( "*** load repo '%s'\t", nrepo.name().c_str() ) << flush;
-            try
-            {
-              repoManager.loadFromCache( nrepo );
-              USR << satpool.reposFind( nrepo.alias() ) << endl;
-            }
-            catch ( const Exception & exp )
-            {
-              USR << exp.asString() + "\n" + exp.historyAsString() << endl;
-              USR << str::form( "*** omit broken repo '%s' (do 'zypper refresh')", nrepo.name().c_str() ) << endl;
-              continue;
-            }
+            USR << exp.asString() + "\n" + exp.historyAsString() << endl;
+            USR << str::form( "*** omit broken repo '%s' (do 'zypper refresh')", nrepo.name().c_str() ) << endl;
+            continue;
           }
         }
       }
     }
+  }
 
-  private:
-    void _ctor( const Pathname & rootdir_r, const Arch & sysarch_r, const Options & options_r )
+private:
+  struct Impl
+  {
+    Impl( const Pathname & rootdir_r, const Arch & sysarch_r, const Options & options_r )
     {
       _options = options_r;
 
@@ -412,10 +420,21 @@ class TestSetup
         ZConfig::instance().setSystemArchitecture( sysarch_r );
       USR << "CREATED TESTSETUP below " << _rootdir << endl;
     }
-  private:
+
+    ~Impl()
+    {
+      USR << (_tmprootdir.path() == _rootdir ? "DELETE" : "KEEP") << " TESTSETUP below " << _rootdir << endl;
+      ZConfig::instance().setRepoManagerRoot( Pathname() );
+      getZYpp()->finishTarget();
+      sat::Pool::instance().reposEraseAll();
+    }
+
     filesystem::TmpDir _tmprootdir;
     Pathname           _rootdir;
     Options            _options;
+  };
+
+  std::unique_ptr<Impl> _pimpl;        // maybe worth creating RW_pointer traits for it
 };
 
 
index e2b7246..568b1d2 100644 (file)
@@ -1,7 +1,14 @@
 #include "TestSetup.h"
 #include <zypp/parser/ProductFileReader.h>
 
-//static TestSetup test( Arch_x86_64 );
+//TestSetup test( TestSetup::initLater );
+//struct TestInit {
+//  TestInit() {
+//    test = TestSetup( Arch_x86_64 );
+//  }
+//  ~TestInit() { test.reset(); }
+//};
+//BOOST_GLOBAL_FIXTURE( TestInit );
 
 // Must be the first test!
 BOOST_AUTO_TEST_CASE(basic)
index 20c724e..72fd91c 100644 (file)
@@ -11,7 +11,15 @@ using namespace zypp;
 using std::cout;
 using std::endl;
 
-TestSetup test( Arch_x86_64 );
+static TestSetup test( TestSetup::initLater );
+struct TestInit {
+  TestInit() {
+    test = TestSetup( Arch_x86_64 );
+  }
+  ~TestInit() { test.reset(); }
+};
+BOOST_GLOBAL_FIXTURE( TestInit );
+
 const Pathname DATADIR( TESTS_SRC_DIR "/repo/RepoLicense" );
 
 ///////////////////////////////////////////////////////////////////
index 8231ede..62a479d 100644 (file)
@@ -16,7 +16,14 @@ using std::endl;
 #define COUT if ( TC_VERBOSE ) std::cout
 #define TAG COUT << "*** " << __PRETTY_FUNCTION__ << endl
 
-TestSetup test( Arch_x86_64, TSO_REPO_DEFAULT_GPG );
+static TestSetup test( TestSetup::initLater );
+struct TestInit {
+  TestInit() {
+    test = TestSetup( Arch_x86_64, TSO_REPO_DEFAULT_GPG );
+  }
+  ~TestInit() { test.reset(); }
+};
+BOOST_GLOBAL_FIXTURE( TestInit );
 const Pathname DATADIR( TESTS_SRC_DIR "/repo/RepoSigcheck" );
 
 ///////////////////////////////////////////////////////////////////
index 936307a..35ad17b 100644 (file)
@@ -3,7 +3,14 @@
 #include <zypp/base/StrMatcher.h>
 #include <zypp/ResObjects.h>
 
-static TestSetup test( Arch_x86_64 );
+static TestSetup test( TestSetup::initLater );
+struct TestInit {
+  TestInit() {
+    test = TestSetup( Arch_x86_64 );
+  }
+  ~TestInit() { test.reset(); }
+};
+BOOST_GLOBAL_FIXTURE( TestInit );
 
 // Must be the first test!
 BOOST_AUTO_TEST_CASE(bnc_435838)
index 119c849..018a236 100644 (file)
@@ -2,7 +2,14 @@
 #include <zypp/Repository.h>
 #include <zypp/sat/Pool.h>
 
-static TestSetup test( Arch_x86_64 );
+static TestSetup test( TestSetup::initLater );
+struct TestInit {
+  TestInit() {
+    test = TestSetup( Arch_x86_64 );
+  }
+  ~TestInit() { test.reset(); }
+};
+BOOST_GLOBAL_FIXTURE( TestInit );
 
 namespace zypp { namespace detail {
   /** \relates RepositoryIterator Stream output */
index f4685ed..ad3e41d 100644 (file)
@@ -18,13 +18,16 @@ using namespace zypp;
 using namespace boost::unit_test;
 
 
-BOOST_AUTO_TEST_CASE(test_init)
-{
-  TestSetup test( Arch_x86_64 );
-  test.loadRepo( TESTS_SRC_DIR "/data/openSUSE-11.1", "opensuse" );
-  test.loadRepo( TESTS_SRC_DIR "/data/11.0-update", "update" );
-}
+struct TestInit {
+  TestInit()
+    : _test( Arch_x86_64 ){
+    _test.loadRepo( TESTS_SRC_DIR "/data/openSUSE-11.1", "opensuse" );
+    _test.loadRepo( TESTS_SRC_DIR "/data/11.0-update", "update" );
+  }
 
+  TestSetup _test;
+};
+BOOST_GLOBAL_FIXTURE( TestInit );
 
 BOOST_AUTO_TEST_CASE(attributes)
 {
index 093f8f3..a0201d4 100644 (file)
@@ -34,6 +34,7 @@ ADD_TESTS(
   ProgressData
   PtrTypes
   PublicKey
+  PurgeKernels
   RWPtr
   RepoInfo
   RepoManager
index 0f64f4d..3a72142 100644 (file)
@@ -19,7 +19,14 @@ using boost::unit_test::test_case;
 using namespace std;
 using namespace zypp;
 
-static TestSetup test( Arch_x86_64 );
+static TestSetup test( TestSetup::initLater );
+struct TestInit {
+  TestInit() {
+    test = TestSetup( Arch_x86_64 );
+  }
+  ~TestInit() { test.reset(); }
+};
+BOOST_GLOBAL_FIXTURE( TestInit );
 
 BOOST_AUTO_TEST_CASE(capabilities_test)
 {
index d063bcd..079e6f4 100644 (file)
@@ -8,7 +8,16 @@
 
 /////////////////////////////////////////////////////////////////////////////
 
-static TestSetup test;
+static TestSetup test( TestSetup::initLater );
+struct TestInit {
+  TestInit() {
+    test = TestSetup( Arch_x86_64 );
+    test.loadTestcaseRepos( TESTS_SRC_DIR"/data/TCdup" );
+    dumpRange( USR, test.pool().knownRepositoriesBegin(),
+      test.pool().knownRepositoriesEnd() ) << endl;
+  }
+  ~TestInit() { test.reset(); }
+};
 
 template <class TIterator>
 std::ostream & vdumpPoolStats( std::ostream & str, TIterator begin_r, TIterator end_r )
@@ -22,7 +31,7 @@ std::ostream & vdumpPoolStats( std::ostream & str, TIterator begin_r, TIterator
   return str << stats;
 }
 
-bool upgrade()
+bool upgrade( )
 {
   bool rres = false;
   {
@@ -42,22 +51,17 @@ bool upgrade()
 }
 
 
-BOOST_AUTO_TEST_CASE(testcase_init)
+BOOST_GLOBAL_FIXTURE( TestInit );
+
+BOOST_AUTO_TEST_CASE( orphaned )
 {
-  //zypp::base::LogControl::instance().logToStdErr();
-  test.loadTestcaseRepos( TESTS_SRC_DIR"/data/TCdup" );
-  dumpRange( USR, test.pool().knownRepositoriesBegin(),
-                  test.pool().knownRepositoriesEnd() ) << endl;
   USR << "pool: " << test.pool() << endl;
-  BOOST_REQUIRE( upgrade() );
-}
-/////////////////////////////////////////////////////////////////////////////
+  BOOST_REQUIRE( upgrade( ) );
 
-BOOST_AUTO_TEST_CASE(orphaned)
-{
   ResPoolProxy proxy( test.poolProxy() );
   BOOST_CHECK_EQUAL( proxy.lookup( ResKind::package, "glibc" )->status(),              ui::S_KeepInstalled );
   BOOST_CHECK_EQUAL( proxy.lookup( ResKind::package, "release-package" )->status(),    ui::S_AutoUpdate );
   BOOST_CHECK_EQUAL( proxy.lookup( ResKind::package, "dropped_required" )->status(),   ui::S_KeepInstalled );
   BOOST_CHECK_EQUAL( proxy.lookup( ResKind::package, "dropped" )->status(),            ui::S_AutoDel );
 }
+
index 3324cb5..c0d8317 100644 (file)
@@ -15,7 +15,14 @@ using std::cerr;
 using std::endl;
 using namespace zypp;
 
-static TestSetup test;
+static TestSetup test( TestSetup::initLater );
+struct TestInit {
+  TestInit() {
+    test = TestSetup( Arch_x86_64 );
+  }
+  ~TestInit() { test.reset(); }
+};
+BOOST_GLOBAL_FIXTURE( TestInit );
 
 void testcase_init()
 {
@@ -79,7 +86,16 @@ void repocheck()
 // Resolvable is still the original one created for a package...
 ///////////////////////////////////////////////////////////////////
 
-BOOST_AUTO_TEST_CASE(t_1)      { testcase_init(); }
-BOOST_AUTO_TEST_CASE(t_2)      { repocheck(); }
-BOOST_AUTO_TEST_CASE(t_4)      { testcase_init2(); }
-BOOST_AUTO_TEST_CASE(t_5)      { repocheck(); }
+BOOST_AUTO_TEST_CASE(t_1) {
+
+  //will print additional context information on error
+  BOOST_TEST_CONTEXT("First phase") {
+    testcase_init();
+    repocheck();
+  }
+
+  BOOST_TEST_CONTEXT("Second phase") {
+    testcase_init2();
+    repocheck();
+  }
+}
index 248056b..90896a6 100644 (file)
@@ -4,16 +4,19 @@
 #define BOOST_TEST_MODULE InstanceId
 
 /////////////////////////////////////////////////////////////////////////////
-static TestSetup test( Arch_x86_64 );
-
-BOOST_AUTO_TEST_CASE(pool_query_init)
-{
-  // Abuse;) vbox as System repo:
-  test.loadTargetRepo( TESTS_SRC_DIR "/data/obs_virtualbox_11_1" );
-  test.loadRepo( TESTS_SRC_DIR "/data/openSUSE-11.1", "opensuse" );
-  test.loadRepo( TESTS_SRC_DIR "/data/OBS_zypp_svn-11.1", "zyppsvn" );
-}
-
+static TestSetup test( TestSetup::initLater );
+struct TestInit {
+  TestInit() {
+    test = TestSetup( Arch_x86_64 );
+
+    // Abuse;) vbox as System repo:
+    test.loadTargetRepo( TESTS_SRC_DIR "/data/obs_virtualbox_11_1" );
+    test.loadRepo( TESTS_SRC_DIR "/data/openSUSE-11.1", "opensuse" );
+    test.loadRepo( TESTS_SRC_DIR "/data/OBS_zypp_svn-11.1", "zyppsvn" );
+  }
+  ~TestInit() { test.reset(); }
+};
+BOOST_GLOBAL_FIXTURE( TestInit );
 /////////////////////////////////////////////////////////////////////////////
 
 BOOST_AUTO_TEST_CASE(default_constructed)
index aac487b..dc2b013 100644 (file)
@@ -26,14 +26,17 @@ bool isLocked( const sat::Solvable & solvable )
   return false;
 }
 
-BOOST_AUTO_TEST_CASE(pool_query_init)
-{
-  TestSetup test( Arch_x86_64 );
-  //test.loadTarget(); // initialize and load target
-  test.loadRepo( TESTS_SRC_DIR "/data/openSUSE-11.1", "opensuse" );
-  test.loadRepo( TESTS_SRC_DIR "/data/OBS_zypp_svn-11.1", "@System" );
-}
-
+static TestSetup test( TestSetup::initLater );
+struct TestInit {
+  TestInit() {
+    test = TestSetup( Arch_x86_64 );
+    //test.loadTarget(); // initialize and load target
+    test.loadRepo( TESTS_SRC_DIR "/data/openSUSE-11.1", "opensuse" );
+    test.loadRepo( TESTS_SRC_DIR "/data/OBS_zypp_svn-11.1", "@System" );
+  }
+  ~TestInit() { test.reset(); }
+};
+BOOST_GLOBAL_FIXTURE( TestInit );
 
 /////////////////////////////////////////////////////////////////////////////
 //  0xx basic queries
index 6c50db4..afccf25 100644 (file)
@@ -14,9 +14,6 @@ using std::cerr;
 using std::endl;
 using namespace zypp;
 
-static TestSetup test;
-
-/////////////////////////////////////////////////////////////////////////////
 template <class TCont>
 std::ostream & nlist( std::ostream & str, const TCont & set_r )
 {
@@ -26,11 +23,19 @@ std::ostream & nlist( std::ostream & str, const TCont & set_r )
   return str << endl;
 }
 
-BOOST_AUTO_TEST_CASE(init)
-{
-  test.loadTargetHelix( TESTS_SRC_DIR "/zypp/data/PoolQueryCC/rxnames.xml" );
-  nlist( cout << "repo ", ResPool::instance() );
-}
+/////////////////////////////////////////////////////////////////////////////
+
+static TestSetup test( TestSetup::initLater );
+struct TestInit {
+  TestInit() {
+    test = TestSetup( Arch_x86_64 );
+
+    test.loadTargetHelix( TESTS_SRC_DIR "/zypp/data/PoolQueryCC/rxnames.xml" );
+    nlist( cout << "repo ", ResPool::instance() );
+  }
+  ~TestInit() { test.reset(); }
+};
+BOOST_GLOBAL_FIXTURE( TestInit );
 
 /////////////////////////////////////////////////////////////////////////////
 // Basic issue: Multiple match strings are compiled into a singe regex. The
index b814e8d..7344e12 100644 (file)
@@ -5,19 +5,23 @@
 #define BOOST_TEST_MODULE PoolQuery
 
 /////////////////////////////////////////////////////////////////////////////
-static TestSetup test( Arch_x86_64 );
+static TestSetup test( TestSetup::initLater );
+struct TestInit {
+  TestInit() {
+    test = TestSetup( Arch_x86_64 );
 
-BOOST_AUTO_TEST_CASE(pool_query_init)
-{
-  // Abuse;) vbox as System repo:
-  test.loadTargetRepo( TESTS_SRC_DIR "/data/obs_virtualbox_11_1" );
-  test.loadRepo( TESTS_SRC_DIR "/data/openSUSE-11.1", "opensuse" );
-  test.loadRepo( TESTS_SRC_DIR "/data/OBS_zypp_svn-11.1", "zyppsvn" );
+    // Abuse;) vbox as System repo:
+    test.loadTargetRepo( TESTS_SRC_DIR "/data/obs_virtualbox_11_1" );
+    test.loadRepo( TESTS_SRC_DIR "/data/openSUSE-11.1", "opensuse" );
+    test.loadRepo( TESTS_SRC_DIR "/data/OBS_zypp_svn-11.1", "zyppsvn" );
 
-  dumpRange( USR, test.pool().knownRepositoriesBegin(),
-                  test.pool().knownRepositoriesEnd() );
-  USR << "pool: " << test.pool() << endl;
-}
+    dumpRange( USR, test.pool().knownRepositoriesBegin(),
+      test.pool().knownRepositoriesEnd() );
+    USR << "pool: " << test.pool() << endl;
+  }
+  ~TestInit() { test.reset(); }
+};
+BOOST_GLOBAL_FIXTURE( TestInit );
 /////////////////////////////////////////////////////////////////////////////
 
 static std::ofstream devNull;
diff --git a/tests/zypp/PurgeKernels_test.cc b/tests/zypp/PurgeKernels_test.cc
new file mode 100644 (file)
index 0000000..f13f1ae
--- /dev/null
@@ -0,0 +1,234 @@
+#include "TestSetup.h"
+#include "zypp/PurgeKernels.h"
+
+#include <boost/test/data/test_case.hpp>
+
+using namespace zypp;
+using namespace boost::unit_test;
+
+namespace boost { namespace test_tools { namespace tt_detail {
+template<>
+struct print_log_value< std::map<std::string, bool> > {
+void operator()( std::ostream& ostr,
+    std::map<std::string, bool> const& set)
+{
+  ostr << "{" << std::endl;
+  for( const auto &elem : set ) ostr << "'" << elem.first << "'," << std::endl;
+  ostr << "}" << std::endl;
+}
+};
+}}}
+
+namespace  {
+  std::string makeNVRA( const PoolItem &pck ) {
+    return pck.name() + "-" + pck.edition().asString() + "." + pck.arch().asString();
+  }
+
+  using TestSample = std::tuple<Pathname, std::string, zypp::Arch, std::string, std::map<std::string, bool> >;
+
+  std::vector<TestSample>  maketestdata() {
+    return {
+      TestSample {
+        TESTS_SRC_DIR"/zypp/data/PurgeKernels/simple",
+        "1-3-default",
+        Arch("x86_64"),
+        "oldest,running,latest",
+        {
+          { "kernel-default-1-2.x86_64", false },
+          { "kernel-default-devel-1-2.x86_64", false },
+          { "kernel-default-devel-debuginfo-1-2.x86_64", false },
+          { "kernel-devel-1-2.noarch", false },
+          { "kernel-livepatch-default-1-2.x86_64", false },
+          { "kernel-syms-1-2.x86_64", false },
+          { "kernel-default-1-4.x86_64", false },
+          { "kernel-default-devel-1-4.x86_64", false },
+          { "kernel-default-devel-debuginfo-1-4.x86_64", false },
+          { "kernel-devel-1-4.noarch", false },
+          { "kernel-syms-1-4.x86_64", false },
+        }
+      },
+      //test that keeps only the running kernel
+      TestSample {
+        TESTS_SRC_DIR"/zypp/data/PurgeKernels/simple",
+        "1-3-default",
+        Arch("x86_64"),
+        "running",
+        {
+          { "kernel-default-1-1.x86_64", false },
+          { "kernel-default-devel-1-1.x86_64", false },
+          { "kernel-default-devel-debuginfo-1-1.x86_64", false },
+          { "kernel-livepatch-default-1-1.x86_64", false },
+          { "kernel-devel-1-1.noarch", false },
+          { "kernel-syms-1-1.x86_64", false },
+          { "kernel-source-1-1.noarch", false },
+          { "kernel-default-1-2.x86_64", false },
+          { "kernel-default-devel-1-2.x86_64", false },
+          { "kernel-default-devel-debuginfo-1-2.x86_64", false },
+          { "kernel-devel-1-2.noarch", false },
+          { "kernel-livepatch-default-1-2.x86_64", false },
+          { "kernel-syms-1-2.x86_64", false },
+          { "kernel-default-1-4.x86_64", false },
+          { "kernel-default-devel-1-4.x86_64", false },
+          { "kernel-default-devel-debuginfo-1-4.x86_64", false },
+          { "kernel-devel-1-4.noarch", false },
+          { "kernel-syms-1-4.x86_64", false },
+          { "kernel-default-1-5.x86_64", false },
+          { "kernel-default-devel-1-5.x86_64", false },
+          { "kernel-default-devel-debuginfo-1-5.x86_64", false },
+          { "kernel-devel-1-5.noarch", false },
+          { "kernel-syms-1-5.x86_64", false },
+          { "dummy-kmp-default-1-0.x86_64", false },
+        }
+      },
+      TestSample {
+        TESTS_SRC_DIR"/zypp/data/PurgeKernels/simple",
+        "1-3-default",
+        Arch("x86_64"),
+        "oldest+1,running,latest-1",
+        {
+          { "kernel-default-1-1.x86_64", false },
+          { "kernel-livepatch-default-1-1.x86_64", false },
+          { "kernel-default-devel-1-1.x86_64", false },
+          { "kernel-default-devel-debuginfo-1-1.x86_64", false },
+          { "kernel-devel-1-1.noarch", false },
+          { "kernel-syms-1-1.x86_64", false },
+          { "kernel-source-1-1.noarch", false },
+          { "kernel-default-1-5.x86_64", false },
+          { "kernel-default-devel-1-5.x86_64", false },
+          { "kernel-default-devel-debuginfo-1-5.x86_64", false },
+          { "kernel-devel-1-5.noarch", false },
+          { "kernel-syms-1-5.x86_64", false },
+          { "dummy-kmp-default-1-0.x86_64", false },
+        }
+      },
+      TestSample {
+        //kernel-1-1 has a non kernel package depending on it, it should not be removed
+        TESTS_SRC_DIR"/zypp/data/PurgeKernels/withdeps",
+        "1-5-default",
+        Arch("x86_64"),
+        "running",
+        {
+          { "kernel-default-1-2.x86_64", false },
+          { "kernel-default-devel-1-2.x86_64", false },
+          { "kernel-default-devel-debuginfo-1-2.x86_64", false },
+          { "kernel-devel-1-2.noarch", false },
+          { "kernel-livepatch-default-1-2.x86_64", false },
+          { "kernel-syms-1-2.x86_64", false },
+        }
+      },
+      TestSample {
+        //kernel-1-5 provides a symbol for a kmp that has a non kernel package depending on it, it should not be removed
+        TESTS_SRC_DIR"/zypp/data/PurgeKernels/withdeps",
+        "1-1-default",
+        Arch("x86_64"),
+        "running",
+        {
+          { "kernel-default-1-2.x86_64", false },
+          { "kernel-default-devel-1-2.x86_64", false },
+          { "kernel-default-devel-debuginfo-1-2.x86_64", false },
+          { "kernel-devel-1-2.noarch", false },
+          { "kernel-livepatch-default-1-2.x86_64", false },
+          { "kernel-syms-1-2.x86_64", false },
+        }
+      },
+      TestSample {
+        //kernel-1-2 is explicitely in the keep spec, it should not be removed
+        //kernel-1-5 provides a symbol for a kmp that has a non kernel package depending on it, it should not be removed
+        TESTS_SRC_DIR"/zypp/data/PurgeKernels/withdeps",
+        "1-1-default",
+        Arch("x86_64"),
+        "running,1-2",
+        {
+        }
+      },
+      TestSample {
+        //kernel-default-1-1.x86_64 is the running kernel it should not be removed,
+        //in all sets with different arch only the latest should be kept
+        TESTS_SRC_DIR"/zypp/data/PurgeKernels/arch",
+        "1-1-default",
+        Arch("x86_64"),
+        "latest,running",
+        {
+          { "kernel-default-1-1.aarch64", false },
+          { "kernel-default-1-1.i686", false },
+
+          /*
+          { "kernel-default-devel-1-1.x86_64", false },
+          { "kernel-default-devel-debuginfo-1-1.x86_64", false },
+          { "kernel-syms-1-1.x86_64", false },
+          */
+
+          { "kernel-default-1-2.aarch64", false },
+          { "kernel-default-1-2.i686", false },
+          { "kernel-default-1-2.x86_64", false },
+
+          { "kernel-default-devel-1-1.aarch64", false },
+          { "kernel-default-devel-1-1.i686", false },
+          { "kernel-default-devel-1-2.aarch64", false },
+          { "kernel-default-devel-1-2.i686", false },
+          { "kernel-default-devel-1-2.x86_64", false },
+
+          { "kernel-default-devel-debuginfo-1-1.aarch64", false },
+          { "kernel-default-devel-debuginfo-1-1.i686", false },
+          { "kernel-default-devel-debuginfo-1-2.aarch64", false },
+          { "kernel-default-devel-debuginfo-1-2.i686", false },
+          { "kernel-default-devel-debuginfo-1-2.x86_64", false },
+
+          { "kernel-devel-1-2.noarch", false },
+
+          { "kernel-livepatch-default-1-2.aarch64", false },
+          { "kernel-livepatch-default-1-2.i686", false },
+          { "kernel-livepatch-default-1-2.x86_64", false },
+
+          /*
+           Since kernel-syms package requires do not care about architecture, every kernel-syms
+           package for flavour-version will be kept as long as a kernel-flavour-devel package of any arch is installed
+          { "kernel-syms-1-1.aarch64", false },
+          { "kernel-syms-1-1.i686", false },
+          */
+          { "kernel-syms-1-2.aarch64", false },
+          { "kernel-syms-1-2.i686", false },
+          { "kernel-syms-1-2.x86_64", false },
+        }
+      },
+    };
+  }
+}
+
+namespace bdata = boost::unit_test::data;
+
+BOOST_DATA_TEST_CASE(purge_kernels, bdata::make( maketestdata() ), repoPath, uname_r, arch, keepSpec, expectedRems )
+{
+  TestSetup test( Arch_x86_64 );
+  test.loadTestcaseRepos( repoPath );
+
+  auto expectedRemovals = expectedRems;
+
+  PurgeKernels krnls;
+  krnls.setUnameR( uname_r );
+  krnls.setKernelArch( arch );
+  krnls.setKeepSpec( keepSpec );
+  krnls.markObsoleteKernels();
+
+  auto pool = ResPool::instance();
+  BOOST_REQUIRE( pool.resolver().resolvePool() );
+
+  unsigned removeCount = 0;
+  const filter::ByStatus toBeUninstalledFilter( &ResStatus::isToBeUninstalled );
+  for ( auto it = pool.byStatusBegin( toBeUninstalledFilter ); it != pool.byStatusEnd( toBeUninstalledFilter );  it++  ) {
+    removeCount++;
+
+    auto pck = expectedRemovals.find( makeNVRA(*it) );
+    BOOST_REQUIRE_MESSAGE(  pck != expectedRemovals.end(), std::string("Unexpected package removed: ") + makeNVRA(*it) );
+
+    pck->second = true;
+  }
+
+  for ( const auto &rem : expectedRemovals ) {
+    if (!rem.second)
+      std::cout << std::string( "Expected package removal did not happen for: ") + rem.first  << std::endl;
+    //BOOST_REQUIRE_MESSAGE( rem.second, std::string( "Expected package removal did not happen for: ") + rem.first );
+  }
+
+  BOOST_REQUIRE_EQUAL( expectedRemovals.size(), removeCount );
+}
index eca9c39..a776ea1 100644 (file)
@@ -8,7 +8,7 @@ using namespace boost::unit_test;
 #include "zypp/pool/PoolStats.h"
 #include "zypp/ui/Selectable.h"
 
-static TestSetup test;
+static TestSetup test( TestSetup::initLater );
 
 struct BAD_TESTCASE {};
 
@@ -48,17 +48,22 @@ PoolItem Apde;      //  A: aspell-de        (wanted locale)
 PoolItem Apfr; //  A: aspell-fr        (unwanted locale)
 PoolItem Aprec;        //  A: recommended-pkg  (by aspell)
 
-BOOST_AUTO_TEST_CASE(testcase_init)
-{
-  test.loadTestcaseRepos( TESTS_SRC_DIR"/data/TCNamespaceRecommends" );
-  Ip   = getIPi( "aspell" );
-  Ap   = getAPi( "aspell" );
-  Ipen = getIPi( "aspell-en" );
-  Apen = getAPi( "aspell-en" );
-  Apde = getAPi( "aspell-de" );
-  Apfr = getAPi( "aspell-fr" );
-  Aprec        = getAPi( "recommended-pkg" );
-}
+struct TestInit {
+  TestInit() {
+    test = TestSetup( );
+
+    test.loadTestcaseRepos( TESTS_SRC_DIR"/data/TCNamespaceRecommends" );
+    Ip = getIPi( "aspell" );
+    Ap = getAPi( "aspell" );
+    Ipen       = getIPi( "aspell-en" );
+    Apen       = getAPi( "aspell-en" );
+    Apde       = getAPi( "aspell-de" );
+    Apfr       = getAPi( "aspell-fr" );
+    Aprec      = getAPi( "recommended-pkg" );
+  }
+  ~TestInit() { test.reset(); }
+};
+BOOST_GLOBAL_FIXTURE( TestInit );
 
 /////////////////////////////////////////////////////////////////////////////
 
index 74f1178..e18d5b0 100644 (file)
@@ -1,5 +1,4 @@
 #include "TestSetup.h"
-static TestSetup test;
 
 #include "zypp/target/rpm/RpmDb.h"
 using target::rpm::RpmDb;
@@ -10,6 +9,15 @@ using target::rpm::RpmDb;
 #define HAVE_RPMTSSETVFYFLAGS
 #endif
 
+static TestSetup test( TestSetup::initLater );
+struct TestInit {
+  TestInit() {
+    test = TestSetup( );
+  }
+  ~TestInit() { test.reset(); }
+};
+BOOST_GLOBAL_FIXTURE( TestInit );
+
 ///////////////////////////////////////////////////////////////////
 //
 // - RpmDb::checkPackage (legacy) and RpmDb::checkPackageSignature are
index 02fc67e..82afc21 100644 (file)
@@ -6,17 +6,23 @@
 
 /////////////////////////////////////////////////////////////////////////////
 
-static TestSetup test;
+static TestSetup test( TestSetup::initLater );
+struct TestInit {
+  TestInit() {
+    test = TestSetup( );
 
-BOOST_AUTO_TEST_CASE(testcase_init)
-{
-//   zypp::base::LogControl::instance().logToStdErr();
-  test.loadTestcaseRepos( TESTS_SRC_DIR"/data/TCSelectable" );
+    //   zypp::base::LogControl::instance().logToStdErr();
+    test.loadTestcaseRepos( TESTS_SRC_DIR"/data/TCSelectable" );
+
+    //   dumpRange( USR, test.pool().knownRepositoriesBegin(),
+    //                   test.pool().knownRepositoriesEnd() ) << endl;
+    //   USR << "pool: " << test.pool() << endl;
+
+  }
+  ~TestInit() { test.reset(); }
+};
+BOOST_GLOBAL_FIXTURE( TestInit );
 
-//   dumpRange( USR, test.pool().knownRepositoriesBegin(),
-//                   test.pool().knownRepositoriesEnd() ) << endl;
-//   USR << "pool: " << test.pool() << endl;
-}
 /////////////////////////////////////////////////////////////////////////////
 
 BOOST_AUTO_TEST_CASE(candiadate)
diff --git a/tests/zypp/data/PurgeKernels/arch/solver-system.xml b/tests/zypp/data/PurgeKernels/arch/solver-system.xml
new file mode 100644 (file)
index 0000000..1268681
--- /dev/null
@@ -0,0 +1,949 @@
+<channel><subchannel>
+<package>
+       <name>glibc</name>
+       <vendor>openSUSE</vendor>
+       <history>
+           <update>
+                   <arch>x86_64</arch>
+                   <version>1</version><release>1</release>
+           </update>
+       </history>
+       <provides>
+               <dep name='glibc' op='==' version='1' release='1' />
+       </provides>
+</package>
+
+<package>
+       <name>kernel-firmware</name>
+    <provides>
+        <dep name='kernel-firmware' op='==' version='20190312' release='lp151.2.3.1' />
+    </provides>
+</package>
+
+<package>
+       <name>kernel-macros</name>
+       <vendor>openSUSE</vendor>
+       <history>
+       <update>
+               <arch>noarch</arch>
+               <version>1</version><release>0</release>
+       </update>
+       </history>
+       <provides>
+               <dep name='kernel-subpackage-macros' />
+               <dep name='kernel-macros' op='==' version='1' release='0' />
+       </provides>
+</package>
+
+<!-- START KERNEL SET -->
+<package>
+       <name>kernel-default</name>
+       <vendor>openSUSE</vendor>
+       <history>
+       <update>
+               <arch>x86_64</arch>
+               <version>1</version><release>1</release>
+       </update>
+       </history>
+       <provides>
+               <dep name='multiversion(kernel)' />
+               <dep name='kernel-default-1-1' />
+               <dep name='kernel' op='==' version='1' release='1' />
+               <dep name='kernel-uname-r' op='==' version='1-1' release='default' />
+       </provides>
+       <recommends>
+               <dep name='kernel-firmware' />
+       </recommends>
+</package>
+
+<package>
+       <name>kernel-livepatch-default</name>
+       <vendor>openSUSE</vendor>
+       <history>
+       <update>
+               <arch>x86_64</arch>
+               <version>1</version><release>1</release>
+       </update>
+       </history>
+       <provides>
+               <dep name='kernel-livepatch-default-1-1' />
+       </provides>
+       <requires>
+               <dep name='kernel' op='==' version='1' release='1' />
+       </requires>
+</package>
+
+<package>
+       <name>kernel-default-devel</name>
+       <vendor>openSUSE</vendor>
+       <history>
+       <update>
+               <arch>x86_64</arch>
+               <version>1</version><release>1</release>
+       </update>
+       </history>
+       <provides>
+               <dep name='multiversion(kernel)' />
+               <dep name='kernel-default-devel' op='==' version='1' release='1' />
+       </provides>
+       <requires>
+               <dep name='kernel-devel' op='==' version='1' release='1' />
+       </requires>
+       <supplements>
+               <dep name='packageand(kernel-default:kernel-devel)' />
+       </supplements>
+</package>
+
+<package>
+       <name>kernel-default-devel-debuginfo</name>
+       <vendor>openSUSE</vendor>
+       <history>
+       <update>
+               <arch>x86_64</arch>
+               <version>1</version><release>1</release>
+       </update>
+       </history>
+       <provides>
+               <dep name='kernel-default-devel-debuginfo' op='==' version='1' release='1' />
+       </provides>
+</package>
+
+<package>
+       <name>kernel-devel</name>
+       <vendor>openSUSE</vendor>
+       <history>
+       <update>
+               <arch>noarch</arch>
+               <version>1</version><release>1</release>
+       </update>
+       </history>
+       <provides>
+               <dep name='multiversion(kernel)' />
+               <dep name='kernel-devel' op='==' version='1' release='1' />
+       </provides>
+       <requires>
+               <dep name='kernel-macros' />
+       </requires>
+</package>
+
+<package>
+       <name>kernel-syms</name>
+       <vendor>openSUSE</vendor>
+       <buildtime>1570603549</buildtime>
+       <history>
+       <update>
+               <arch>x86_64</arch>
+               <version>1</version><release>1</release>
+       </update>
+       </history>
+       <provides>
+               <dep name='multiversion(kernel)' />
+               <dep name='kernel-syms' op='==' version='1' release='1' />
+       </provides>
+       <requires>
+               <dep name='kernel-devel' op='==' version='1' release='1' />
+               <dep name='kernel-default-devel' op='==' version='1' release='1' />
+       </requires>
+</package>
+<!-- END KERNEL SET -->
+
+<!-- START KERNEL SET -->
+<package>
+       <name>kernel-default</name>
+       <vendor>openSUSE</vendor>
+       <history>
+       <update>
+               <arch>x86_64</arch>
+               <version>1</version><release>2</release>
+       </update>
+       </history>
+       <provides>
+               <dep name='multiversion(kernel)' />
+               <dep name='kernel-default-1-2' />
+               <dep name='kernel' op='==' version='1' release='2' />
+               <dep name='kernel-uname-r' op='==' version='1-2' release='default' />
+       </provides>
+       <recommends>
+               <dep name='kernel-firmware' />
+       </recommends>
+</package>
+
+<package>
+       <name>kernel-livepatch-default</name>
+       <vendor>openSUSE</vendor>
+       <history>
+       <update>
+               <arch>x86_64</arch>
+               <version>1</version><release>2</release>
+       </update>
+       </history>
+       <provides>
+               <dep name='kernel-livepatch-default-1-2' />
+       </provides>
+       <requires>
+               <dep name='kernel' op='==' version='1' release='2' />
+       </requires>
+</package>
+
+<package>
+       <name>kernel-default-devel</name>
+       <vendor>openSUSE</vendor>
+       <history>
+       <update>
+               <arch>x86_64</arch>
+               <version>1</version><release>2</release>
+       </update>
+       </history>
+       <provides>
+               <dep name='multiversion(kernel)' />
+               <dep name='kernel-default-devel' op='==' version='1' release='2' />
+       </provides>
+       <requires>
+               <dep name='kernel-devel' op='==' version='1' release='2' />
+       </requires>
+       <supplements>
+               <dep name='packageand(kernel-default:kernel-devel)' />
+       </supplements>
+</package>
+
+<package>
+       <name>kernel-default-devel-debuginfo</name>
+       <vendor>openSUSE</vendor>
+       <history>
+       <update>
+               <arch>x86_64</arch>
+               <version>1</version><release>2</release>
+       </update>
+       </history>
+       <provides>
+               <dep name='kernel-default-devel-debuginfo' op='==' version='1' release='2' />
+       </provides>
+</package>
+
+<package>
+       <name>kernel-devel</name>
+       <vendor>openSUSE</vendor>
+       <history>
+       <update>
+               <arch>noarch</arch>
+               <version>1</version><release>2</release>
+       </update>
+       </history>
+       <provides>
+               <dep name='multiversion(kernel)' />
+               <dep name='kernel-devel' op='==' version='1' release='2' />
+       </provides>
+       <requires>
+               <dep name='kernel-macros' />
+       </requires>
+</package>
+
+<package>
+       <name>kernel-syms</name>
+       <vendor>openSUSE</vendor>
+       <history>
+       <update>
+               <arch>x86_64</arch>
+               <version>1</version><release>2</release>
+       </update>
+       </history>
+       <provides>
+               <dep name='multiversion(kernel)' />
+               <dep name='kernel-syms' op='==' version='1' release='2' />
+       </provides>
+       <requires>
+               <dep name='kernel-devel' op='==' version='1' release='2' />
+               <dep name='kernel-default-devel' op='==' version='1' release='2' />
+       </requires>
+</package>
+<!-- END KERNEL SET -->
+
+<!-- START KERNEL SET -->
+<package>
+       <name>kernel-default</name>
+       <vendor>openSUSE</vendor>
+       <history>
+       <update>
+               <arch>x86_64</arch>
+               <version>1</version><release>5</release>
+       </update>
+       </history>
+       <provides>
+               <dep name='multiversion(kernel)' />
+               <dep name='kernel-default-1-5' />
+               <dep name='kernel' op='==' version='1' release='5' />
+               <dep name='kernel-uname-r' op='==' version='1-5' release='default' />
+               <dep name='ksym(foobar)' op='==' version='abdcfee' />
+       </provides>
+       <recommends>
+               <dep name='kernel-firmware' />
+       </recommends>
+</package>
+
+<package>
+       <name>kernel-default-devel</name>
+       <vendor>openSUSE</vendor>
+       <history>
+       <update>
+               <arch>x86_64</arch>
+               <version>1</version><release>5</release>
+       </update>
+       </history>
+       <provides>
+               <dep name='multiversion(kernel)' />
+               <dep name='kernel-default-devel' op='==' version='1' release='5' />
+       </provides>
+       <requires>
+               <dep name='kernel-devel' op='==' version='1' release='5' />
+       </requires>
+       <supplements>
+               <dep name='packageand(kernel-default:kernel-devel)' />
+       </supplements>
+</package>
+
+<package>
+       <name>kernel-default-devel-debuginfo</name>
+       <vendor>openSUSE</vendor>
+       <history>
+       <update>
+               <arch>x86_64</arch>
+               <version>1</version><release>5</release>
+       </update>
+       </history>
+       <provides>
+               <dep name='kernel-default-devel-debuginfo' op='==' version='1' release='5' />
+       </provides>
+</package>
+
+<package>
+       <name>kernel-devel</name>
+       <vendor>openSUSE</vendor>
+       <history>
+       <update>
+               <arch>noarch</arch>
+               <version>1</version><release>5</release>
+       </update>
+       </history>
+       <provides>
+               <dep name='multiversion(kernel)' />
+               <dep name='kernel-devel' op='==' version='1' release='5' />
+       </provides>
+       <requires>
+               <dep name='kernel-macros' />
+       </requires>
+</package>
+
+<package>
+       <name>kernel-syms</name>
+       <vendor>openSUSE</vendor>
+       <buildtime>1570603549</buildtime>
+       <history>
+       <update>
+               <arch>x86_64</arch>
+               <version>1</version><release>5</release>
+       </update>
+       </history>
+       <provides>
+               <dep name='multiversion(kernel)' />
+               <dep name='kernel-syms' op='==' version='1' release='5' />
+       </provides>
+       <requires>
+               <dep name='kernel-devel' op='==' version='1' release='5' />
+               <dep name='kernel-default-devel' op='==' version='1' release='5' />
+       </requires>
+</package>
+
+<package>
+    <name>dummy-kmp-default</name>
+       <history>
+       <update>
+               <arch>x86_64</arch>
+               <version>1</version><release>0</release>
+       </update>
+       </history>
+       <provides>
+               <dep name='kmod(dummy)' />
+               <dep name='dummy-kmp-default' op='==' version='1' release='0' />
+       </provides>
+       <requires>
+               <dep name='kernel-default-devel'/>
+    <dep name='ksym(foobar)' op='==' version='abdcfee' />
+       </requires>
+</package>
+
+<!-- END KERNEL SET -->
+
+<!-- NEXT ARCH ____________________________________________________________________________ -->
+
+<!-- START KERNEL SET -->
+<package>
+       <name>kernel-default</name>
+       <vendor>openSUSE</vendor>
+       <history>
+       <update>
+               <arch>i686</arch>
+               <version>1</version><release>1</release>
+       </update>
+       </history>
+       <provides>
+               <dep name='multiversion(kernel)' />
+               <dep name='kernel-default-1-1' />
+               <dep name='kernel' op='==' version='1' release='1' />
+               <dep name='kernel-uname-r' op='==' version='1-1' release='default' />
+       </provides>
+       <recommends>
+               <dep name='kernel-firmware' />
+       </recommends>
+</package>
+
+<package>
+       <name>kernel-livepatch-default</name>
+       <vendor>openSUSE</vendor>
+       <history>
+       <update>
+               <arch>i686</arch>
+               <version>1</version><release>1</release>
+       </update>
+       </history>
+       <provides>
+               <dep name='kernel-livepatch-default-1-1' />
+       </provides>
+       <requires>
+               <dep name='kernel' op='==' version='1' release='1' />
+       </requires>
+</package>
+
+<package>
+       <name>kernel-default-devel</name>
+       <vendor>openSUSE</vendor>
+       <history>
+       <update>
+               <arch>i686</arch>
+               <version>1</version><release>1</release>
+       </update>
+       </history>
+       <provides>
+               <dep name='multiversion(kernel)' />
+               <dep name='kernel-default-devel' op='==' version='1' release='1' />
+       </provides>
+       <requires>
+               <dep name='kernel-devel' op='==' version='1' release='1' />
+       </requires>
+       <supplements>
+               <dep name='packageand(kernel-default:kernel-devel)' />
+       </supplements>
+</package>
+
+<package>
+       <name>kernel-default-devel-debuginfo</name>
+       <vendor>openSUSE</vendor>
+       <history>
+       <update>
+               <arch>i686</arch>
+               <version>1</version><release>1</release>
+       </update>
+       </history>
+       <provides>
+               <dep name='kernel-default-devel-debuginfo' op='==' version='1' release='1' />
+       </provides>
+</package>
+
+<package>
+       <name>kernel-syms</name>
+       <vendor>openSUSE</vendor>
+       <buildtime>1570603549</buildtime>
+       <history>
+       <update>
+               <arch>i686</arch>
+               <version>1</version><release>1</release>
+       </update>
+       </history>
+       <provides>
+               <dep name='multiversion(kernel)' />
+               <dep name='kernel-syms' op='==' version='1' release='1' />
+       </provides>
+       <requires>
+               <dep name='kernel-devel' op='==' version='1' release='1' />
+               <dep name='kernel-default-devel' op='==' version='1' release='1' />
+       </requires>
+</package>
+<!-- END KERNEL SET -->
+
+<!-- START KERNEL SET -->
+<package>
+       <name>kernel-default</name>
+       <vendor>openSUSE</vendor>
+       <history>
+       <update>
+               <arch>i686</arch>
+               <version>1</version><release>2</release>
+       </update>
+       </history>
+       <provides>
+               <dep name='multiversion(kernel)' />
+               <dep name='kernel-default-1-2' />
+               <dep name='kernel' op='==' version='1' release='2' />
+               <dep name='kernel-uname-r' op='==' version='1-2' release='default' />
+       </provides>
+       <recommends>
+               <dep name='kernel-firmware' />
+       </recommends>
+</package>
+
+<package>
+       <name>kernel-livepatch-default</name>
+       <vendor>openSUSE</vendor>
+       <history>
+       <update>
+               <arch>i686</arch>
+               <version>1</version><release>2</release>
+       </update>
+       </history>
+       <provides>
+               <dep name='kernel-livepatch-default-1-2' />
+       </provides>
+       <requires>
+               <dep name='kernel' op='==' version='1' release='2' />
+       </requires>
+</package>
+
+<package>
+       <name>kernel-default-devel</name>
+       <vendor>openSUSE</vendor>
+       <history>
+       <update>
+               <arch>i686</arch>
+               <version>1</version><release>2</release>
+       </update>
+       </history>
+       <provides>
+               <dep name='multiversion(kernel)' />
+               <dep name='kernel-default-devel' op='==' version='1' release='2' />
+       </provides>
+       <requires>
+               <dep name='kernel-devel' op='==' version='1' release='2' />
+       </requires>
+       <supplements>
+               <dep name='packageand(kernel-default:kernel-devel)' />
+       </supplements>
+</package>
+
+<package>
+       <name>kernel-default-devel-debuginfo</name>
+       <vendor>openSUSE</vendor>
+       <history>
+       <update>
+               <arch>i686</arch>
+               <version>1</version><release>2</release>
+       </update>
+       </history>
+       <provides>
+               <dep name='kernel-default-devel-debuginfo' op='==' version='1' release='2' />
+       </provides>
+</package>
+
+<package>
+       <name>kernel-syms</name>
+       <vendor>openSUSE</vendor>
+       <history>
+       <update>
+               <arch>i686</arch>
+               <version>1</version><release>2</release>
+       </update>
+       </history>
+       <provides>
+               <dep name='multiversion(kernel)' />
+               <dep name='kernel-syms' op='==' version='1' release='2' />
+       </provides>
+       <requires>
+               <dep name='kernel-devel' op='==' version='1' release='2' />
+               <dep name='kernel-default-devel' op='==' version='1' release='2' />
+       </requires>
+</package>
+<!-- END KERNEL SET -->
+
+<!-- START KERNEL SET -->
+<package>
+       <name>kernel-default</name>
+       <vendor>openSUSE</vendor>
+       <history>
+       <update>
+               <arch>i686</arch>
+               <version>1</version><release>5</release>
+       </update>
+       </history>
+       <provides>
+               <dep name='multiversion(kernel)' />
+               <dep name='kernel-default-1-5' />
+               <dep name='kernel' op='==' version='1' release='5' />
+               <dep name='kernel-uname-r' op='==' version='1-5' release='default' />
+               <dep name='ksym(foobar)' op='==' version='abdcfee' />
+       </provides>
+       <recommends>
+               <dep name='kernel-firmware' />
+       </recommends>
+</package>
+
+<package>
+       <name>kernel-default-devel</name>
+       <vendor>openSUSE</vendor>
+       <history>
+       <update>
+               <arch>i686</arch>
+               <version>1</version><release>5</release>
+       </update>
+       </history>
+       <provides>
+               <dep name='multiversion(kernel)' />
+               <dep name='kernel-default-devel' op='==' version='1' release='5' />
+       </provides>
+       <requires>
+               <dep name='kernel-devel' op='==' version='1' release='5' />
+       </requires>
+       <supplements>
+               <dep name='packageand(kernel-default:kernel-devel)' />
+       </supplements>
+</package>
+
+<package>
+       <name>kernel-default-devel-debuginfo</name>
+       <vendor>openSUSE</vendor>
+       <history>
+       <update>
+               <arch>i686</arch>
+               <version>1</version><release>5</release>
+       </update>
+       </history>
+       <provides>
+               <dep name='kernel-default-devel-debuginfo' op='==' version='1' release='5' />
+       </provides>
+</package>
+
+<package>
+       <name>kernel-syms</name>
+       <vendor>openSUSE</vendor>
+       <buildtime>1570603549</buildtime>
+       <history>
+       <update>
+               <arch>i686</arch>
+               <version>1</version><release>5</release>
+       </update>
+       </history>
+       <provides>
+               <dep name='multiversion(kernel)' />
+               <dep name='kernel-syms' op='==' version='1' release='5' />
+       </provides>
+       <requires>
+               <dep name='kernel-devel' op='==' version='1' release='5' />
+               <dep name='kernel-default-devel' op='==' version='1' release='5' />
+       </requires>
+</package>
+
+<package>
+    <name>dummy-kmp-default</name>
+       <history>
+       <update>
+               <arch>i686</arch>
+               <version>1</version><release>0</release>
+       </update>
+       </history>
+       <provides>
+               <dep name='kmod(dummy)' />
+               <dep name='dummy-kmp-default' op='==' version='1' release='0' />
+       </provides>
+       <requires>
+               <dep name='kernel-default-devel'/>
+    <dep name='ksym(foobar)' op='==' version='abdcfee' />
+       </requires>
+</package>
+
+<!-- END KERNEL SET -->
+
+<!-- NEXT ARCH ____________________________________________________________________________ -->
+
+<!-- START KERNEL SET -->
+<package>
+       <name>kernel-default</name>
+       <vendor>openSUSE</vendor>
+       <history>
+       <update>
+               <arch>aarch64</arch>
+               <version>1</version><release>1</release>
+       </update>
+       </history>
+       <provides>
+               <dep name='multiversion(kernel)' />
+               <dep name='kernel-default-1-1' />
+               <dep name='kernel' op='==' version='1' release='1' />
+               <dep name='kernel-uname-r' op='==' version='1-1' release='default' />
+       </provides>
+       <recommends>
+               <dep name='kernel-firmware' />
+       </recommends>
+</package>
+
+<package>
+       <name>kernel-livepatch-default</name>
+       <vendor>openSUSE</vendor>
+       <history>
+       <update>
+               <arch>aarch64</arch>
+               <version>1</version><release>1</release>
+       </update>
+       </history>
+       <provides>
+               <dep name='kernel-livepatch-default-1-1' />
+       </provides>
+       <requires>
+               <dep name='kernel' op='==' version='1' release='1' />
+       </requires>
+</package>
+
+<package>
+       <name>kernel-default-devel</name>
+       <vendor>openSUSE</vendor>
+       <history>
+       <update>
+               <arch>aarch64</arch>
+               <version>1</version><release>1</release>
+       </update>
+       </history>
+       <provides>
+               <dep name='multiversion(kernel)' />
+               <dep name='kernel-default-devel' op='==' version='1' release='1' />
+       </provides>
+       <requires>
+               <dep name='kernel-devel' op='==' version='1' release='1' />
+       </requires>
+       <supplements>
+               <dep name='packageand(kernel-default:kernel-devel)' />
+       </supplements>
+</package>
+
+<package>
+       <name>kernel-default-devel-debuginfo</name>
+       <vendor>openSUSE</vendor>
+       <history>
+       <update>
+               <arch>aarch64</arch>
+               <version>1</version><release>1</release>
+       </update>
+       </history>
+       <provides>
+               <dep name='kernel-default-devel-debuginfo' op='==' version='1' release='1' />
+       </provides>
+</package>
+
+<package>
+       <name>kernel-syms</name>
+       <vendor>openSUSE</vendor>
+       <buildtime>1570603549</buildtime>
+       <history>
+       <update>
+               <arch>aarch64</arch>
+               <version>1</version><release>1</release>
+       </update>
+       </history>
+       <provides>
+               <dep name='multiversion(kernel)' />
+               <dep name='kernel-syms' op='==' version='1' release='1' />
+       </provides>
+       <requires>
+               <dep name='kernel-devel' op='==' version='1' release='1' />
+               <dep name='kernel-default-devel' op='==' version='1' release='1' />
+       </requires>
+</package>
+<!-- END KERNEL SET -->
+
+<!-- START KERNEL SET -->
+<package>
+       <name>kernel-default</name>
+       <vendor>openSUSE</vendor>
+       <history>
+       <update>
+               <arch>aarch64</arch>
+               <version>1</version><release>2</release>
+       </update>
+       </history>
+       <provides>
+               <dep name='multiversion(kernel)' />
+               <dep name='kernel-default-1-2' />
+               <dep name='kernel' op='==' version='1' release='2' />
+               <dep name='kernel-uname-r' op='==' version='1-2' release='default' />
+       </provides>
+       <recommends>
+               <dep name='kernel-firmware' />
+       </recommends>
+</package>
+
+<package>
+       <name>kernel-livepatch-default</name>
+       <vendor>openSUSE</vendor>
+       <history>
+       <update>
+               <arch>aarch64</arch>
+               <version>1</version><release>2</release>
+       </update>
+       </history>
+       <provides>
+               <dep name='kernel-livepatch-default-1-2' />
+       </provides>
+       <requires>
+               <dep name='kernel' op='==' version='1' release='2' />
+       </requires>
+</package>
+
+<package>
+       <name>kernel-default-devel</name>
+       <vendor>openSUSE</vendor>
+       <history>
+       <update>
+               <arch>aarch64</arch>
+               <version>1</version><release>2</release>
+       </update>
+       </history>
+       <provides>
+               <dep name='multiversion(kernel)' />
+               <dep name='kernel-default-devel' op='==' version='1' release='2' />
+       </provides>
+       <requires>
+               <dep name='kernel-devel' op='==' version='1' release='2' />
+       </requires>
+       <supplements>
+               <dep name='packageand(kernel-default:kernel-devel)' />
+       </supplements>
+</package>
+
+<package>
+       <name>kernel-default-devel-debuginfo</name>
+       <vendor>openSUSE</vendor>
+       <history>
+       <update>
+               <arch>aarch64</arch>
+               <version>1</version><release>2</release>
+       </update>
+       </history>
+       <provides>
+               <dep name='kernel-default-devel-debuginfo' op='==' version='1' release='2' />
+       </provides>
+</package>
+
+<package>
+       <name>kernel-syms</name>
+       <vendor>openSUSE</vendor>
+       <history>
+       <update>
+               <arch>aarch64</arch>
+               <version>1</version><release>2</release>
+       </update>
+       </history>
+       <provides>
+               <dep name='multiversion(kernel)' />
+               <dep name='kernel-syms' op='==' version='1' release='2' />
+       </provides>
+       <requires>
+               <dep name='kernel-devel' op='==' version='1' release='2' />
+               <dep name='kernel-default-devel' op='==' version='1' release='2' />
+       </requires>
+</package>
+<!-- END KERNEL SET -->
+
+<!-- START KERNEL SET -->
+<package>
+       <name>kernel-default</name>
+       <vendor>openSUSE</vendor>
+       <history>
+       <update>
+               <arch>aarch64</arch>
+               <version>1</version><release>5</release>
+       </update>
+       </history>
+       <provides>
+               <dep name='multiversion(kernel)' />
+               <dep name='kernel-default-1-5' />
+               <dep name='kernel' op='==' version='1' release='5' />
+               <dep name='kernel-uname-r' op='==' version='1-5' release='default' />
+               <dep name='ksym(foobar)' op='==' version='abdcfee' />
+       </provides>
+       <recommends>
+               <dep name='kernel-firmware' />
+       </recommends>
+</package>
+
+<package>
+       <name>kernel-default-devel</name>
+       <vendor>openSUSE</vendor>
+       <history>
+       <update>
+               <arch>aarch64</arch>
+               <version>1</version><release>5</release>
+       </update>
+       </history>
+       <provides>
+               <dep name='multiversion(kernel)' />
+               <dep name='kernel-default-devel' op='==' version='1' release='5' />
+       </provides>
+       <requires>
+               <dep name='kernel-devel' op='==' version='1' release='5' />
+       </requires>
+       <supplements>
+               <dep name='packageand(kernel-default:kernel-devel)' />
+       </supplements>
+</package>
+
+<package>
+       <name>kernel-default-devel-debuginfo</name>
+       <vendor>openSUSE</vendor>
+       <history>
+       <update>
+               <arch>aarch64</arch>
+               <version>1</version><release>5</release>
+       </update>
+       </history>
+       <provides>
+               <dep name='kernel-default-devel-debuginfo' op='==' version='1' release='5' />
+       </provides>
+</package>
+
+<package>
+       <name>kernel-syms</name>
+       <vendor>openSUSE</vendor>
+       <buildtime>1570603549</buildtime>
+       <history>
+       <update>
+               <arch>aarch64</arch>
+               <version>1</version><release>5</release>
+       </update>
+       </history>
+       <provides>
+               <dep name='multiversion(kernel)' />
+               <dep name='kernel-syms' op='==' version='1' release='5' />
+       </provides>
+       <requires>
+               <dep name='kernel-devel' op='==' version='1' release='5' />
+               <dep name='kernel-default-devel' op='==' version='1' release='5' />
+       </requires>
+</package>
+
+<package>
+    <name>dummy-kmp-default</name>
+       <history>
+       <update>
+               <arch>aarch64</arch>
+               <version>1</version><release>0</release>
+       </update>
+       </history>
+       <provides>
+               <dep name='kmod(dummy)' />
+               <dep name='dummy-kmp-default' op='==' version='1' release='0' />
+       </provides>
+       <requires>
+               <dep name='kernel-default-devel'/>
+    <dep name='ksym(foobar)' op='==' version='abdcfee' />
+       </requires>
+</package>
+
+<!-- END KERNEL SET -->
+
+
+</channel></subchannel>
diff --git a/tests/zypp/data/PurgeKernels/arch/solver-test.xml b/tests/zypp/data/PurgeKernels/arch/solver-test.xml
new file mode 100644 (file)
index 0000000..993d537
--- /dev/null
@@ -0,0 +1,8 @@
+<?xml version="1.0"?>
+<test>
+<setup arch="x86_64">
+       <system file="solver-system.xml"/>
+       <locale name="en_US" />
+       <locale name="de" />
+</setup>
+</test>
diff --git a/tests/zypp/data/PurgeKernels/simple/solver-system.xml b/tests/zypp/data/PurgeKernels/simple/solver-system.xml
new file mode 100644 (file)
index 0000000..2a14967
--- /dev/null
@@ -0,0 +1,583 @@
+<channel><subchannel>
+<package>
+       <name>glibc</name>
+       <vendor>openSUSE</vendor>
+       <history>
+           <update>
+                   <arch>x86_64</arch>
+                   <version>1</version><release>1</release>
+           </update>
+       </history>
+       <provides>
+               <dep name='glibc' op='==' version='1' release='1' />
+       </provides>
+</package>
+
+<package>
+       <name>kernel-firmware</name>
+    <provides>
+        <dep name='kernel-firmware' op='==' version='20190312' release='lp151.2.3.1' />
+    </provides>
+</package>
+
+<package>
+       <name>kernel-macros</name>
+       <vendor>openSUSE</vendor>
+       <history>
+       <update>
+               <arch>noarch</arch>
+               <version>1</version><release>0</release>
+       </update>
+       </history>
+       <provides>
+               <dep name='kernel-subpackage-macros' />
+               <dep name='kernel-macros' op='==' version='1' release='0' />
+       </provides>
+</package>
+
+<!-- START KERNEL SET -->
+<package>
+       <name>kernel-default</name>
+       <vendor>openSUSE</vendor>
+       <history>
+       <update>
+               <arch>x86_64</arch>
+               <version>1</version><release>1</release>
+       </update>
+       </history>
+       <provides>
+               <dep name='multiversion(kernel)' />
+               <dep name='kernel-default-1-1' />
+               <dep name='kernel' op='==' version='1' release='1' />
+               <dep name='kernel-uname-r' op='==' version='1-1' release='default' />
+       </provides>
+       <recommends>
+               <dep name='kernel-firmware' />
+       </recommends>
+</package>
+
+<package>
+       <name>kernel-livepatch-default</name>
+       <vendor>openSUSE</vendor>
+       <history>
+       <update>
+               <arch>x86_64</arch>
+               <version>1</version><release>1</release>
+       </update>
+       </history>
+       <provides>
+               <dep name='kernel-livepatch-default-1-1' />
+       </provides>
+       <requires>
+               <dep name='kernel' op='==' version='1' release='1' />
+       </requires>
+</package>
+
+<package>
+       <name>kernel-default-devel</name>
+       <vendor>openSUSE</vendor>
+       <history>
+       <update>
+               <arch>x86_64</arch>
+               <version>1</version><release>1</release>
+       </update>
+       </history>
+       <provides>
+               <dep name='multiversion(kernel)' />
+               <dep name='kernel-default-devel' op='==' version='1' release='1' />
+       </provides>
+       <requires>
+               <dep name='kernel-devel' op='==' version='1' release='1' />
+       </requires>
+       <supplements>
+               <dep name='packageand(kernel-default:kernel-devel)' />
+       </supplements>
+</package>
+
+<package>
+       <name>kernel-default-devel-debuginfo</name>
+       <vendor>openSUSE</vendor>
+       <history>
+       <update>
+               <arch>x86_64</arch>
+               <version>1</version><release>1</release>
+       </update>
+       </history>
+       <provides>
+               <dep name='kernel-default-devel-debuginfo' op='==' version='1' release='1' />
+       </provides>
+</package>
+
+<package>
+       <name>kernel-devel</name>
+       <vendor>openSUSE</vendor>
+       <history>
+       <update>
+               <arch>noarch</arch>
+               <version>1</version><release>1</release>
+       </update>
+       </history>
+       <provides>
+               <dep name='multiversion(kernel)' />
+               <dep name='kernel-devel' op='==' version='1' release='1' />
+       </provides>
+       <requires>
+               <dep name='kernel-macros' />
+       </requires>
+</package>
+
+<package>
+       <name>kernel-source</name>
+       <vendor>openSUSE</vendor>
+       <history>
+       <update>
+               <arch>noarch</arch>
+               <version>1</version><release>1</release>
+       </update>
+       </history>
+       <provides>
+               <dep name='kernel-source' op='==' version='1' release='1' />
+       </provides>
+       <requires>
+               <dep name='kernel-devel' op='==' version='1' release='1' />
+       </requires>
+</package>
+
+<package>
+       <name>kernel-syms</name>
+       <vendor>openSUSE</vendor>
+       <buildtime>1570603549</buildtime>
+       <history>
+       <update>
+               <arch>x86_64</arch>
+               <version>1</version><release>1</release>
+       </update>
+       </history>
+       <provides>
+               <dep name='multiversion(kernel)' />
+               <dep name='kernel-syms' op='==' version='1' release='1' />
+       </provides>
+       <requires>
+               <dep name='kernel-devel' op='==' version='1' release='1' />
+               <dep name='kernel-default-devel' op='==' version='1' release='1' />
+       </requires>
+</package>
+<!-- END KERNEL SET -->
+
+<!-- START KERNEL SET -->
+<package>
+       <name>kernel-default</name>
+       <vendor>openSUSE</vendor>
+       <history>
+       <update>
+               <arch>x86_64</arch>
+               <version>1</version><release>2</release>
+       </update>
+       </history>
+       <provides>
+               <dep name='multiversion(kernel)' />
+               <dep name='kernel-default-1-2' />
+               <dep name='kernel' op='==' version='1' release='2' />
+               <dep name='kernel-uname-r' op='==' version='1-2' release='default' />
+       </provides>
+       <recommends>
+               <dep name='kernel-firmware' />
+       </recommends>
+</package>
+
+<package>
+       <name>kernel-livepatch-default</name>
+       <vendor>openSUSE</vendor>
+       <history>
+       <update>
+               <arch>x86_64</arch>
+               <version>1</version><release>2</release>
+       </update>
+       </history>
+       <provides>
+               <dep name='kernel-livepatch-default-1-2' />
+       </provides>
+       <requires>
+               <dep name='kernel' op='==' version='1' release='2' />
+       </requires>
+</package>
+
+<package>
+       <name>kernel-default-devel</name>
+       <vendor>openSUSE</vendor>
+       <history>
+       <update>
+               <arch>x86_64</arch>
+               <version>1</version><release>2</release>
+       </update>
+       </history>
+       <provides>
+               <dep name='multiversion(kernel)' />
+               <dep name='kernel-default-devel' op='==' version='1' release='2' />
+       </provides>
+       <requires>
+               <dep name='kernel-devel' op='==' version='1' release='2' />
+       </requires>
+       <supplements>
+               <dep name='packageand(kernel-default:kernel-devel)' />
+       </supplements>
+</package>
+
+<package>
+       <name>kernel-default-devel-debuginfo</name>
+       <vendor>openSUSE</vendor>
+       <history>
+       <update>
+               <arch>x86_64</arch>
+               <version>1</version><release>2</release>
+       </update>
+       </history>
+       <provides>
+               <dep name='kernel-default-devel-debuginfo' op='==' version='1' release='2' />
+       </provides>
+</package>
+
+<package>
+       <name>kernel-devel</name>
+       <vendor>openSUSE</vendor>
+       <history>
+       <update>
+               <arch>noarch</arch>
+               <version>1</version><release>2</release>
+       </update>
+       </history>
+       <provides>
+               <dep name='multiversion(kernel)' />
+               <dep name='kernel-devel' op='==' version='1' release='2' />
+       </provides>
+       <requires>
+               <dep name='kernel-macros' />
+       </requires>
+</package>
+
+<package>
+       <name>kernel-syms</name>
+       <vendor>openSUSE</vendor>
+       <history>
+       <update>
+               <arch>x86_64</arch>
+               <version>1</version><release>2</release>
+       </update>
+       </history>
+       <provides>
+               <dep name='multiversion(kernel)' />
+               <dep name='kernel-syms' op='==' version='1' release='2' />
+       </provides>
+       <requires>
+               <dep name='kernel-devel' op='==' version='1' release='2' />
+               <dep name='kernel-default-devel' op='==' version='1' release='2' />
+       </requires>
+</package>
+<!-- END KERNEL SET -->
+
+<!-- START KERNEL SET -->
+<package>
+       <name>kernel-default</name>
+       <vendor>openSUSE</vendor>
+       <history>
+       <update>
+               <arch>x86_64</arch>
+               <version>1</version><release>3</release>
+       </update>
+       </history>
+       <provides>
+               <dep name='multiversion(kernel)' />
+               <dep name='kernel-default-1-3' />
+               <dep name='kernel' op='==' version='1' release='3' />
+               <dep name='kernel-uname-r' op='==' version='1-3' release='default' />
+       </provides>
+       <recommends>
+               <dep name='kernel-firmware' />
+       </recommends>
+</package>
+
+<package>
+       <name>kernel-default-devel</name>
+       <vendor>openSUSE</vendor>
+       <history>
+       <update>
+               <arch>x86_64</arch>
+               <version>1</version><release>3</release>
+       </update>
+       </history>
+       <provides>
+               <dep name='multiversion(kernel)' />
+               <dep name='kernel-default-devel' op='==' version='1' release='3' />
+       </provides>
+       <requires>
+               <dep name='kernel-devel' op='==' version='1' release='3' />
+       </requires>
+       <supplements>
+               <dep name='packageand(kernel-default:kernel-devel)' />
+       </supplements>
+</package>
+
+<package>
+       <name>kernel-default-devel-debuginfo</name>
+       <vendor>openSUSE</vendor>
+       <history>
+       <update>
+               <arch>x86_64</arch>
+               <version>1</version><release>3</release>
+       </update>
+       </history>
+       <provides>
+               <dep name='kernel-default-devel-debuginfo' op='==' version='1' release='3' />
+       </provides>
+</package>
+
+<package>
+       <name>kernel-devel</name>
+       <vendor>openSUSE</vendor>
+       <history>
+       <update>
+               <arch>noarch</arch>
+               <version>1</version><release>3</release>
+       </update>
+       </history>
+       <provides>
+               <dep name='multiversion(kernel)' />
+               <dep name='kernel-devel' op='==' version='1' release='3' />
+       </provides>
+       <requires>
+               <dep name='kernel-macros' />
+       </requires>
+</package>
+
+<package>
+       <name>kernel-syms</name>
+       <vendor>openSUSE</vendor>
+       <history>
+       <update>
+               <arch>x86_64</arch>
+               <version>1</version><release>3</release>
+       </update>
+       </history>
+       <provides>
+               <dep name='multiversion(kernel)' />
+               <dep name='kernel-syms' op='==' version='1' release='3' />
+       </provides>
+       <requires>
+               <dep name='kernel-devel' op='==' version='1' release='3' />
+               <dep name='kernel-default-devel' op='==' version='1' release='3' />
+       </requires>
+</package>
+<!-- END KERNEL SET -->
+
+<!-- START KERNEL SET -->
+<package>
+       <name>kernel-default</name>
+       <vendor>openSUSE</vendor>
+       <history>
+       <update>
+               <arch>x86_64</arch>
+               <version>1</version><release>4</release>
+       </update>
+       </history>
+       <provides>
+               <dep name='multiversion(kernel)' />
+               <dep name='kernel-default-1-4' />
+               <dep name='kernel' op='==' version='1' release='4' />
+               <dep name='kernel-uname-r' op='==' version='1-4' release='default' />
+       </provides>
+       <recommends>
+               <dep name='kernel-firmware' />
+       </recommends>
+</package>
+
+<package>
+       <name>kernel-default-devel</name>
+       <vendor>openSUSE</vendor>
+       <history>
+       <update>
+               <arch>x86_64</arch>
+               <version>1</version><release>4</release>
+       </update>
+       </history>
+       <provides>
+               <dep name='multiversion(kernel)' />
+               <dep name='kernel-default-devel' op='==' version='1' release='4' />
+       </provides>
+       <requires>
+               <dep name='kernel-devel' op='==' version='1' release='4' />
+       </requires>
+       <supplements>
+               <dep name='packageand(kernel-default:kernel-devel)' />
+       </supplements>
+</package>
+
+<package>
+       <name>kernel-default-devel-debuginfo</name>
+       <vendor>openSUSE</vendor>
+       <history>
+       <update>
+               <arch>x86_64</arch>
+               <version>1</version><release>4</release>
+       </update>
+       </history>
+       <provides>
+               <dep name='kernel-default-devel-debuginfo' op='==' version='1' release='4' />
+       </provides>
+</package>
+
+<package>
+       <name>kernel-devel</name>
+       <vendor>openSUSE</vendor>
+       <history>
+       <update>
+               <arch>noarch</arch>
+               <version>1</version><release>4</release>
+       </update>
+       </history>
+       <provides>
+               <dep name='multiversion(kernel)' />
+               <dep name='kernel-devel' op='==' version='1' release='4' />
+       </provides>
+       <requires>
+               <dep name='kernel-macros' />
+       </requires>
+</package>
+
+<package>
+       <name>kernel-syms</name>
+       <vendor>openSUSE</vendor>
+       <buildtime>1570603549</buildtime>
+       <history>
+       <update>
+               <arch>x86_64</arch>
+               <version>1</version><release>4</release>
+       </update>
+       </history>
+       <provides>
+               <dep name='multiversion(kernel)' />
+               <dep name='kernel-syms' op='==' version='1' release='4' />
+       </provides>
+       <requires>
+               <dep name='kernel-devel' op='==' version='1' release='4' />
+               <dep name='kernel-default-devel' op='==' version='1' release='4' />
+       </requires>
+</package>
+<!-- END KERNEL SET -->
+
+<!-- START KERNEL SET -->
+<package>
+       <name>kernel-default</name>
+       <vendor>openSUSE</vendor>
+       <history>
+       <update>
+               <arch>x86_64</arch>
+               <version>1</version><release>5</release>
+       </update>
+       </history>
+       <provides>
+               <dep name='multiversion(kernel)' />
+               <dep name='kernel-default-1-5' />
+               <dep name='kernel' op='==' version='1' release='5' />
+               <dep name='kernel-uname-r' op='==' version='1-5' release='default' />
+               <dep name='ksym(foobar)' op='==' version='abdcfee' />
+       </provides>
+       <recommends>
+               <dep name='kernel-firmware' />
+       </recommends>
+</package>
+
+<package>
+       <name>kernel-default-devel</name>
+       <vendor>openSUSE</vendor>
+       <history>
+       <update>
+               <arch>x86_64</arch>
+               <version>1</version><release>5</release>
+       </update>
+       </history>
+       <provides>
+               <dep name='multiversion(kernel)' />
+               <dep name='kernel-default-devel' op='==' version='1' release='5' />
+       </provides>
+       <requires>
+               <dep name='kernel-devel' op='==' version='1' release='5' />
+       </requires>
+       <supplements>
+               <dep name='packageand(kernel-default:kernel-devel)' />
+       </supplements>
+</package>
+
+<package>
+       <name>kernel-default-devel-debuginfo</name>
+       <vendor>openSUSE</vendor>
+       <history>
+       <update>
+               <arch>x86_64</arch>
+               <version>1</version><release>5</release>
+       </update>
+       </history>
+       <provides>
+               <dep name='kernel-default-devel-debuginfo' op='==' version='1' release='5' />
+       </provides>
+</package>
+
+<package>
+       <name>kernel-devel</name>
+       <vendor>openSUSE</vendor>
+       <history>
+       <update>
+               <arch>noarch</arch>
+               <version>1</version><release>5</release>
+       </update>
+       </history>
+       <provides>
+               <dep name='multiversion(kernel)' />
+               <dep name='kernel-devel' op='==' version='1' release='5' />
+       </provides>
+       <requires>
+               <dep name='kernel-macros' />
+       </requires>
+</package>
+
+<package>
+       <name>kernel-syms</name>
+       <vendor>openSUSE</vendor>
+       <buildtime>1570603549</buildtime>
+       <history>
+       <update>
+               <arch>x86_64</arch>
+               <version>1</version><release>5</release>
+       </update>
+       </history>
+       <provides>
+               <dep name='multiversion(kernel)' />
+               <dep name='kernel-syms' op='==' version='1' release='5' />
+       </provides>
+       <requires>
+               <dep name='kernel-devel' op='==' version='1' release='5' />
+               <dep name='kernel-default-devel' op='==' version='1' release='5' />
+       </requires>
+</package>
+
+<package>
+    <name>dummy-kmp-default</name>
+       <history>
+       <update>
+               <arch>x86_64</arch>
+               <version>1</version><release>0</release>
+       </update>
+       </history>
+       <provides>
+               <dep name='kmod(dummy)' />
+               <dep name='dummy-kmp-default' op='==' version='1' release='0' />
+       </provides>
+       <requires>
+               <dep name='kernel-default-devel'/>
+    <dep name='ksym(foobar)' op='==' version='abdcfee' />
+       </requires>
+</package>
+
+<!-- END KERNEL SET -->
+
+
+</channel></subchannel>
diff --git a/tests/zypp/data/PurgeKernels/simple/solver-test.xml b/tests/zypp/data/PurgeKernels/simple/solver-test.xml
new file mode 100644 (file)
index 0000000..993d537
--- /dev/null
@@ -0,0 +1,8 @@
+<?xml version="1.0"?>
+<test>
+<setup arch="x86_64">
+       <system file="solver-system.xml"/>
+       <locale name="en_US" />
+       <locale name="de" />
+</setup>
+</test>
diff --git a/tests/zypp/data/PurgeKernels/withdeps/solver-system.xml b/tests/zypp/data/PurgeKernels/withdeps/solver-system.xml
new file mode 100644 (file)
index 0000000..6e30db8
--- /dev/null
@@ -0,0 +1,411 @@
+<channel><subchannel>
+<package>
+       <name>glibc</name>
+       <vendor>openSUSE</vendor>
+       <history>
+           <update>
+                   <arch>x86_64</arch>
+                   <version>1</version><release>1</release>
+           </update>
+       </history>
+       <provides>
+               <dep name='glibc' op='==' version='1' release='1' />
+       </provides>
+</package>
+
+<package>
+       <name>kernel-firmware</name>
+    <provides>
+        <dep name='kernel-firmware' op='==' version='20190312' release='lp151.2.3.1' />
+    </provides>
+</package>
+
+<package>
+       <name>kernel-macros</name>
+       <vendor>openSUSE</vendor>
+       <history>
+       <update>
+               <arch>noarch</arch>
+               <version>1</version><release>0</release>
+       </update>
+       </history>
+       <provides>
+               <dep name='kernel-subpackage-macros' />
+               <dep name='kernel-macros' op='==' version='1' release='0' />
+       </provides>
+</package>
+
+<!-- START KERNEL SET -->
+<package>
+       <name>kernel-default</name>
+       <vendor>openSUSE</vendor>
+       <history>
+       <update>
+               <arch>x86_64</arch>
+               <version>1</version><release>1</release>
+       </update>
+       </history>
+       <provides>
+               <dep name='multiversion(kernel)' />
+               <dep name='kernel-default-1-1' />
+               <dep name='kernel' op='==' version='1' release='1' />
+               <dep name='kernel-uname-r' op='==' version='1-1' release='default' />
+       </provides>
+       <recommends>
+               <dep name='kernel-firmware' />
+       </recommends>
+</package>
+
+<package>
+       <name>kernel-livepatch-default</name>
+       <vendor>openSUSE</vendor>
+       <history>
+       <update>
+               <arch>x86_64</arch>
+               <version>1</version><release>1</release>
+       </update>
+       </history>
+       <provides>
+               <dep name='kernel-livepatch-default-1-1' />
+       </provides>
+       <requires>
+               <dep name='kernel' op='==' version='1' release='1' />
+       </requires>
+</package>
+
+<!-- random package depending on a specific kernel version, this should block the kernel from being purged -->
+<package>
+       <name>foo</name>
+       <vendor>openSUSE</vendor>
+       <history>
+       <update>
+               <arch>x86_64</arch>
+               <version>1</version><release>1</release>
+       </update>
+       </history>
+       <provides>
+               <dep name='foo' op='==' version='1' release='1'  />
+       </provides>
+       <requires>
+               <dep name='kernel' op='==' version='1' release='1' />
+       </requires>
+</package>
+
+<package>
+       <name>kernel-default-devel</name>
+       <vendor>openSUSE</vendor>
+       <history>
+       <update>
+               <arch>x86_64</arch>
+               <version>1</version><release>1</release>
+       </update>
+       </history>
+       <provides>
+               <dep name='multiversion(kernel)' />
+               <dep name='kernel-default-devel' op='==' version='1' release='1' />
+       </provides>
+       <requires>
+               <dep name='kernel-devel' op='==' version='1' release='1' />
+       </requires>
+       <supplements>
+               <dep name='packageand(kernel-default:kernel-devel)' />
+       </supplements>
+</package>
+
+<package>
+       <name>kernel-default-devel-debuginfo</name>
+       <vendor>openSUSE</vendor>
+       <history>
+       <update>
+               <arch>x86_64</arch>
+               <version>1</version><release>1</release>
+       </update>
+       </history>
+       <provides>
+               <dep name='kernel-default-devel-debuginfo' op='==' version='1' release='1' />
+       </provides>
+</package>
+
+<package>
+       <name>kernel-devel</name>
+       <vendor>openSUSE</vendor>
+       <history>
+       <update>
+               <arch>noarch</arch>
+               <version>1</version><release>1</release>
+       </update>
+       </history>
+       <provides>
+               <dep name='multiversion(kernel)' />
+               <dep name='kernel-devel' op='==' version='1' release='1' />
+       </provides>
+       <requires>
+               <dep name='kernel-macros' />
+       </requires>
+</package>
+
+<package>
+       <name>kernel-syms</name>
+       <vendor>openSUSE</vendor>
+       <buildtime>1570603549</buildtime>
+       <history>
+       <update>
+               <arch>x86_64</arch>
+               <version>1</version><release>1</release>
+       </update>
+       </history>
+       <provides>
+               <dep name='multiversion(kernel)' />
+               <dep name='kernel-syms' op='==' version='1' release='1' />
+       </provides>
+       <requires>
+               <dep name='kernel-devel' op='==' version='1' release='1' />
+               <dep name='kernel-default-devel' op='==' version='1' release='1' />
+       </requires>
+</package>
+<!-- END KERNEL SET -->
+
+<!-- START KERNEL SET -->
+<package>
+       <name>kernel-default</name>
+       <vendor>openSUSE</vendor>
+       <history>
+       <update>
+               <arch>x86_64</arch>
+               <version>1</version><release>2</release>
+       </update>
+       </history>
+       <provides>
+               <dep name='multiversion(kernel)' />
+               <dep name='kernel-default-1-2' />
+               <dep name='kernel' op='==' version='1' release='2' />
+               <dep name='kernel-uname-r' op='==' version='1-2' release='default' />
+       </provides>
+       <recommends>
+               <dep name='kernel-firmware' />
+       </recommends>
+</package>
+
+<package>
+       <name>kernel-livepatch-default</name>
+       <vendor>openSUSE</vendor>
+       <history>
+       <update>
+               <arch>x86_64</arch>
+               <version>1</version><release>2</release>
+       </update>
+       </history>
+       <provides>
+               <dep name='kernel-livepatch-default-1-2' />
+       </provides>
+       <requires>
+               <dep name='kernel' op='==' version='1' release='2' />
+       </requires>
+</package>
+
+<package>
+       <name>kernel-default-devel</name>
+       <vendor>openSUSE</vendor>
+       <history>
+       <update>
+               <arch>x86_64</arch>
+               <version>1</version><release>2</release>
+       </update>
+       </history>
+       <provides>
+               <dep name='multiversion(kernel)' />
+               <dep name='kernel-default-devel' op='==' version='1' release='2' />
+       </provides>
+       <requires>
+               <dep name='kernel-devel' op='==' version='1' release='2' />
+       </requires>
+       <supplements>
+               <dep name='packageand(kernel-default:kernel-devel)' />
+       </supplements>
+</package>
+
+<package>
+       <name>kernel-default-devel-debuginfo</name>
+       <vendor>openSUSE</vendor>
+       <history>
+       <update>
+               <arch>x86_64</arch>
+               <version>1</version><release>2</release>
+       </update>
+       </history>
+       <provides>
+               <dep name='kernel-default-devel-debuginfo' op='==' version='1' release='2' />
+       </provides>
+</package>
+
+<package>
+       <name>kernel-devel</name>
+       <vendor>openSUSE</vendor>
+       <history>
+       <update>
+               <arch>noarch</arch>
+               <version>1</version><release>2</release>
+       </update>
+       </history>
+       <provides>
+               <dep name='multiversion(kernel)' />
+               <dep name='kernel-devel' op='==' version='1' release='2' />
+       </provides>
+       <requires>
+               <dep name='kernel-macros' />
+       </requires>
+</package>
+
+<package>
+       <name>kernel-syms</name>
+       <vendor>openSUSE</vendor>
+       <history>
+       <update>
+               <arch>x86_64</arch>
+               <version>1</version><release>2</release>
+       </update>
+       </history>
+       <provides>
+               <dep name='multiversion(kernel)' />
+               <dep name='kernel-syms' op='==' version='1' release='2' />
+       </provides>
+       <requires>
+               <dep name='kernel-devel' op='==' version='1' release='2' />
+               <dep name='kernel-default-devel' op='==' version='1' release='2' />
+       </requires>
+</package>
+<!-- END KERNEL SET -->
+
+<!-- START KERNEL SET -->
+<package>
+       <name>kernel-default</name>
+       <vendor>openSUSE</vendor>
+       <history>
+       <update>
+               <arch>x86_64</arch>
+               <version>1</version><release>5</release>
+       </update>
+       </history>
+       <provides>
+               <dep name='multiversion(kernel)' />
+               <dep name='kernel-default-1-5' />
+               <dep name='kernel' op='==' version='1' release='5' />
+               <dep name='kernel-uname-r' op='==' version='1-5' release='default' />
+               <dep name='ksym(foobar)' op='==' version='abdcfee' />
+       </provides>
+       <recommends>
+               <dep name='kernel-firmware' />
+       </recommends>
+</package>
+
+<package>
+       <name>kernel-default-devel</name>
+       <vendor>openSUSE</vendor>
+       <history>
+       <update>
+               <arch>x86_64</arch>
+               <version>1</version><release>5</release>
+       </update>
+       </history>
+       <provides>
+               <dep name='multiversion(kernel)' />
+               <dep name='kernel-default-devel' op='==' version='1' release='5' />
+       </provides>
+       <requires>
+               <dep name='kernel-devel' op='==' version='1' release='5' />
+       </requires>
+       <supplements>
+               <dep name='packageand(kernel-default:kernel-devel)' />
+       </supplements>
+</package>
+
+<package>
+       <name>kernel-default-devel-debuginfo</name>
+       <vendor>openSUSE</vendor>
+       <history>
+       <update>
+               <arch>x86_64</arch>
+               <version>1</version><release>5</release>
+       </update>
+       </history>
+       <provides>
+               <dep name='kernel-default-devel-debuginfo' op='==' version='1' release='5' />
+       </provides>
+</package>
+
+<package>
+       <name>kernel-devel</name>
+       <vendor>openSUSE</vendor>
+       <history>
+       <update>
+               <arch>noarch</arch>
+               <version>1</version><release>5</release>
+       </update>
+       </history>
+       <provides>
+               <dep name='multiversion(kernel)' />
+               <dep name='kernel-devel' op='==' version='1' release='5' />
+       </provides>
+       <requires>
+               <dep name='kernel-macros' />
+       </requires>
+</package>
+
+<package>
+       <name>kernel-syms</name>
+       <vendor>openSUSE</vendor>
+       <buildtime>1570603549</buildtime>
+       <history>
+       <update>
+               <arch>x86_64</arch>
+               <version>1</version><release>5</release>
+       </update>
+       </history>
+       <provides>
+               <dep name='multiversion(kernel)' />
+               <dep name='kernel-syms' op='==' version='1' release='5' />
+       </provides>
+       <requires>
+               <dep name='kernel-devel' op='==' version='1' release='5' />
+               <dep name='kernel-default-devel' op='==' version='1' release='5' />
+       </requires>
+</package>
+
+<package>
+    <name>dummy-kmp-default</name>
+       <history>
+       <update>
+               <arch>x86_64</arch>
+               <version>1</version><release>0</release>
+       </update>
+       </history>
+       <provides>
+               <dep name='kmod(dummy)' />
+               <dep name='dummy-kmp-default' op='==' version='1' release='0' />
+       </provides>
+       <requires>
+               <dep name='kernel-default-devel'/>
+    <dep name='ksym(foobar)' op='==' version='abdcfee' />
+       </requires>
+</package>
+
+<package>
+    <name>package-needing-krnlmodule</name>
+       <history>
+       <update>
+               <arch>x86_64</arch>
+               <version>1</version><release>0</release>
+       </update>
+       </history>
+       <provides>
+               <dep name='package-needing-krnlmodule' op='==' version='1' release='0' />
+       </provides>
+       <requires>
+       <dep name='dummy-kmp-default' op='==' version='1' release='0' />
+       </requires>
+</package>
+
+<!-- END KERNEL SET -->
+
+
+</channel></subchannel>
diff --git a/tests/zypp/data/PurgeKernels/withdeps/solver-test.xml b/tests/zypp/data/PurgeKernels/withdeps/solver-test.xml
new file mode 100644 (file)
index 0000000..993d537
--- /dev/null
@@ -0,0 +1,8 @@
+<?xml version="1.0"?>
+<test>
+<setup arch="x86_64">
+       <system file="solver-system.xml"/>
+       <locale name="en_US" />
+       <locale name="de" />
+</setup>
+</test>
index a5204f6..f21adc3 100644 (file)
@@ -50,6 +50,32 @@ int usage( const std::string & msg_r = std::string(), int exit_r = 100 )
   return exit_r;
 }
 
+#define COL_R   "\033[0;31m"
+#define COL_G   "\033[0;32m"
+#define COL_B   "\033[0;34m"
+#define COL_C   "\033[0;36m"
+#define COL_M   "\033[0;35m"
+#define COL_Y   "\033[0;33m"
+#define COL_BL  "\033[0;30m"
+#define COL_WH  "\033[1;37m"
+#define COL_OFF "\033[0m"
+
+std::string colorId( sat::Solvable solv_r )
+{
+  // return solv_r.asString();
+  std::string ret;
+  if ( solv_r )
+  {
+    static str::Format fmt { COL_B "%s" COL_OFF "-" COL_G "%s" COL_OFF ".%s" };
+    ret = fmt % solv_r.name() % solv_r.edition() % solv_r.arch();
+  }
+  else
+  {
+    ret = ( solv_r.id() == sat::detail::systemSolvableId ?  "systemSolvable" : "noSolvable" );
+  }
+  return ret;
+}
+
 void tableOut( const std::string & s1 = std::string(),
                const std::string & s2 = std::string(),
                const std::string & s3 = std::string(),
@@ -241,38 +267,41 @@ int main( int argc, char * argv[] )
   {
     if ( (*argv)[0] == '-' )
     {
-      switch ( (*argv)[1] )
+      for ( const char * arg = (*argv)+1; *arg != '\0'; ++arg )        // -pr for -p -r
       {
-        case 'a': names =      true,   requires = provides =   true;   break;
-        case 'A': names =      true,   requires = provides =   false;  break;
-       case 'D':
-         if ( argc > 1 )
-         {
-           --argc,++argv;
-           dDump( *argv );
-         }
-         else
-           return errexit("-D <pkgspec> requires an argument.");
-         break;
-        case 'i': ignorecase = true;   break;
-        case 'I': ignorecase = false;  break;
-        case 'x': matechexact =        true;   break;
-        case 'n': names =      true;   break;
-        case 'N': names =      false;  break;
-        case 'r': requires =   true;   break;
-        case 'R': requires =   false;  break;
-        case 'p': provides =   true;   break;
-        case 'P': provides =   false;  break;
-        case 'c': conflicts =  true;   break;
-        case 'C': conflicts =  false;  break;
-        case 'o': obsoletes =  true;   break;
-        case 'O': obsoletes =  false;  break;
-        case 'm': recommends = true;   break;
-        case 'M': recommends = false;  break;
-        case 's': supplements =        true;   break;
-        case 'S': supplements =        false;  break;
-        case 'e': enhacements =        true;   break;
-        case 'E': enhacements =        false;  break;
+       switch ( *arg )
+       {
+         case 'a': names =             true,   requires = provides =   true;   break;
+         case 'A': names =             true,   requires = provides =   false;  break;
+         case 'D':
+           if ( argc > 1 )
+           {
+             --argc,++argv;
+             dDump( *argv );
+           }
+           else
+             return errexit("-D <pkgspec> requires an argument.");
+           break;
+         case 'i': ignorecase =        true;   break;
+         case 'I': ignorecase =        false;  break;
+         case 'x': matechexact =       true;   break;
+         case 'n': names =             true;   break;
+         case 'N': names =             false;  break;
+         case 'r': requires =          true;   break;
+         case 'R': requires =          false;  break;
+         case 'p': provides =          true;   break;
+         case 'P': provides =          false;  break;
+         case 'c': conflicts =         true;   break;
+         case 'C': conflicts =         false;  break;
+         case 'o': obsoletes =         true;   break;
+         case 'O': obsoletes =         false;  break;
+         case 'm': recommends =        true;   break;
+         case 'M': recommends =        false;  break;
+         case 's': supplements =       true;   break;
+         case 'S': supplements =       false;  break;
+         case 'e': enhacements =       true;   break;
+         case 'E': enhacements =       false;  break;
+       }
       }
       continue;
     }
@@ -365,7 +394,7 @@ int main( int argc, char * argv[] )
       if ( it->isKind( ResKind::srcpackage ) && !withSrcPackages )
        continue;
 
-      tableOut( str::numstring( it->id() ), it->asString(),
+      tableOut( str::numstring( it->id() ), colorId(*it),
                str::form( "(%d)%s", it->repository().info().priority(), it->repository().name().c_str() ),
                str::numstring( PoolItem(*it)->buildtime() ) );
       tableOut( "", "",
index 6cedd9c..2523d5a 100644 (file)
@@ -55,6 +55,7 @@ SET( zypp_SRCS
   ProgressData.cc
   ProvideFilePolicy.cc
   PublicKey.cc
+  PurgeKernels.cc
   Range.cc
   Rel.cc
   RepoInfo.cc
@@ -159,6 +160,7 @@ SET( zypp_HEADERS
   ProgressData.h
   ProvideFilePolicy.h
   PublicKey.h
+  PurgeKernels.h
   Range.h
   RelCompare.h
   Rel.h
index 4651e72..43059dd 100644 (file)
@@ -234,7 +234,7 @@ namespace zypp
   {
     return xmlout::node( str, name_r, {
       { "time_t",      Date::ValueType(obj) },
-      { "text",                obj.printISO() },
+      { "text",                obj.printISO( Date::TB_UTC ) },
     } );
   }
 
diff --git a/zypp/PurgeKernels.cc b/zypp/PurgeKernels.cc
new file mode 100644 (file)
index 0000000..cfe7c7e
--- /dev/null
@@ -0,0 +1,422 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/PurgeKernels.cc
+ *
+*/
+
+#include "zypp/base/String.h"
+#include "zypp/base/Logger.h"
+#include "zypp/base/Regex.h"
+#include "zypp/ui/Selectable.h"
+#include "zypp/PurgeKernels.h"
+#include "zypp/PoolQuery.h"
+#include "zypp/ResPool.h"
+#include "zypp/Resolver.h"
+#include "zypp/Filter.h"
+#include "zypp/ZConfig.h"
+
+#include <iostream>
+#include <fstream>
+#include <map>
+#include <unordered_map>
+#include <sys/utsname.h>
+
+#undef ZYPP_BASE_LOGGER_LOGGROUP
+#define ZYPP_BASE_LOGGER_LOGGROUP "PurgeKernels"
+
+namespace zypp {
+
+  struct PurgeKernels::Impl  {
+
+    Impl() {
+      struct utsname unameData;
+      if ( uname( &unameData) == 0 ) {
+        _kernelArch = Arch( unameData.machine );
+        _uname_r = std::string( unameData.release );
+      }
+    }
+
+    bool removePackageAndCheck( PoolItem &item, const str::regex &validRemovals ) const;
+    void parseKeepSpec();
+    void fillKeepList( const std::unordered_map< std::string, std::map< Arch, std::map<Edition, sat::Solvable> > > &installedKernels, std::set<sat::Solvable::IdType> &list ) const;
+    void cleanDevelAndSrcPackages ( const str::regex &validRemovals, std::set<Edition> &validEditions, const std::string &flavour = std::string() );
+
+    static std::string detectRunningKernel() {
+
+      std::string kernelVersion;
+      std::ifstream procKernel( "/proc/sys/kernel/osrelease" );
+      if ( procKernel ) {
+        std::getline( procKernel, kernelVersion );
+      }
+      return kernelVersion;
+
+    }
+
+
+    std::set<size_t>  _keepLatestOffsets = { 0 };
+    std::set<size_t>  _keepOldestOffsets;
+    std::set<Edition> _keepSpecificEditions;
+    std::string       _uname_r;
+    Arch              _kernelArch;
+    std::string       _keepSpec = ZConfig::instance().multiversionKernels();
+    bool              _keepRunning    = true;
+  };
+
+  /*!
+   * tries to remove a the \ref PoolItem \a pi from the pool, solves and checks if no unexpected packages are removed due to the \a validRemovals regex.
+   * If the constraint fails the changes are reverted and \a false is returned.
+   */
+  bool PurgeKernels::Impl::removePackageAndCheck( PoolItem &pi, const str::regex &validRemovals ) const
+  {
+    const filter::ByStatus toBeUninstalledFilter( &ResStatus::isToBeUninstalled );
+    auto pool = ResPool::instance();
+
+    //remember which packages are already marked for removal, we do not need to check them again
+    std::set< sat::Solvable::IdType> currentSetOfRemovals;
+    for ( auto it = pool.byStatusBegin( toBeUninstalledFilter ); it != pool.byStatusEnd( toBeUninstalledFilter );  it++  )
+      currentSetOfRemovals.insert( it->id() );
+
+    pi.status().setToBeUninstalled( ResStatus::USER );
+
+    if ( !pool.resolver().resolvePool() ) {
+      MIL << "Failed to resolve pool, skipping " << pi << std::endl;
+      pool.resolver().problems();
+      pi.statusReset();
+
+      return false;
+    }
+
+    for ( auto it = pool.byStatusBegin( toBeUninstalledFilter ); it != pool.byStatusEnd( toBeUninstalledFilter );  it++  ) {
+
+      //this was set by us or marked by a previous removal, ignore them
+      if ( it->status().isByUser() || (currentSetOfRemovals.find( it->id() ) != currentSetOfRemovals.end()) )
+        continue;
+
+      str::smatch what;
+      if ( !str::regex_match( it->name(), what, validRemovals) ) {
+        MIL << "Package " << PoolItem(*it) << " should not be removed, skipping " << pi << std::endl;
+        pi.statusReset();
+        return false;
+      }
+    }
+
+    MIL << "Removing package: " << pi << std::endl;
+    return true;
+  }
+
+  /*!
+   * Parse the config line keep spec that tells us which kernels should be kept
+   */
+  void PurgeKernels::Impl::parseKeepSpec( )
+  {
+    //keep spec parse regex, make sure to edit the group offsets if changing this regex
+    const str::regex specRegex( "^(latest|oldest)([+-][0-9]+)?$", str::regex::match_extended );
+
+    const unsigned tokenGrp = 1; //index of the group matching the token
+    const unsigned modifierGrp = 2; //index of the group matching the offset modifier
+
+
+    MIL << "Parsing keep spec: " << _keepSpec << std::endl;
+
+    std::vector<std::string> words;
+    str::split( _keepSpec, std::back_inserter(words), ",", str::TRIM );
+    if ( words.empty() ) {
+      WAR << "Invalid keep spec: " << _keepSpec << " using default latest,running." << std::endl;
+      return;
+    }
+
+    _keepRunning = false;
+    _keepLatestOffsets.clear();
+    _keepOldestOffsets.clear();
+
+    for ( const std::string &word : words ) {
+      if ( word == "running" ) {
+        _keepRunning = true;
+      } else {
+        str::smatch what;
+        if ( !str::regex_match( word, what, specRegex ) ) {
+          _keepSpecificEditions.insert( Edition(word) );
+          continue;
+        }
+
+        auto addKeepOff = []( const auto &off, auto &set, const auto &constraint ){
+          const off_t num = off.empty() ? 0 : str::strtonum<off_t>( off );
+          if ( !constraint(num) ) return false;
+          set.insert( static_cast<size_t>(std::abs(num)) );
+          return true;
+        };
+
+        if ( what[tokenGrp] == "oldest" ) {
+          addKeepOff( what[modifierGrp], _keepOldestOffsets, [ &word ]( off_t num ) {
+            if ( num < 0 ) {
+              WAR << "Ignoring invalid modifier in keep spec: " << word << ", oldest supports only positive modifiers." << std::endl;
+              return false;
+            }
+            return true;
+          });
+        } else {
+          addKeepOff( what[modifierGrp], _keepLatestOffsets, [ &word ]( off_t num ) {
+            if ( num > 0 ) {
+              WAR << "Ignoring invalid modifier in keep spec: " << word << ", latest supports only negative modifiers." << std::endl;
+              return false;
+            }
+            return true;
+          });
+        }
+      }
+    }
+  }
+
+  /*!
+   * Go over the list of installed kernels and mark those as "do not remove" that match
+   * a entry in the keep spec
+   */
+  void PurgeKernels::Impl::fillKeepList( const std::unordered_map<std::string, std::map<Arch, std::map<Edition, sat::Solvable> > > &installedKernels, std::set<sat::Solvable::IdType> &list ) const
+  {
+    for ( const auto &flavourMap : installedKernels ) {
+      for ( const auto &archMap : flavourMap.second ) {
+        size_t currOff = 0; //the current "oldest" offset ( runs from map start to end )
+        size_t currROff = archMap.second.size() - 1; // the current "latest" offset ( runs from map end to start )
+        for ( const auto &kernelMap : archMap.second ) {
+
+          //if we find one of the running offsets in the keepspec, we add the kernel id the the list of packages to keep
+          if (  _keepOldestOffsets.find( currOff ) != _keepOldestOffsets.end()
+               || _keepLatestOffsets.find( currROff ) != _keepLatestOffsets.end()
+               // a kernel might be explicitely locked by version
+               || _keepSpecificEditions.find( kernelMap.second.edition() ) != _keepSpecificEditions.end() ) {
+            MIL << "Marking kernel " << kernelMap.second << " as to keep." << std::endl;
+            list.insert( kernelMap.second.id() ) ;
+          }
+
+          currOff++;
+          currROff--;
+        }
+      }
+    }
+  }
+
+  /*!
+   * This cleans up the source and devel packages tree for a specific flavour.
+   */
+  void PurgeKernels::Impl::cleanDevelAndSrcPackages(const str::regex &validRemovals, std::set<Edition> &validEditions, const std::string &flavour )
+  {
+    bool isWithFlavour = flavour.size();
+
+    if ( isWithFlavour )
+      MIL << "Trying to remove source/devel packages for flavour " << flavour << std::endl;
+    else
+      MIL << "Trying to remove global/default source/devel packages "<< std::endl;
+
+    auto withFlavour = [&isWithFlavour, &flavour]( const std::string &name ) {
+      return isWithFlavour ? name+"-"+flavour : name;
+    };
+
+    //try to remove the kernel-devel-flavour and kernel-source-flavour packages
+    PoolQuery q;
+    q.addKind( zypp::ResKind::package );
+
+    q.addAttribute( sat::SolvAttr::name, withFlavour("kernel-devel") );
+    q.addAttribute( sat::SolvAttr::name, withFlavour("kernel-source") );
+    q.setInstalledOnly();
+    q.setMatchExact();
+
+    for ( auto installedSrcPck : q ) {
+
+      if ( validEditions.find( installedSrcPck.edition() ) == validEditions.end() ) {
+        MIL << "Skipping source package " << installedSrcPck <<  " no corresponding kernel with the same version was installed." << std::endl;
+        continue;
+      }
+
+      //if no package providing kernel-flavour = VERSION is installed , we are free to remove the package
+      PoolQuery instKrnl;
+      instKrnl.addKind( zypp::ResKind::package );
+      instKrnl.setInstalledOnly();
+      instKrnl.setMatchExact();
+      instKrnl.addDependency( sat::SolvAttr::provides, withFlavour("kernel"), Rel::EQ, installedSrcPck.edition() );
+
+      bool found = std::any_of ( instKrnl.begin(), instKrnl.end(), []( auto it ) {  return !PoolItem(it).status().isToBeUninstalled(); } );
+      if ( found ) {
+        MIL << "Skipping source package " << installedSrcPck << " binary packages with the same edition are still installed" << std::endl;
+        continue;
+      }
+
+      PoolItem pi( installedSrcPck );
+      removePackageAndCheck( pi, validRemovals );
+    }
+  }
+
+  PurgeKernels::PurgeKernels()
+    : _pimpl( new Impl() )
+  {
+
+  }
+
+  void PurgeKernels::markObsoleteKernels()
+  {
+    if ( _pimpl->_keepSpec.empty() )
+      return;
+
+    _pimpl->parseKeepSpec();
+
+    auto pool = ResPool::instance();
+    pool.resolver().setForceResolve( true ); // set allow uninstall flag
+
+    const filter::ByStatus toBeUninstalledFilter( &ResStatus::isToBeUninstalled );
+
+    //list of packages that are allowed to be removed automatically.
+    const str::regex validRemovals("(kernel-syms(-.*)?|kgraft-patch(-.*)?|kernel-livepatch(-.*)?|.*-kmp(-.*)?)");
+
+    //list of packages that are allowed to be removed automatically when uninstalling kernel-devel packages
+    const str::regex validDevelRemovals("(kernel-source(-.*)?|(kernel-syms(-.*)?)|(kernel-devel(-.*)?)|(kernel(-.*)?-devel))");
+
+    // kernel flavour regex
+    const str::regex kernelFlavourRegex("^kernel-(.*)$");
+
+    // the map of all installed kernels, grouped by Flavour -> Arch -> Version
+    std::unordered_map< std::string, std::map< Arch, std::map<Edition, sat::Solvable> > > installedKernels;
+
+    // the set of kernel package IDs that have to be kept always
+    std::set<sat::Solvable::IdType> packagesToKeep;
+
+    //collect the list of installed kernel packages
+    PoolQuery q;
+    q.addKind( zypp::ResKind::package );
+    q.addAttribute( sat::SolvAttr::provides, "kernel" );
+    q.setInstalledOnly();
+    q.setMatchExact();
+
+    MIL << "Searching for obsolete kernels." << std::endl;
+
+    for ( auto installedKernel : q ) {
+
+      MIL << "Found installed kernel " << installedKernel << std::endl;
+
+      //we can not simply skip the running kernel to make sure the keep-spec works correctly
+      if ( _pimpl->_keepRunning
+           && installedKernel.provides().matches( Capability( "kernel-uname-r", Rel::EQ, Edition( _pimpl->_uname_r ) ) )
+           && installedKernel.arch() == _pimpl->_kernelArch ) {
+        MIL << "Marking kernel " << installedKernel << " as to keep." << std::endl;
+        packagesToKeep.insert( installedKernel.id() );
+      }
+
+      str::smatch what;
+      str::regex_match( installedKernel.name(), what, kernelFlavourRegex );
+      if ( what[1].empty() ) {
+        WAR << "Could not detect kernel flavour for: " << installedKernel << " ...skipping" << std::endl;
+        continue;
+      }
+
+      const std::string flavour = what[1];
+      if ( !installedKernels.count( flavour ) )
+        installedKernels.insert( std::make_pair( flavour, std::map< Arch, std::map<Edition, sat::Solvable> > {} ) );
+
+      auto &flavourMap = installedKernels[ flavour ];
+      if ( !flavourMap.count( installedKernel.arch() ) )
+        flavourMap.insert( std::make_pair( installedKernel.arch(), std::map<Edition, sat::Solvable>{} ) );
+
+      flavourMap[ installedKernel.arch() ].insert( std::make_pair( installedKernel.edition(), installedKernel ) );
+    }
+
+    _pimpl->fillKeepList( installedKernels, packagesToKeep );
+
+
+    MIL << "Starting to remove obsolete kernels." << std::endl;
+
+
+    std::set<Edition> removedVersions;
+
+    /*
+     * If there is a KMP or livepatch depending on the package remove it as well. If
+     * there is another package depending on the kernel keep the kernel. If there is
+     * a package that depends on a KMP keep the KMP and a kernel required to use the
+     * KMP.
+     */
+    for ( const auto &flavourMap : installedKernels ) {
+
+      // collect all removed versions of this edition
+      std::set<Edition> removedFlavourVersions;
+
+      for ( const auto &archMap : flavourMap.second ) {
+        for ( const auto &kernelMap : archMap.second ) {
+          auto &installedKernel = kernelMap.second;
+
+          // if the kernel is locked by the user, its not removed automatically
+          if ( ui::asSelectable()( installedKernel )->hasLocks() )
+            continue;
+
+          // this package is in the keep spec, do not touch
+          if ( packagesToKeep.count( installedKernel.id() ) )
+            continue;
+
+          // try to remove the kernel package, check afterwards if only expected packages have been removed
+          PoolItem pi( installedKernel );
+          if ( !_pimpl->removePackageAndCheck( pi, validRemovals ) ) {
+            continue;
+          }
+
+          removedFlavourVersions.insert( installedKernel.edition() );
+
+          //lets remove the kernel-flavour-devel package too
+          PoolQuery develPckQ;
+          develPckQ.addKind( zypp::ResKind::package );
+          develPckQ.addDependency( sat::SolvAttr::name, installedKernel.name()+"-devel", Rel::EQ, installedKernel.edition() );
+          develPckQ.addDependency( sat::SolvAttr::name, installedKernel.name()+"-devel-debuginfo", Rel::EQ, installedKernel.edition() );
+          develPckQ.setInstalledOnly();
+          develPckQ.setMatchExact();
+
+          for ( auto krnlDevPck : develPckQ ) {
+
+            if ( krnlDevPck.arch() != installedKernel.arch() )
+              continue;
+
+            PoolItem devPi(krnlDevPck);
+            _pimpl->removePackageAndCheck( devPi, validDevelRemovals );
+          }
+        }
+      }
+      //try to remove the kernel-devel-flavour and kernel-source-flavour packages
+      _pimpl->cleanDevelAndSrcPackages( validDevelRemovals, removedFlavourVersions, flavourMap.first );
+      removedVersions.insert( removedFlavourVersions.begin(), removedFlavourVersions.end() );
+    }
+
+    // clean the global -devel and -source packages
+    _pimpl->cleanDevelAndSrcPackages( validDevelRemovals, removedVersions );
+  }
+
+  void PurgeKernels::setUnameR( const std::string &val )
+  {
+    _pimpl->_uname_r = val;
+  }
+
+  std::string PurgeKernels::unameR() const
+  {
+    return _pimpl->_uname_r;
+  }
+
+  void PurgeKernels::setKernelArch(const Arch &arch)
+  {
+    _pimpl->_kernelArch = arch;
+  }
+
+  Arch PurgeKernels::kernelArch() const
+  {
+    return _pimpl->_kernelArch;
+  }
+
+  void PurgeKernels::setKeepSpec( const std::string &val )
+  {
+    _pimpl->_keepSpec = val;
+  }
+
+  std::string PurgeKernels::keepSpec() const
+  {
+    return _pimpl->_keepSpec;
+  }
+
+}
diff --git a/zypp/PurgeKernels.h b/zypp/PurgeKernels.h
new file mode 100644 (file)
index 0000000..c0034d5
--- /dev/null
@@ -0,0 +1,72 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/PurgeKernels.h
+ *
+*/
+
+#include <zypp/PoolItem.h>
+#include <zypp/base/PtrTypes.h>
+
+namespace zypp {
+
+  namespace str {
+  class regex;
+  }
+
+  /*!
+   * Implements the logic of the "purge-kernels" command.
+   *
+   */
+  class PurgeKernels
+  {
+  public:
+    PurgeKernels();
+
+
+    /*!
+     * Marks all currently obsolete Kernels according to the keep spec.
+     * \note This will not commit the changes
+     */
+    void markObsoleteKernels();
+
+    /*!
+     * Force a specific uname to be set, only used for testing,
+     * in production the running kernel is detected.
+     */
+    void setUnameR( const std::string &val );
+    std::string unameR() const;
+
+
+    /*!
+     * Force a specific kernel arch to be set, only used for testing,
+     * in production the running kernel arch is detected.
+     */
+    void setKernelArch( const zypp::Arch &arch );
+    Arch kernelArch() const;
+
+    /*!
+       * Overrides the keep spec, the default value is read from ZConfig.
+       * The keep spec is a string of tokens seperated by ",".
+       * It only supports 3 different tokens:
+       *  - "running" matches only the currently running kernel of the system
+       *  - "oldest"  matches the kernel version for each flavour/arch combination with the lowest edition
+       *              can be modified with a positive number:  oldest+n
+       *  - "latest"  matches the kernel version for each flavour/arch combination with the highest edition
+       *              can be modified with a negative number:  latest-n
+       */
+    void setKeepSpec( const std::string &val );
+    std::string keepSpec () const;
+
+    struct Impl;
+  private:
+    RW_pointer<Impl> _pimpl;
+  };
+
+}
+
index 607c90e..ed80fa2 100644 (file)
@@ -495,6 +495,10 @@ namespace zypp
                 {
                   cfg_multiversion_path = Pathname(value);
                 }
+                else if ( entry == "multiversion.kernels" )
+                {
+                  cfg_kernel_keep_spec = value;
+                }
                 else if ( entry == "solver.focus" )
                 {
                  fromString( value, solver_focus );
@@ -637,6 +641,7 @@ namespace zypp
 
     Pathname cfg_vendor_path;
     Pathname cfg_multiversion_path;
+    std::string cfg_kernel_keep_spec;
     Pathname locks_file;
 
     Pathname update_data_path;
@@ -1182,6 +1187,11 @@ namespace zypp
   Pathname ZConfig::pluginsPath() const
   { return _pimpl->pluginsPath.get(); }
 
+  string ZConfig::multiversionKernels() const
+  {
+    return _pimpl->cfg_kernel_keep_spec;
+  }
+
   ///////////////////////////////////////////////////////////////////
 
   std::ostream & ZConfig::about( std::ostream & str ) const
index 73a1a68..884047c 100644 (file)
@@ -534,6 +534,11 @@ namespace zypp
        */
       Pathname pluginsPath() const;
 
+      /*!
+       * Defaults to a empty string, if no keep spec is defined no kernels are removed
+       */
+      std::string multiversionKernels() const;
+
       //@}
     public:
       class Impl;
index f4deb90..8486b8f 100644 (file)
@@ -263,6 +263,18 @@ namespace zypp
     inline typename MapKVIteratorTraits<TMap>::Value_const_iterator make_map_value_upper_bound( const TMap & map_r, const typename TMap::key_type & key_r )
     { return make_transform_iterator( map_r.upper_bound( key_r ), GetPairSecond<typename TMap::value_type>() ); }
 
+
+  /** Convenience to create an \ref Iterable over the container keys */
+  template<class TMap>
+  inline Iterable<typename MapKVIteratorTraits<TMap>::Key_const_iterator> make_map_key_Iterable( const TMap & map_r )
+  { return makeIterable( make_map_key_begin( map_r ), make_map_key_end( map_r ) ); }
+
+  /** Convenience to create an \ref Iterable over the container values */
+  template<class TMap>
+  inline Iterable<typename MapKVIteratorTraits<TMap>::Value_const_iterator> make_map_value_Iterable( const TMap & map_r )
+  { return makeIterable( make_map_value_begin( map_r ), make_map_value_end( map_r ) ); }
+
+
   /** \class function_output_iterator
    * An output iterator wrapping a unary function object; each time an
    * element is written into the dereferenced iterator, it is passed as
index 27c4536..776c2fa 100644 (file)
@@ -487,6 +487,33 @@ namespace zypp
     }
 
     ///////////////////////////////////////////////////////////////////
+    /** \name Trimming whitepace.
+     * \todo optimize l/r trim.
+    */
+    //@{
+    /** To define how to trim. */
+    enum Trim {
+      NO_TRIM = 0x00,
+      L_TRIM  = 0x01,
+      R_TRIM  = 0x02,
+      TRIM    = (L_TRIM|R_TRIM)
+    };
+
+    std::string trim( const std::string & s, const Trim trim_r = TRIM );
+    std::string trim( std::string && s, const Trim trim_r = TRIM );
+
+    inline std::string ltrim( const std::string & s )
+    { return trim( s, L_TRIM ); }
+    inline std::string ltrim( std::string && s )
+    { return trim( std::move(s), L_TRIM ); }
+
+    inline std::string rtrim( const std::string & s )
+    { return trim( s, R_TRIM ); }
+    inline std::string rtrim( std::string && s )
+    { return trim( std::move(s), R_TRIM ); }
+    //@}
+
+    ///////////////////////////////////////////////////////////////////
     /** \name Split. */
     //@{
     /** Split \a line_r into words.
@@ -499,7 +526,7 @@ namespace zypp
      *
     */
     template<class TOutputIterator>
-      unsigned split( const C_Str & line_r, TOutputIterator result_r, const C_Str & sepchars_r = " \t" )
+      unsigned split( const C_Str & line_r, TOutputIterator result_r, const C_Str & sepchars_r = " \t", const Trim trim_r = NO_TRIM )
       {
         const char * beg = line_r;
         const char * cur = beg;
@@ -513,7 +540,7 @@ namespace zypp
             while( *cur && !::strchr( sepchars_r, *cur ) )
               ++cur;
             // build string
-            *result_r = std::string( beg, cur-beg );
+            *result_r = trim( std::string( beg, cur-beg ), trim_r );
             // skip sepchars
             while ( *cur && ::strchr( sepchars_r, *cur ) )
               ++cur;
@@ -521,6 +548,11 @@ namespace zypp
         return ret;
       }
 
+    template<class TOutputIterator>
+      unsigned split( const C_Str & line_r, TOutputIterator result_r, const Trim trim_r )
+      { return split( line_r, result_r, " \t", trim_r ); }
+
+
     /** Split \a line_r into words with respect to escape delimeters.
      * Any sequence of characters in \a sepchars_r is treated as
      * delimiter if not inside \c "" or \c '' or escaped by \c \.
@@ -961,33 +993,6 @@ namespace zypp
     { return ::strcasestr( str_r, val_r ); }
     //@}
 
-    ///////////////////////////////////////////////////////////////////
-    /** \name Trimming whitepace.
-     * \todo optimize l/r trim.
-    */
-    //@{
-    /** To define how to trim. */
-    enum Trim {
-      NO_TRIM = 0x00,
-      L_TRIM  = 0x01,
-      R_TRIM  = 0x02,
-      TRIM    = (L_TRIM|R_TRIM)
-    };
-
-    std::string trim( const std::string & s, const Trim trim_r = TRIM );
-    std::string trim( std::string && s, const Trim trim_r = TRIM );
-
-    inline std::string ltrim( const std::string & s )
-    { return trim( s, L_TRIM ); }
-    inline std::string ltrim( std::string && s )
-    { return trim( std::move(s), L_TRIM ); }
-
-    inline std::string rtrim( const std::string & s )
-    { return trim( s, R_TRIM ); }
-    inline std::string rtrim( std::string && s )
-    { return trim( std::move(s), R_TRIM ); }
-    //@}
-
     std::string stripFirstWord( std::string & line, const bool ltrim_first = true );
 
     std::string stripLastWord( std::string & line, const bool rtrim_first = true );
index b9f42b3..f01de1b 100644 (file)
@@ -522,10 +522,10 @@ namespace zypp
            { localeIds.removed().insert( IdString(lang) ); }
          }
 
-         // Assert that TrackedLocaleIds::current is not empty.
-         // If, so fill in LanguageCode::enCode as last resort.
-         if ( localeIds.current().empty() )
-         { localeIds.current().insert( IdString(Locale::enCode) ); }
+         // bsc#1155678: We try to differ between an empty RequestedLocales
+         // and one containing 'en' (explicit or as fallback). An empty RequestedLocales
+         // should not even drag in recommended 'en' packages. So we no longer enforce
+         // 'en' being in the set.
        }
        return *_trackedLocaleIdsPtr;
       }
index 0d98e19..0fd2b4c 100644 (file)
@@ -645,6 +645,16 @@ SATResolver::solverInit(const PoolItemList & weakItems)
         queue_push( &(_jobQueue), id );
     }
 
+    // Ad rules for retracted pathces and packages
+    {
+      static const IdString retractedToken { "retracted-patch-package()" };
+      static const IdString ptfToken { "ptf()" };
+      queue_push( &(_jobQueue), SOLVER_BLACKLIST|SOLVER_SOLVABLE_PROVIDES );
+      queue_push( &(_jobQueue), retractedToken.id() );
+      queue_push( &(_jobQueue), SOLVER_BLACKLIST|SOLVER_SOLVABLE_PROVIDES );
+      queue_push( &(_jobQueue), ptfToken.id() );
+    }
+
     // Ad rules for changed requestedLocales
     {
       const auto & trackedLocaleIds( myPool().trackedLocaleIds() );
index d449677..fcc6bb5 100644 (file)
@@ -21,6 +21,7 @@
 #include "zypp/ZYppCallbacks.h"
 #include "zypp/ExternalProgram.h"
 #include "zypp/target/rpm/RpmHeader.h"
+#include "zypp/target/rpm/librpmDb.h"
 #include "zypp/ZConfig.h"
 #include "zypp/ZYppCallbacks.h"
 
@@ -73,8 +74,8 @@ namespace zypp
            out << "#! " << pkg->tag_posttransprog() << endl
                << pkg->tag_posttrans() << endl;
          }
-         _scripts.push_back( script.path().basename() );
-         MIL << "COLLECT posttrans: " << PathInfo( script.path() ) << endl;
+          _scripts.push_back( std::make_pair( script.path().basename(), pkg->tag_name() ) );
+          MIL << "COLLECT posttrans: '" << PathInfo( script.path() ) << "' for package: '" << pkg->tag_name() << "'" << endl;
          //DBG << "PROG:  " << pkg->tag_posttransprog() << endl;
          //DBG << "SCRPT: " << pkg->tag_posttrans() << endl;
          return true;
@@ -97,7 +98,8 @@ namespace zypp
          bool firstScript = true;
          while ( ! _scripts.empty() )
          {
-           const std::string & script = _scripts.front();
+           const auto &scriptPair = _scripts.front();
+           const std::string & script = scriptPair.first;
            const std::string & pkgident( script.substr( 0, script.size()-6 ) );        // strip tmp file suffix
 
            scriptProgress.name( str::Format(_("Executing %%posttrans script '%1%'")) % pkgident );
@@ -119,8 +121,13 @@ namespace zypp
              return false;
            }
 
-           MIL << "EXECUTE posttrans: " << script << endl;
-           ExternalProgram prog( (noRootScriptDir/script).asString() + " 0", ExternalProgram::Stderr_To_Stdout, false, -1, true, _root );
+            int npkgs = 0;
+            rpm::librpmDb::db_const_iterator it;
+            for ( it.findByName( scriptPair.second ); *it; ++it )
+              npkgs++;
+
+            MIL << "EXECUTE posttrans: " << script << " with argument: " << npkgs << endl;
+            ExternalProgram prog( (noRootScriptDir/script).asString() + " " +str::numstring( npkgs ), ExternalProgram::Stderr_To_Stdout, false, -1, true, _root );
 
            str::Str collect;
            for( std::string line = prog.receiveLine(); ! line.empty(); line = prog.receiveLine() )
@@ -176,8 +183,8 @@ namespace zypp
          msg << "%posttrans scripts skipped while aborting:\n";
          for ( const auto & script : _scripts )
          {
-           const std::string & pkgident( script.substr( 0, script.size()-6 ) );        // strip tmp file suffix
-           WAR << "UNEXECUTED posttrans: " << script << endl;
+           const std::string & pkgident( script.first.substr( 0, script.first.size()-6 ) );    // strip tmp file suffix
+           WAR << "UNEXECUTED posttrans: " << script.first << endl;
            msg << "    " << pkgident << "\n";
          }
 
@@ -199,7 +206,7 @@ namespace zypp
 
       private:
        Pathname _root;
-       std::list<std::string> _scripts;
+        std::list< std::pair< std::string, std::string > > _scripts;
        boost::scoped_ptr<filesystem::TmpDir> _ptrTmpdir;
     };