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