Imported Upstream version 14.45.0
[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
24 using std::cin;
25 using std::cout;
26 using std::cerr;
27 using std::endl;
28 using std::flush;
29 using namespace zypp;
30
31 #ifndef BOOST_CHECK_NE
32 #define BOOST_CHECK_NE( L, R ) BOOST_CHECK( (L) != (R) )
33 #endif
34
35 #define LABELED(V) #V << ":\t" << V
36
37 inline std::string getXmlNodeVal( const std::string & line_r, const std::string & node_r )
38 {
39   std::string::size_type pos = line_r.find( node_r + "=\"" );
40   if ( pos != std::string::npos )
41   {
42     pos += node_r.size() + 2;
43     std::string::size_type epos = line_r.find( "\"", pos );
44     return line_r.substr( pos, epos-pos );
45   }
46   return std::string();
47 }
48
49 enum TestSetupOptionBits
50 {
51   TSO_CLEANROOT = (1 <<  0)
52 };
53 ZYPP_DECLARE_FLAGS_AND_OPERATORS( TestSetupOptions, TestSetupOptionBits );
54
55 /** Build a test environment below a temp. root directory.
56  * If a \c rootdir_r was provided to the ctor, this directory
57  * will be used and it will \b not be removed.
58  *
59  * \note The lifetime of this objects is the lifetime of the temp. root directory.
60  *
61  * \code
62  * #include "TestSetup.h"
63  *
64  * BOOST_AUTO_TEST_CASE(WhatProvides)
65  * {
66  *   // enabls loging fot the scope of this block:
67  *   // base::LogControl::TmpLineWriter shutUp( new log::FileLineWriter( "/tmp/YLOG" ) );
68  *
69  *   TestSetup test( Arch_x86_64 );
70  *   // test.loadTarget(); // initialize and load target
71  *   test.loadRepo( TESTS_SRC_DIR"/data/openSUSE-11.1" );
72  *
73  *   // Here the pool is ready to be used.
74  *
75  * }
76  * \endcode
77 */
78 class TestSetup
79 {
80   public:
81     typedef TestSetupOptions Options;
82
83   public:
84     TestSetup( const Arch & sysarch_r = Arch_empty )
85     { _ctor( Pathname(), sysarch_r, Options() ); }
86
87     TestSetup( const Pathname & rootdir_r, const Arch & sysarch_r = Arch_empty, const Options & options_r = Options() )
88     { _ctor( rootdir_r, sysarch_r, options_r ); }
89
90     TestSetup( const Pathname & rootdir_r, const Options & options_r )
91     { _ctor( rootdir_r, Arch_empty, options_r ); }
92
93     ~TestSetup()
94     { USR << (_tmprootdir.path() == _rootdir ? "DELETE" : "KEEP") << " TESTSETUP below " << _rootdir << endl; }
95
96   public:
97     /** Whether directory \a path_r contains a solver testcase. */
98     static bool isTestcase( const Pathname & path_r )
99     {
100       return filesystem::PathInfo( path_r / "solver-test.xml" ).isFile();
101     }
102
103     /** Whether directory \a path_r contains a testsetup. */
104     static bool isTestSetup( const Pathname & path_r )
105     {
106       return filesystem::PathInfo( path_r / "repos.d" ).isDir() && filesystem::PathInfo( path_r / "raw" ).isDir();
107     }
108
109   public:
110     const Pathname & root() const { return _rootdir; }
111
112     Target &     target()      { if ( ! getZYpp()->getTarget() ) getZYpp()->initializeTarget( _rootdir ); return *getZYpp()->getTarget(); }
113     RepoManager  repomanager() { return RepoManager( RepoManagerOptions::makeTestSetup( _rootdir ) ); }
114     ResPool      pool()        { return ResPool::instance(); }
115     ResPoolProxy poolProxy()   { return pool().proxy(); }
116     sat::Pool    satpool()     { return sat::Pool::instance(); }
117     Resolver &   resolver()    { return *getZYpp()->resolver(); }
118
119   public:
120     /** Load target repo. */
121     void loadTarget()
122     { target().load(); }
123     /** Fake @System repo from url. */
124     void loadTargetRepo( const Url & url_r )
125     { loadRepo( url_r, sat::Pool::systemRepoAlias() ); }
126     /** Fake @System repo from Path. */
127     void loadTargetRepo( const Pathname & path_r )
128     { loadRepo( path_r, sat::Pool::systemRepoAlias() ); }
129     /** Fake @System repo from helix repo. */
130     void loadTargetHelix( const Pathname & path_r )
131     { loadHelix( path_r, sat::Pool::systemRepoAlias() ); }
132
133   public:
134     /** Directly load repoinfo to pool. */
135     void loadRepo( RepoInfo nrepo )
136     {
137       RepoManager rmanager( repomanager() );
138       if ( rmanager.hasRepo( nrepo ) )
139         nrepo.setAlias( RepoManager::makeStupidAlias( nrepo.url() ) );
140       rmanager.addRepository( nrepo );
141       rmanager.buildCache( nrepo );
142       rmanager.loadFromCache( nrepo );
143     }
144     /** Directly load repo from url to pool. */
145     void loadRepo( const Url & url_r, const std::string & alias_r = std::string() )
146     {
147       RepoInfo nrepo;
148       nrepo.setAlias( alias_r.empty() ? url_r.getHost()+":"+Pathname::basename(url_r.getPathName()) : alias_r );
149       nrepo.addBaseUrl( url_r );
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   private:
189     // repo data from solver-test.xml
190     struct RepoD {
191       DefaultIntegral<unsigned,0> priority;
192       std::string alias;
193       Url url;
194     };
195
196   public:
197     /** Directly load a helix repo from some testcase.
198      * An empty alias is guessed.
199      */
200     void loadHelix( const Pathname & path_r, const std::string & alias_r = std::string() )
201     {
202       // .solv file is loaded directly using a faked RepoInfo
203       RepoInfo nrepo;
204       nrepo.setAlias( alias_r.empty() ? path_r.basename() : alias_r );
205       satpool().addRepoHelix( path_r, nrepo );
206     }
207
208     // Load repos included in a solver testcase.
209     void loadTestcaseRepos( const Pathname & path_r )
210     {
211       filesystem::PathInfo pi( path_r / "solver-test.xml" );
212       if ( ! pi.isFile() )
213       {
214         ERR << "No testcase in " << filesystem::PathInfo( path_r ) << endl;
215         return;
216       }
217       // dumb parse
218       InputStream infile( pi.path() );
219       Arch sysarch( Arch_empty );
220       Url guessedUrl;
221       typedef std::map<std::string,RepoD> RepoI;
222       RepoI repoi;
223       for( iostr::EachLine in( infile ); in; in.next() )
224       {
225         if ( str::hasPrefix( *in, "\t<channel" ) )
226         {
227           RepoD & repod( repoi[getXmlNodeVal( *in, "file" )] );
228
229           repod.alias = getXmlNodeVal( *in, "name" );
230           repod.priority = str::strtonum<unsigned>( getXmlNodeVal( *in, "priority" ) );
231           repod.url = guessedUrl;
232           guessedUrl = Url();
233         }
234         else if ( str::hasPrefix( *in, "\t- url " ) )
235         {
236           std::string::size_type pos = in->find( ": " );
237           if ( pos != std::string::npos )
238           {
239             guessedUrl = Url( in->substr( pos+2 ) );
240           }
241         }
242         else if ( str::hasPrefix( *in, "\t<locale" ) )
243         {
244           satpool().addRequestedLocale( Locale( getXmlNodeVal( *in, "name" ) ) );
245         }
246        else if ( sysarch.empty() && str::hasPrefix( *in, "<setup" ) )
247         {
248           sysarch = Arch( getXmlNodeVal( *in, "arch" ) );
249           if ( ! sysarch.empty() )
250             ZConfig::instance().setSystemArchitecture( sysarch );
251         }
252       }
253
254       //
255       filesystem::Glob files( path_r/"*{.xml,.xml.gz}", filesystem::Glob::_BRACE );
256       for_( it, files.begin(), files.end() )
257       {
258         std::string basename( Pathname::basename( *it ) );
259         if ( str::hasPrefix( basename, "solver-test.xml" ) )
260           continue; // master index currently unevaluated
261         if ( str::hasPrefix( basename, "solver-system.xml" ) )
262           loadTargetHelix( *it );
263         else
264         {
265           const RepoD & repod( repoi[basename] );
266
267           RepoInfo nrepo;
268           nrepo.setAlias( repod.alias.empty() ? basename : repod.alias );
269           nrepo.setPriority( repod.priority );
270           nrepo.setBaseUrl( repod.url );
271           satpool().addRepoHelix( *it, nrepo );
272         }
273       }
274
275       poolProxy(); // prepare
276     }
277
278   public:
279     /** Load all enabled repos in repos.d to pool. */
280     void loadRepos()
281     {
282       RepoManager repoManager( repomanager() );
283       RepoInfoList repos = repoManager.knownRepositories();
284       for ( RepoInfoList::iterator it = repos.begin(); it != repos.end(); ++it )
285       {
286         RepoInfo & nrepo( *it );
287         USR << nrepo << endl;
288
289         if ( ! nrepo.enabled() )
290           continue;
291
292         if ( ! repoManager.isCached( nrepo ) || nrepo.type() == repo::RepoType::RPMPLAINDIR )
293         {
294           if ( repoManager.isCached( nrepo ) )
295           {
296             USR << "cleanCache" << endl;
297             repoManager.cleanCache( nrepo );
298           }
299           //USR << "refreshMetadata" << endl;
300           //repoManager.refreshMetadata( nrepo );
301           USR << "buildCache" << endl;
302           repoManager.buildCache( nrepo );
303         }
304         USR << "Create from cache" << endl;
305         repoManager.loadFromCache( nrepo );
306       }
307     }
308
309   public:
310     /** Detect and load the system located at \a sysRoot.
311      *
312      * \a sysRoot needs to be a directory containing either a SolverTestcase,
313      * a TestSetup system or a real system. The  provided repostitories are
314      * loaded into the pool (without refresh).
315     */
316     static void LoadSystemAt( const Pathname & sysRoot, const Arch & _testSetupArch_r = Arch_x86_64 )
317     {
318       if ( ! PathInfo( sysRoot ).isDir() )
319         ZYPP_THROW( Exception("sysRoot argument needs to be a directory") );
320
321       if ( TestSetup::isTestcase( sysRoot ) )
322       {
323         USR << str::form( "*** Load Testcase from '%s'", sysRoot.c_str() ) << endl;
324         TestSetup test;
325         test.loadTestcaseRepos( sysRoot );
326       }
327       else if ( TestSetup::isTestSetup( sysRoot ) )
328       {
329         USR << str::form( "*** Load TestSetup from '%s'", sysRoot.c_str() ) << endl;
330
331         TestSetup test( sysRoot, _testSetupArch_r );
332         test.loadRepos();
333
334         Pathname solvCachePath( RepoManagerOptions::makeTestSetup( test.root() ).repoSolvCachePath );
335         Pathname fakeTargetSolv( solvCachePath / sat::Pool::systemRepoAlias() / "solv" );
336         if ( PathInfo( fakeTargetSolv ).isFile() )
337         {
338           USR << str::form( "*** Fake TestSetup Target from '%s'", fakeTargetSolv.c_str() ) << endl;
339           test.target();
340           test.loadTargetRepo( fakeTargetSolv );
341         }
342       }
343       else
344       {
345         sat::Pool satpool( sat::Pool::instance() );
346         // a system
347         USR << str::form( "*** Load system at '%s'", sysRoot.c_str() ) << endl;
348         if ( 1 )
349         {
350           USR << "*** load target '" << Repository::systemRepoAlias() << "'\t" << endl;
351           getZYpp()->initializeTarget( sysRoot );
352           getZYpp()->target()->load();
353           USR << satpool.systemRepo() << endl;
354         }
355
356         if ( 1 )
357         {
358           RepoManager repoManager( sysRoot );
359           RepoInfoList repos = repoManager.knownRepositories();
360           for_( it, repos.begin(), repos.end() )
361           {
362             RepoInfo & nrepo( *it );
363
364             if ( ! nrepo.enabled() )
365               continue;
366
367             if ( ! repoManager.isCached( nrepo ) )
368             {
369               USR << str::form( "*** omit uncached repo '%s' (do 'zypper refresh')", nrepo.name().c_str() ) << endl;
370               continue;
371             }
372
373             USR << str::form( "*** load repo '%s'\t", nrepo.name().c_str() ) << flush;
374             try
375             {
376               repoManager.loadFromCache( nrepo );
377               USR << satpool.reposFind( nrepo.alias() ) << endl;
378             }
379             catch ( const Exception & exp )
380             {
381               USR << exp.asString() + "\n" + exp.historyAsString() << endl;
382               USR << str::form( "*** omit broken repo '%s' (do 'zypper refresh')", nrepo.name().c_str() ) << endl;
383               continue;
384             }
385           }
386         }
387       }
388     }
389
390   private:
391     void _ctor( const Pathname & rootdir_r, const Arch & sysarch_r, const Options & options_r )
392     {
393       if ( rootdir_r.empty() )
394         _rootdir = _tmprootdir.path();
395       else
396       {
397         filesystem::assert_dir( (_rootdir = rootdir_r) );
398         if ( options_r.testFlag( TSO_CLEANROOT ) )
399           filesystem::clean_dir( _rootdir );
400       }
401
402       if ( ! sysarch_r.empty() )
403         ZConfig::instance().setSystemArchitecture( sysarch_r );
404       USR << "CREATED TESTSETUP below " << _rootdir << endl;
405     }
406   private:
407     filesystem::TmpDir _tmprootdir;
408     Pathname           _rootdir;
409 };
410
411
412 #endif //INCLUDE_TESTSETUP