1 #ifndef INCLUDE_TESTSETUP
2 #define INCLUDE_TESTSETUP
5 #ifndef INCLUDE_TESTSETUP_WITHOUT_BOOST
6 #include <boost/test/auto_unit_test.hpp>
7 using boost::unit_test::test_case;
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>
31 #ifndef BOOST_CHECK_NE
32 #define BOOST_CHECK_NE( L, R ) BOOST_CHECK( (L) != (R) )
35 #define LABELED(V) #V << ":\t" << V
37 inline std::string getXmlNodeVal( const std::string & line_r, const std::string & node_r )
39 std::string::size_type pos = line_r.find( node_r + "=\"" );
40 if ( pos != std::string::npos )
42 pos += node_r.size() + 2;
43 std::string::size_type epos = line_r.find( "\"", pos );
44 return line_r.substr( pos, epos-pos );
49 enum TestSetupOptionBits
51 TSO_CLEANROOT = (1 << 0), // wipe rootdir in ctor
52 TSO_REPO_DEFAULT_GPG = (1 << 1), // dont turn off gpgcheck in repos
54 ZYPP_DECLARE_FLAGS_AND_OPERATORS( TestSetupOptions, TestSetupOptionBits );
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.
60 * \note The lifetime of this objects is the lifetime of the temp. root directory.
63 * #include "TestSetup.h"
65 * BOOST_AUTO_TEST_CASE(WhatProvides)
67 * // enabls loging fot the scope of this block:
68 * // base::LogControl::TmpLineWriter shutUp( new log::FileLineWriter( "/tmp/YLOG" ) );
70 * TestSetup test( Arch_x86_64 );
71 * // test.loadTarget(); // initialize and load target
72 * test.loadRepo( TESTS_SRC_DIR"/data/openSUSE-11.1" );
74 * // Here the pool is ready to be used.
82 typedef TestSetupOptions Options;
85 struct InitLaterType {};
86 static constexpr InitLaterType initLater = InitLaterType();
88 TestSetup( InitLaterType )
91 TestSetup( const Arch & sysarch_r = Arch_empty, const Options & options_r = Options() )
92 : _pimpl { new Impl( Pathname(), sysarch_r, options_r ) }
95 TestSetup( const Pathname & rootdir_r, const Arch & sysarch_r = Arch_empty, const Options & options_r = Options() )
96 : _pimpl { new Impl( rootdir_r, sysarch_r, options_r ) }
99 TestSetup( const Pathname & rootdir_r, const Options & options_r )
100 : _pimpl { new Impl( rootdir_r, Arch_empty, options_r ) }
107 /** Whether directory \a path_r contains a solver testcase. */
108 static bool isTestcase( const Pathname & path_r )
110 return filesystem::PathInfo( path_r / "solver-test.xml" ).isFile();
113 /** Whether directory \a path_r contains a testsetup. */
114 static bool isTestSetup( const Pathname & path_r )
116 return filesystem::PathInfo( path_r / "repos.d" ).isDir() && filesystem::PathInfo( path_r / "raw" ).isDir();
120 const Pathname & root() const { return _pimpl->_rootdir; }
122 Target & target() { if ( ! getZYpp()->getTarget() ) getZYpp()->initializeTarget( _pimpl->_rootdir ); return *getZYpp()->getTarget(); }
123 RepoManager repomanager() { return RepoManager( RepoManagerOptions::makeTestSetup( _pimpl->_rootdir ) ); }
124 ResPool pool() { return ResPool::instance(); }
125 ResPoolProxy poolProxy() { return pool().proxy(); }
126 sat::Pool satpool() { return sat::Pool::instance(); }
127 Resolver & resolver() { return *getZYpp()->resolver(); }
130 /** Load target repo. */
133 /** Fake @System repo from url. */
134 void loadTargetRepo( const Url & url_r )
135 { loadRepo( url_r, sat::Pool::systemRepoAlias() ); }
136 /** Fake @System repo from Path. */
137 void loadTargetRepo( const Pathname & path_r )
138 { loadRepo( path_r, sat::Pool::systemRepoAlias() ); }
139 /** Fake @System repo from helix repo. */
140 void loadTargetHelix( const Pathname & path_r )
141 { loadHelix( path_r, sat::Pool::systemRepoAlias() ); }
144 /** Directly load repoinfo to pool. */
145 void loadRepo( RepoInfo nrepo )
147 RepoManager rmanager( repomanager() );
148 if ( rmanager.hasRepo( nrepo ) )
149 nrepo.setAlias( RepoManager::makeStupidAlias( nrepo.url() ) );
150 rmanager.addRepository( nrepo );
151 rmanager.buildCache( nrepo );
152 rmanager.loadFromCache( nrepo );
154 /** Directly load repo from url to pool. */
155 void loadRepo( const Url & url_r, const std::string & alias_r = std::string() )
158 nrepo.setAlias( alias_r.empty() ? url_r.getHost()+":"+Pathname::basename(url_r.getPathName()) : alias_r );
159 nrepo.addBaseUrl( url_r );
160 if ( ! _pimpl->_options.testFlag( TSO_REPO_DEFAULT_GPG ) )
161 nrepo.setGpgCheck( false );
164 /** Directly load repo from metadata(dir) or solvfile(file) to pool.
165 * An empty alias is guessed.
167 void loadRepo( const Pathname & path_r, const std::string & alias_r = std::string() )
169 if ( filesystem::PathInfo( path_r ).isDir() )
171 loadRepo( path_r.asUrl(), alias_r );
174 // .solv file is loaded directly using a faked RepoInfo
176 nrepo.setAlias( alias_r.empty() ? path_r.basename() : alias_r );
177 satpool().addRepoSolv( path_r, nrepo );
179 /** Directly load repo from some location (url or absolute(!)path).
180 * An empty alias is guessed.
182 void loadRepo( const std::string & loc_r, const std::string & alias_r = std::string() )
184 if ( *loc_r.c_str() == '/' )
186 loadRepo( Pathname( loc_r ), alias_r );
190 loadRepo( Url( loc_r ), alias_r );
193 /** Directly load repo from some location (url or absolute(!)path).
194 * An empty alias is guessed.
196 void loadRepo( const char * loc_r, const std::string & alias_r = std::string() )
197 { loadRepo( std::string( loc_r ? loc_r : "" ), alias_r ); }
200 // repo data from solver-test.xml
202 DefaultIntegral<unsigned,0> priority;
208 /** Directly load a helix repo from some testcase.
209 * An empty alias is guessed.
211 void loadHelix( const Pathname & path_r, const std::string & alias_r = std::string() )
213 // .solv file is loaded directly using a faked RepoInfo
215 nrepo.setAlias( alias_r.empty() ? path_r.basename() : alias_r );
216 satpool().addRepoHelix( path_r, nrepo );
219 // Load repos included in a solver testcase.
220 void loadTestcaseRepos( const Pathname & path_r )
222 filesystem::PathInfo pi( path_r / "solver-test.xml" );
225 ERR << "No testcase in " << filesystem::PathInfo( path_r ) << endl;
229 InputStream infile( pi.path() );
230 Arch sysarch( Arch_empty );
232 typedef std::map<std::string,RepoD> RepoI;
234 for( iostr::EachLine in( infile ); in; in.next() )
236 if ( str::hasPrefix( *in, "\t<channel" ) )
238 RepoD & repod( repoi[getXmlNodeVal( *in, "file" )] );
240 repod.alias = getXmlNodeVal( *in, "name" );
241 repod.priority = str::strtonum<unsigned>( getXmlNodeVal( *in, "priority" ) );
242 repod.url = guessedUrl;
245 else if ( str::hasPrefix( *in, "\t- url " ) )
247 std::string::size_type pos = in->find( ": " );
248 if ( pos != std::string::npos )
250 guessedUrl = Url( in->substr( pos+2 ) );
253 else if ( str::hasPrefix( *in, "\t<locale" ) )
255 satpool().addRequestedLocale( Locale( getXmlNodeVal( *in, "name" ) ) );
257 else if ( sysarch.empty() && str::hasPrefix( *in, "<setup" ) )
259 sysarch = Arch( getXmlNodeVal( *in, "arch" ) );
260 if ( ! sysarch.empty() )
261 ZConfig::instance().setSystemArchitecture( sysarch );
266 filesystem::Glob files( path_r/"*{.xml,.xml.gz}", filesystem::Glob::kBrace );
267 for_( it, files.begin(), files.end() )
269 std::string basename( Pathname::basename( *it ) );
270 if ( str::hasPrefix( basename, "solver-test.xml" ) )
271 continue; // master index currently unevaluated
272 if ( str::hasPrefix( basename, "solver-system.xml" ) )
273 loadTargetHelix( *it );
276 const RepoD & repod( repoi[basename] );
279 nrepo.setAlias( repod.alias.empty() ? basename : repod.alias );
280 nrepo.setPriority( repod.priority );
281 nrepo.setBaseUrl( repod.url );
282 satpool().addRepoHelix( *it, nrepo );
286 poolProxy(); // prepare
290 /** Load all enabled repos in repos.d to pool. */
293 RepoManager repoManager( repomanager() );
294 RepoInfoList repos = repoManager.knownRepositories();
295 for ( RepoInfoList::iterator it = repos.begin(); it != repos.end(); ++it )
297 RepoInfo & nrepo( *it );
298 USR << nrepo << endl;
300 if ( ! nrepo.enabled() )
303 if ( ! repoManager.isCached( nrepo ) || nrepo.type() == repo::RepoType::RPMPLAINDIR )
305 if ( repoManager.isCached( nrepo ) )
307 USR << "cleanCache" << endl;
308 repoManager.cleanCache( nrepo );
310 //USR << "refreshMetadata" << endl;
311 //repoManager.refreshMetadata( nrepo );
312 USR << "buildCache" << endl;
313 repoManager.buildCache( nrepo );
315 USR << "Create from cache" << endl;
316 repoManager.loadFromCache( nrepo );
321 /** Detect and load the system located at \a sysRoot.
323 * \a sysRoot needs to be a directory containing either a SolverTestcase,
324 * a TestSetup system or a real system. The provided repostitories are
325 * loaded into the pool (without refresh).
327 static void LoadSystemAt( const Pathname & sysRoot, const Arch & _testSetupArch_r = Arch_x86_64 )
329 if ( ! PathInfo( sysRoot ).isDir() )
330 ZYPP_THROW( Exception("sysRoot argument needs to be a directory") );
332 if ( TestSetup::isTestcase( sysRoot ) )
334 USR << str::form( "*** Load Testcase from '%s'", sysRoot.c_str() ) << endl;
336 test.loadTestcaseRepos( sysRoot );
338 else if ( TestSetup::isTestSetup( sysRoot ) )
340 USR << str::form( "*** Load TestSetup from '%s'", sysRoot.c_str() ) << endl;
342 TestSetup test( sysRoot, _testSetupArch_r );
345 Pathname solvCachePath( RepoManagerOptions::makeTestSetup( test.root() ).repoSolvCachePath );
346 Pathname fakeTargetSolv( solvCachePath / sat::Pool::systemRepoAlias() / "solv" );
347 if ( PathInfo( fakeTargetSolv ).isFile() )
349 USR << str::form( "*** Fake TestSetup Target from '%s'", fakeTargetSolv.c_str() ) << endl;
351 test.loadTargetRepo( fakeTargetSolv );
356 sat::Pool satpool( sat::Pool::instance() );
358 USR << str::form( "*** Load system at '%s'", sysRoot.c_str() ) << endl;
361 USR << "*** load target '" << Repository::systemRepoAlias() << "'\t" << endl;
362 getZYpp()->initializeTarget( sysRoot );
363 getZYpp()->target()->load();
364 USR << satpool.systemRepo() << endl;
369 RepoManager repoManager( sysRoot );
370 RepoInfoList repos = repoManager.knownRepositories();
371 for_( it, repos.begin(), repos.end() )
373 RepoInfo & nrepo( *it );
375 if ( ! nrepo.enabled() )
378 if ( ! repoManager.isCached( nrepo ) )
380 USR << str::form( "*** omit uncached repo '%s' (do 'zypper refresh')", nrepo.name().c_str() ) << endl;
384 USR << str::form( "*** load repo '%s'\t", nrepo.name().c_str() ) << flush;
387 repoManager.loadFromCache( nrepo );
388 USR << satpool.reposFind( nrepo.alias() ) << endl;
390 catch ( const Exception & exp )
392 USR << exp.asString() + "\n" + exp.historyAsString() << endl;
393 USR << str::form( "*** omit broken repo '%s' (do 'zypper refresh')", nrepo.name().c_str() ) << endl;
404 Impl( const Pathname & rootdir_r, const Arch & sysarch_r, const Options & options_r )
406 _options = options_r;
408 if ( rootdir_r.empty() )
409 _rootdir = _tmprootdir.path();
412 filesystem::assert_dir( (_rootdir = rootdir_r) );
413 if ( _options.testFlag( TSO_CLEANROOT ) )
414 filesystem::clean_dir( _rootdir );
417 // erase any old pool content...
418 getZYpp()->finishTarget();
419 sat::Pool::instance().reposEraseAll();
420 // prepare for the new one...
421 ZConfig::instance().setRepoManagerRoot( _rootdir );
423 if ( ! sysarch_r.empty() )
424 ZConfig::instance().setSystemArchitecture( sysarch_r );
425 USR << "CREATED TESTSETUP below " << _rootdir << endl;
429 { USR << (_tmprootdir.path() == _rootdir ? "DELETE" : "KEEP") << " TESTSETUP below " << _rootdir << endl; }
431 filesystem::TmpDir _tmprootdir;
436 std::unique_ptr<Impl> _pimpl; // maybe worth creating RW_pointer traits for it
440 #endif //INCLUDE_TESTSETUP