b10bbcc824f3e5bb3ebd0f8b9025991fd2193649
[platform/upstream/libzypp.git] / tests / lib / TestSetup.h
1 #ifndef INCLUDE_TESTSETUP
2 #define INCLUDE_TESTSETUP
3 #include <iostream>
4
5 #ifndef INCLUDE_TESTSETUP_WITHOUT_BOOST
6 #include <boost/test/auto_unit_test.hpp>
7 using boost::unit_test::test_case;
8 #endif
9
10 #include <zypp/base/LogControl.h>
11 #include <zypp/base/LogTools.h>
12 #include <zypp/base/InputStream.h>
13 #include <zypp/base/IOStream.h>
14 #include <zypp/base/Flags.h>
15 #include <zypp/ZYppFactory.h>
16 #include <zypp/ZYpp.h>
17 #include <zypp/TmpPath.h>
18 #include <zypp/Glob.h>
19 #include <zypp/PathInfo.h>
20 #include <zypp/RepoManager.h>
21 #include <zypp/Target.h>
22 #include <zypp/ResPool.h>
23 #include <zypp/misc/LoadTestcase.h>
24
25 using std::cin;
26 using std::cout;
27 using std::cerr;
28 using std::endl;
29 using std::flush;
30 using namespace zypp;
31
32 #ifndef BOOST_CHECK_NE
33 #define BOOST_CHECK_NE( L, R ) BOOST_CHECK( (L) != (R) )
34 #endif
35
36 #define LABELED(V) #V << ":\t" << V
37
38 enum TestSetupOptionBits
39 {
40   TSO_CLEANROOT         = (1 <<  0),    // wipe rootdir in ctor
41   TSO_REPO_DEFAULT_GPG  = (1 <<  1),    // dont turn off gpgcheck in repos
42 };
43 ZYPP_DECLARE_FLAGS_AND_OPERATORS( TestSetupOptions, TestSetupOptionBits );
44
45 /** Build a test environment below a temp. root directory.
46  * If a \c rootdir_r was provided to the ctor, this directory
47  * will be used and it will \b not be removed.
48  *
49  * \note The lifetime of this objects is the lifetime of the temp. root directory.
50  *
51  * \code
52  * #include "TestSetup.h"
53  *
54  * BOOST_AUTO_TEST_CASE(WhatProvides)
55  * {
56  *   // enabls loging fot the scope of this block:
57  *   // base::LogControl::TmpLineWriter shutUp( new log::FileLineWriter( "/tmp/YLOG" ) );
58  *
59  *   TestSetup test( Arch_x86_64 );
60  *   // test.loadTarget(); // initialize and load target
61  *   test.loadRepo( TESTS_SRC_DIR"/data/openSUSE-11.1" );
62  *
63  *   // Here the pool is ready to be used.
64  *
65  * }
66  * \endcode
67 */
68 class TestSetup
69 {
70 public:
71   typedef TestSetupOptions Options;
72
73 public:
74   struct InitLaterType {};
75   static constexpr InitLaterType initLater = InitLaterType();
76
77   TestSetup( InitLaterType )
78   {}
79
80   TestSetup( const Arch & sysarch_r = Arch_empty, const Options & options_r = Options() )
81     : _pimpl { new Impl( Pathname(), sysarch_r, options_r ) }
82   {}
83
84   TestSetup( const Pathname & rootdir_r, const Arch & sysarch_r = Arch_empty, const Options & options_r = Options() )
85     : _pimpl { new Impl( rootdir_r, sysarch_r, options_r ) }
86   {}
87
88   TestSetup( const Pathname & rootdir_r, const Options & options_r )
89     : _pimpl { new Impl( rootdir_r, Arch_empty, options_r ) }
90   {}
91
92   void reset()
93   { _pimpl.reset(); }
94
95 public:
96   /** Whether directory \a path_r contains a solver testcase. */
97   static bool isTestcase( const Pathname & path_r )
98   {
99     return  zypp::misc::testcase::LoadTestcase::None != zypp::misc::testcase::LoadTestcase::testcaseTypeAt( path_r );
100   }
101
102   /** Whether directory \a path_r contains a testsetup. */
103   static bool isTestSetup( const Pathname & path_r )
104   {
105     return filesystem::PathInfo( path_r / "repos.d" ).isDir() && filesystem::PathInfo( path_r / "raw" ).isDir();
106   }
107
108 public:
109   const Pathname & root() const { return _pimpl->_rootdir; }
110
111   Target &     target()      { if ( ! getZYpp()->getTarget() ) getZYpp()->initializeTarget( _pimpl->_rootdir ); return *getZYpp()->getTarget(); }
112   RepoManager  repomanager() { return RepoManager( RepoManagerOptions::makeTestSetup( _pimpl->_rootdir ) ); }
113   ResPool      pool()        { return ResPool::instance(); }
114   ResPoolProxy poolProxy()   { return pool().proxy(); }
115   sat::Pool    satpool()     { return sat::Pool::instance(); }
116   Resolver &   resolver()    { return *getZYpp()->resolver(); }
117
118 public:
119   /** Load target repo. */
120   void loadTarget()
121   { target().load(); }
122   /** Fake @System repo from url. */
123   void loadTargetRepo( const Url & url_r )
124   { loadRepo( url_r, sat::Pool::systemRepoAlias() ); }
125   /** Fake @System repo from Path. */
126   void loadTargetRepo( const Pathname & path_r )
127   { loadRepo( path_r, sat::Pool::systemRepoAlias() ); }
128   /** Fake @System repo from helix repo. */
129   void loadTargetHelix( const Pathname & path_r )
130   { loadHelix( path_r, sat::Pool::systemRepoAlias() ); }
131
132 public:
133   /** Directly load repoinfo to pool. */
134   void loadRepo( RepoInfo nrepo )
135   {
136     RepoManager rmanager( repomanager() );
137     if ( rmanager.hasRepo( nrepo ) )
138       nrepo.setAlias( RepoManager::makeStupidAlias( nrepo.url() ) );
139     rmanager.addRepository( nrepo );
140     rmanager.buildCache( nrepo );
141     rmanager.loadFromCache( nrepo );
142   }
143   /** Directly load repo from url to pool. */
144   void loadRepo( const Url & url_r, const std::string & alias_r = std::string() )
145   {
146     RepoInfo nrepo;
147     nrepo.setAlias( alias_r.empty() ? url_r.getHost()+":"+Pathname::basename(url_r.getPathName()) : alias_r );
148     nrepo.addBaseUrl( url_r );
149     if ( ! _pimpl->_options.testFlag( TSO_REPO_DEFAULT_GPG ) )
150       nrepo.setGpgCheck( false );
151     loadRepo( nrepo );
152   }
153   /** Directly load repo from metadata(dir) or solvfile(file) to pool.
154      * An empty alias is guessed.
155     */
156   void loadRepo( const Pathname & path_r, const std::string & alias_r = std::string() )
157   {
158     if ( filesystem::PathInfo( path_r ).isDir() )
159     {
160       loadRepo( path_r.asUrl(), alias_r );
161       return;
162     }
163     // .solv file is loaded directly using a faked RepoInfo
164     RepoInfo nrepo;
165     nrepo.setAlias( alias_r.empty() ? path_r.basename() : alias_r );
166     satpool().addRepoSolv( path_r, nrepo );
167   }
168   /** Directly load repo from some location (url or absolute(!)path).
169      * An empty alias is guessed.
170     */
171   void loadRepo( const std::string & loc_r, const std::string & alias_r = std::string() )
172   {
173     if ( *loc_r.c_str() == '/' )
174     {
175       loadRepo( Pathname( loc_r ), alias_r );
176     }
177     else
178     {
179       loadRepo( Url( loc_r ), alias_r );
180     }
181   }
182   /** Directly load repo from some location (url or absolute(!)path).
183      * An empty alias is guessed.
184     */
185   void loadRepo( const char * loc_r, const std::string & alias_r = std::string() )
186   { loadRepo( std::string( loc_r ? loc_r : "" ), alias_r ); }
187
188   /** Directly load a helix repo from some testcase.
189    ** An empty alias is guessed.
190    **/
191   void loadHelix( const Pathname & path_r, const std::string & alias_r = std::string() )
192   {
193     // .solv file is loaded directly using a faked RepoInfo
194     RepoInfo nrepo;
195     nrepo.setAlias( alias_r.empty() ? path_r.basename() : alias_r );
196     satpool().addRepoHelix( path_r, nrepo );
197   }
198
199   // Load repos included in a solver testcase.
200   void loadTestcaseRepos( const Pathname & path_r )
201   {
202     zypp::misc::testcase::LoadTestcase loader;
203     std::string err;
204     if (!loader.loadTestcaseAt( path_r, &err ) ) {
205       ZYPP_THROW( Exception(err) );
206     }
207
208     const auto &setup = loader.setupInfo();
209     auto tempRepoManager = repomanager();
210     if ( !setup.applySetup( tempRepoManager ) ) {
211       ZYPP_THROW( Exception("Failed to apply setup!") );
212     }
213
214     {
215       base::SetTracker<LocaleSet> localesTracker = setup.localesTracker;
216       localesTracker.removed().insert( localesTracker.current().begin(), localesTracker.current().end() );
217       satpool().initRequestedLocales( localesTracker.removed() );
218
219       localesTracker.added().insert( localesTracker.current().begin(), localesTracker.current().end() );
220       satpool().setRequestedLocales( localesTracker.added() );
221     }
222     poolProxy(); // prepare
223   }
224
225 public:
226   /** Load all enabled repos in repos.d to pool. */
227   void loadRepos()
228   {
229     RepoManager repoManager( repomanager() );
230     RepoInfoList repos = repoManager.knownRepositories();
231     for ( RepoInfoList::iterator it = repos.begin(); it != repos.end(); ++it )
232     {
233       RepoInfo & nrepo( *it );
234       USR << nrepo << endl;
235
236       if ( ! nrepo.enabled() )
237         continue;
238
239       if ( ! repoManager.isCached( nrepo ) || nrepo.type() == repo::RepoType::RPMPLAINDIR )
240       {
241         if ( repoManager.isCached( nrepo ) )
242         {
243           USR << "cleanCache" << endl;
244           repoManager.cleanCache( nrepo );
245         }
246         //USR << "refreshMetadata" << endl;
247         //repoManager.refreshMetadata( nrepo );
248         USR << "buildCache" << endl;
249         repoManager.buildCache( nrepo );
250       }
251       USR << "Create from cache" << endl;
252       repoManager.loadFromCache( nrepo );
253     }
254   }
255
256 public:
257   /** Detect and load the system located at \a sysRoot.
258      *
259      * \a sysRoot needs to be a directory containing either a SolverTestcase,
260      * a TestSetup system or a real system. The  provided repostitories are
261      * loaded into the pool (without refresh).
262     */
263   static void LoadSystemAt( const Pathname & sysRoot, const Arch & _testSetupArch_r = Arch_x86_64 )
264   {
265     if ( ! PathInfo( sysRoot ).isDir() )
266       ZYPP_THROW( Exception("sysRoot argument needs to be a directory") );
267
268     if ( TestSetup::isTestcase( sysRoot ) )
269     {
270       USR << str::form( "*** Load Testcase from '%s'", sysRoot.c_str() ) << endl;
271       TestSetup test;
272       test.loadTestcaseRepos( sysRoot );
273     }
274     else if ( TestSetup::isTestSetup( sysRoot ) )
275     {
276       USR << str::form( "*** Load TestSetup from '%s'", sysRoot.c_str() ) << endl;
277
278       TestSetup test( sysRoot, _testSetupArch_r );
279       test.loadRepos();
280
281       Pathname solvCachePath( RepoManagerOptions::makeTestSetup( test.root() ).repoSolvCachePath );
282       Pathname fakeTargetSolv( solvCachePath / sat::Pool::systemRepoAlias() / "solv" );
283       if ( PathInfo( fakeTargetSolv ).isFile() )
284       {
285         USR << str::form( "*** Fake TestSetup Target from '%s'", fakeTargetSolv.c_str() ) << endl;
286         test.target();
287         test.loadTargetRepo( fakeTargetSolv );
288       }
289     }
290     else
291     {
292       sat::Pool satpool( sat::Pool::instance() );
293       // a system
294       USR << str::form( "*** Load system at '%s'", sysRoot.c_str() ) << endl;
295       if ( 1 )
296       {
297         USR << "*** load target '" << Repository::systemRepoAlias() << "'\t" << endl;
298         getZYpp()->initializeTarget( sysRoot );
299         getZYpp()->target()->load();
300         USR << satpool.systemRepo() << endl;
301       }
302
303       if ( 1 )
304       {
305         RepoManager repoManager( sysRoot );
306         RepoInfoList repos = repoManager.knownRepositories();
307         for_( it, repos.begin(), repos.end() )
308         {
309           RepoInfo & nrepo( *it );
310
311           if ( ! nrepo.enabled() )
312             continue;
313
314           if ( ! repoManager.isCached( nrepo ) )
315           {
316             USR << str::form( "*** omit uncached repo '%s' (do 'zypper refresh')", nrepo.name().c_str() ) << endl;
317             continue;
318           }
319
320           USR << str::form( "*** load repo '%s'\t", nrepo.name().c_str() ) << flush;
321           try
322           {
323             repoManager.loadFromCache( nrepo );
324             USR << satpool.reposFind( nrepo.alias() ) << endl;
325           }
326           catch ( const Exception & exp )
327           {
328             USR << exp.asString() + "\n" + exp.historyAsString() << endl;
329             USR << str::form( "*** omit broken repo '%s' (do 'zypper refresh')", nrepo.name().c_str() ) << endl;
330             continue;
331           }
332         }
333       }
334     }
335   }
336
337 private:
338   struct Impl
339   {
340     Impl( const Pathname & rootdir_r, const Arch & sysarch_r, const Options & options_r )
341     {
342       _options = options_r;
343
344       if ( rootdir_r.empty() )
345         _rootdir = _tmprootdir.path();
346       else
347       {
348         filesystem::assert_dir( (_rootdir = rootdir_r) );
349         if ( _options.testFlag( TSO_CLEANROOT ) )
350           filesystem::clean_dir( _rootdir );
351       }
352
353       // erase any old pool content...
354       getZYpp()->finishTarget();
355       sat::Pool::instance().reposEraseAll();
356       // prepare for the new one...
357       ZConfig::instance().setRepoManagerRoot( _rootdir );
358
359       if ( ! sysarch_r.empty() )
360         ZConfig::instance().setSystemArchitecture( sysarch_r );
361       USR << "CREATED TESTSETUP below " << _rootdir << endl;
362     }
363
364     ~Impl()
365     { USR << (_tmprootdir.path() == _rootdir ? "DELETE" : "KEEP") << " TESTSETUP below " << _rootdir << endl; }
366
367     filesystem::TmpDir _tmprootdir;
368     Pathname           _rootdir;
369     Options            _options;
370   };
371
372   std::unique_ptr<Impl> _pimpl; // maybe worth creating RW_pointer traits for it
373 };
374
375
376 #endif //INCLUDE_TESTSETUP