1 /*---------------------------------------------------------------------\
3 | |__ / \ / / . \ . \ |
8 \---------------------------------------------------------------------*/
9 /** \file zypp/solver/detail/Testcase.cc
17 #define ZYPP_USE_RESOLVER_INTERNALS
19 #include "zypp/solver/detail/Testcase.h"
20 #include "zypp/base/Logger.h"
21 #include "zypp/base/LogControl.h"
22 #include "zypp/base/GzStream.h"
23 #include "zypp/base/String.h"
24 #include "zypp/base/PtrTypes.h"
25 #include "zypp/base/NonCopyable.h"
26 #include "zypp/base/ReferenceCounted.h"
28 #include "zypp/parser/xml/XmlEscape.h"
30 #include "zypp/ZConfig.h"
31 #include "zypp/PathInfo.h"
32 #include "zypp/ResPool.h"
33 #include "zypp/Repository.h"
34 #include "zypp/target/modalias/Modalias.h"
36 #include "zypp/sat/detail/PoolImpl.h"
37 #include "zypp/solver/detail/Resolver.h"
38 #include "zypp/solver/detail/SystemCheck.h"
40 /////////////////////////////////////////////////////////////////////////
42 { ///////////////////////////////////////////////////////////////////////
43 ///////////////////////////////////////////////////////////////////////
45 { /////////////////////////////////////////////////////////////////////
46 /////////////////////////////////////////////////////////////////////
48 { ///////////////////////////////////////////////////////////////////
54 using namespace zypp::str;
56 //---------------------------------------------------------------------------
58 inline std::string xml_escape( const std::string &text )
60 return zypp::xml::escape(text);
63 inline std::string xml_tag_enclose( const std::string &text, const std::string &tag, bool escape = false )
66 result += "<" + tag + ">";
69 result += xml_escape(text);
73 result += "</" + tag + ">";
78 std::string helixXML( const T &obj ); //undefined
81 std::string helixXML( const Edition &edition )
84 str << xml_tag_enclose(edition.version(), "version");
85 if (!edition.release().empty())
86 str << xml_tag_enclose(edition.release(), "release");
87 if (edition.epoch() != Edition::noepoch)
88 str << xml_tag_enclose(numstring(edition.epoch()), "epoch");
93 std::string helixXML( const Arch &arch )
96 str << xml_tag_enclose(arch.asString(), "arch");
101 std::string helixXML( const Capability &cap )
104 CapDetail detail = cap.detail();
105 if (detail.isSimple()) {
106 if (detail.isVersioned()) {
107 str << "<dep name='" << xml_escape(detail.name().asString()) << "'"
108 << " op='" << xml_escape(detail.op().asString()) << "'"
109 << " version='" << xml_escape(detail.ed().version()) << "'";
110 if (!detail.ed().release().empty())
111 str << " release='" << xml_escape(detail.ed().release()) << "'";
112 if (detail.ed().epoch() != Edition::noepoch)
113 str << " epoch='" << xml_escape(numstring(detail.ed().epoch())) << "'";
114 str << " />" << endl;
116 str << "<dep name='" << xml_escape(cap.asString()) << "' />" << endl;
118 } else if (detail.isExpression()) {
119 if (detail.capRel() == CapDetail::CAP_AND
120 && detail.lhs().detail().isNamed()
121 && detail.rhs().detail().isNamed()) {
122 // packageand dependency
123 str << "<dep name='packageand("
124 << IdString(detail.lhs().id()) << ":"
125 << IdString(detail.rhs().id()) << ")' />" << endl;
126 } else if (detail.capRel() == CapDetail::CAP_NAMESPACE
127 && detail.lhs().id() == NAMESPACE_OTHERPROVIDERS) {
128 str << "<dep name='otherproviders("
129 << IdString(detail.rhs().id()) << ")' />" << endl;
132 IdString packageName;
133 if (detail.capRel() == CapDetail::CAP_AND) {
134 packageName = IdString(detail.lhs().id());
135 detail = detail.rhs().detail();
137 if (detail.capRel() == CapDetail::CAP_NAMESPACE
138 && detail.lhs().id() == NAMESPACE_MODALIAS) {
139 str << "<dep name='modalias(";
140 if (!packageName.empty())
141 str << packageName << ":";
142 str << IdString(detail.rhs().id()) << ")' />" << endl;
144 str << "<!--- ignoring '" << xml_escape(cap.asString()) << "' -->" << endl;
148 str << "<!--- ignoring '" << xml_escape(cap.asString()) << "' -->" << endl;
155 std::string helixXML( const Capabilities &caps )
158 Capabilities::const_iterator it = caps.begin();
160 for ( ; it != caps.end(); ++it)
162 str << TAB2 << helixXML((*it));
169 std::string helixXML( const CapabilitySet &caps )
172 CapabilitySet::const_iterator it = caps.begin();
174 for ( ; it != caps.end(); ++it)
176 str << TAB2 << helixXML((*it));
182 inline string helixXML( const PoolItem & obj, Dep deptag_r )
185 Capabilities caps( obj[deptag_r] );
186 if ( ! caps.empty() )
187 out << TAB << xml_tag_enclose(helixXML(caps), deptag_r.asString()) << endl;
191 std::string helixXML( const PoolItem & item )
194 str << "<" << item.kind() << ">" << endl;
195 str << TAB << xml_tag_enclose( item.name(), "name", true ) << endl;
196 str << TAB << xml_tag_enclose( item.vendor().asString(), "vendor", true ) << endl;
197 str << TAB << xml_tag_enclose( item.buildtime().asSeconds(), "buildtime", true ) << endl;
198 if ( isKind<Package>( item ) ) {
199 str << TAB << "<history>" << endl << TAB << "<update>" << endl;
200 str << TAB2 << helixXML( item.arch() ) << endl;
201 str << TAB2 << helixXML( item.edition() ) << endl;
202 str << TAB << "</update>" << endl << TAB << "</history>" << endl;
204 str << TAB << helixXML( item.arch() ) << endl;
205 str << TAB << helixXML( item.edition() ) << endl;
207 str << helixXML( item, Dep::PROVIDES );
208 str << helixXML( item, Dep::PREREQUIRES );
209 str << helixXML( item, Dep::CONFLICTS );
210 str << helixXML( item, Dep::OBSOLETES );
211 str << helixXML( item, Dep::REQUIRES );
212 str << helixXML( item, Dep::RECOMMENDS );
213 str << helixXML( item, Dep::ENHANCES );
214 str << helixXML( item, Dep::SUPPLEMENTS );
215 str << helixXML( item, Dep::SUGGESTS );
217 str << "</" << item.kind() << ">" << endl;
221 ///////////////////////////////////////////////////////////////////
223 // CLASS NAME : HelixResolvable
225 * Creates a file in helix format which includes all available
226 * or installed packages,patches,selections.....
228 class HelixResolvable : public base::ReferenceCounted, private base::NonCopyable{
231 std::string dumpFile; // Path of the generated testcase
235 HelixResolvable (const std::string & path);
238 void addResolvable (const PoolItem item)
239 { *file << helixXML (item); }
241 std::string filename ()
245 DEFINE_PTR_TYPE(HelixResolvable);
246 IMPL_PTR_TYPE(HelixResolvable);
248 typedef std::map<Repository, HelixResolvable_Ptr> RepositoryTable;
250 HelixResolvable::HelixResolvable(const std::string & path)
253 file = new ofgzstream(path.c_str());
255 ZYPP_THROW (Exception( "Can't open " + path ) );
258 *file << "<channel><subchannel>" << endl;
261 HelixResolvable::~HelixResolvable()
263 *file << "</subchannel></channel>" << endl;
267 ///////////////////////////////////////////////////////////////////
269 // CLASS NAME : HelixControl
271 * Creates a file in helix format which contains all controll
272 * action of a testcase ( file is known as *-test.xml)
277 std::string dumpFile; // Path of the generated testcase
282 HelixControl (const std::string & controlPath,
283 const RepositoryTable & sourceTable,
284 const Arch & systemArchitecture,
285 const target::Modalias::ModaliasList & modaliasList,
286 const std::set<std::string> & multiversionSpec,
287 const std::string & systemPath);
295 *file << "</setup>" << endl << "<trial>" << endl;
300 void addTagIf( const std::string & tag_r, bool yesno_r = true )
303 *file << (_inSetup ? TAB : "") << "<" << tag_r << "/>" << endl;
306 void installResolvable( const PoolItem & pi_r );
307 void lockResolvable( const PoolItem & pi_r );
308 void keepResolvable( const PoolItem & pi_r );
309 void deleteResolvable( const PoolItem & pi_r );
310 void addDependencies (const CapabilitySet &capRequire, const CapabilitySet &capConflict);
311 void addUpgradeRepos( const std::set<Repository> & upgradeRepos_r );
313 std::string filename () { return dumpFile; }
316 HelixControl::HelixControl(const std::string & controlPath,
317 const RepositoryTable & repoTable,
318 const Arch & systemArchitecture,
319 const target::Modalias::ModaliasList & modaliasList,
320 const std::set<std::string> & multiversionSpec,
321 const std::string & systemPath)
322 :dumpFile (controlPath)
325 file = new ofstream(controlPath.c_str());
327 ZYPP_THROW (Exception( "Can't open " + controlPath ) );
330 *file << "<?xml version=\"1.0\"?>" << endl
331 << "<!-- testcase generated by YaST -->" << endl
333 << "<setup arch=\"" << systemArchitecture << "\">" << endl
334 << TAB << "<system file=\"" << systemPath << "\"/>" << endl << endl;
335 for ( RepositoryTable::const_iterator it = repoTable.begin();
336 it != repoTable.end(); ++it ) {
337 RepoInfo repo = it->first.info();
338 *file << TAB << "<!-- " << endl
339 << TAB << "- alias : " << repo.alias() << endl;
340 for ( RepoInfo::urls_const_iterator itUrl = repo.baseUrlsBegin();
341 itUrl != repo.baseUrlsEnd();
344 *file << TAB << "- url : " << *itUrl << endl;
346 *file << TAB << "- path : " << repo.path() << endl;
347 *file << TAB << "- type : " << repo.type() << endl;
348 *file << TAB << "- generated : " << (it->first.generatedTimestamp()).form( "%Y-%m-%d %H:%M:%S" ) << endl;
349 *file << TAB << "- outdated : " << (it->first.suggestedExpirationTimestamp()).form( "%Y-%m-%d %H:%M:%S" ) << endl;
350 *file << TAB << " -->" << endl;
352 *file << TAB << "<channel file=\"" << str::numstring((long)it->first.id())
353 << "-package.xml.gz\" name=\"" << repo.alias() << "\""
354 << " priority=\"" << repo.priority()
355 << "\" />" << endl << endl;
358 // HACK: directly access sat::pool
359 const sat::Pool & satpool( sat::Pool::instance() );
362 const LocaleSet & addedLocales( satpool.getAddedRequestedLocales() );
363 const LocaleSet & removedLocales( satpool.getRemovedRequestedLocales() );
364 const LocaleSet & requestedLocales( satpool.getRequestedLocales() );
366 for ( Locale l : requestedLocales )
368 const char * fate = ( addedLocales.count(l) ? "\" fate=\"added" : "" );
369 *file << TAB << "<locale name=\"" << l << fate << "\" />" << endl;
371 for ( Locale l : removedLocales )
373 *file << TAB << "<locale name=\"" << l << "\" fate=\"removed\" />" << endl;
377 for ( IdString::IdType n : satpool.autoInstalled() )
379 *file << TAB << "<autoinst name=\"" << IdString(n) << "\" />" << endl;
384 for_( it, modaliasList.begin(), modaliasList.end() ) {
385 *file << TAB << "<modalias name=\"" << xml_escape(*it)
389 for_( it, multiversionSpec.begin(), multiversionSpec.end() ) {
390 *file << TAB << "<multiversion name=\"" << *it
394 // setup continued outside....
397 HelixControl::HelixControl()
398 :dumpFile ("/var/log/YaST2/solverTestcase/solver-test.xml")
400 HelixControl (dumpFile);
403 HelixControl::~HelixControl()
405 closeSetup(); // in case it is still open
406 *file << "</trial>" << endl
407 << "</test>" << endl;
411 void HelixControl::installResolvable( const PoolItem & pi_r )
413 *file << "<install channel=\"" << pi_r.repoInfo().alias() << "\""
414 << " kind=\"" << pi_r.kind() << "\""
415 << " name=\"" << pi_r.name() << "\""
416 << " arch=\"" << pi_r.arch() << "\""
417 << " version=\"" << pi_r.edition().version() << "\""
418 << " release=\"" << pi_r.edition().release() << "\""
419 << " status=\"" << pi_r.status() << "\""
423 void HelixControl::lockResolvable( const PoolItem & pi_r )
425 *file << "<lock channel=\"" << pi_r.repoInfo().alias() << "\""
426 << " kind=\"" << pi_r.kind() << "\""
427 << " name=\"" << pi_r.name() << "\""
428 << " arch=\"" << pi_r.arch() << "\""
429 << " version=\"" << pi_r.edition().version() << "\""
430 << " release=\"" << pi_r.edition().release() << "\""
431 << " status=\"" << pi_r.status() << "\""
435 void HelixControl::keepResolvable( const PoolItem & pi_r )
437 *file << "<keep channel=\"" << pi_r.repoInfo().alias() << "\""
438 << " kind=\"" << pi_r.kind() << "\""
439 << " name=\"" << pi_r.name() << "\""
440 << " arch=\"" << pi_r.arch() << "\""
441 << " version=\"" << pi_r.edition().version() << "\""
442 << " release=\"" << pi_r.edition().release() << "\""
443 << " status=\"" << pi_r.status() << "\""
447 void HelixControl::deleteResolvable( const PoolItem & pi_r )
449 *file << "<uninstall kind=\"" << pi_r.kind() << "\""
450 << " name=\"" << pi_r.name() << "\""
451 << " status=\"" << pi_r.status() << "\""
455 void HelixControl::addDependencies (const CapabilitySet & capRequire, const CapabilitySet & capConflict)
457 for (CapabilitySet::const_iterator iter = capRequire.begin(); iter != capRequire.end(); iter++) {
458 *file << "<addRequire " << " name=\"" << iter->asString() << "\"" << "/>" << endl;
460 for (CapabilitySet::const_iterator iter = capConflict.begin(); iter != capConflict.end(); iter++) {
461 *file << "<addConflict " << " name=\"" << iter->asString() << "\"" << "/>" << endl;
465 void HelixControl::addUpgradeRepos( const std::set<Repository> & upgradeRepos_r )
467 for_( it, upgradeRepos_r.begin(), upgradeRepos_r.end() )
469 *file << "<upgradeRepo name=\"" << it->alias() << "\"/>" << endl;
473 //---------------------------------------------------------------------------
476 :dumpPath("/var/log/YaST2/solverTestcase")
479 Testcase::Testcase(const std::string & path)
483 Testcase::~Testcase()
486 bool Testcase::createTestcase(Resolver & resolver, bool dumpPool, bool runSolver)
488 PathInfo path (dumpPath);
490 if ( !path.isExist() ) {
491 if (zypp::filesystem::assert_dir (dumpPath)!=0) {
492 ERR << "Cannot create directory " << dumpPath << endl;
497 ERR << dumpPath << " is not a directory." << endl;
500 // remove old stuff if pool will be dump
502 zypp::filesystem::clean_dir (dumpPath);
506 zypp::base::LogControl::TmpLineWriter tempRedirect;
507 zypp::base::LogControl::instance().logfile( dumpPath +"/y2log" );
508 zypp::base::LogControl::TmpExcessive excessive;
510 resolver.resolvePool();
513 ResPool pool = resolver.pool();
514 RepositoryTable repoTable;
515 PoolItemList items_to_install;
516 PoolItemList items_to_remove;
517 PoolItemList items_locked;
518 PoolItemList items_keep;
519 HelixResolvable_Ptr system = NULL;
522 system = new HelixResolvable(dumpPath + "/solver-system.xml.gz");
524 for ( const PoolItem & pi : pool )
526 if ( system && pi.status().isInstalled() ) {
528 system->addResolvable( pi );
531 Repository repo = pi.repository();
533 if (repoTable.find (repo) == repoTable.end()) {
534 repoTable[repo] = new HelixResolvable(dumpPath + "/"
535 + str::numstring((long)repo.id())
536 + "-package.xml.gz");
538 repoTable[repo]->addResolvable( pi );
542 if ( pi.status().isToBeInstalled()
543 && !(pi.status().isBySolver())) {
544 items_to_install.push_back( pi );
546 if ( pi.status().isKept()
547 && !(pi.status().isBySolver())) {
548 items_keep.push_back( pi );
550 if ( pi.status().isToBeUninstalled()
551 && !(pi.status().isBySolver())) {
552 items_to_remove.push_back( pi );
554 if ( pi.status().isLocked()
555 && !(pi.status().isBySolver())) {
556 items_locked.push_back( pi );
560 // writing control file "*-test.xml"
561 HelixControl control (dumpPath + "/solver-test.xml",
563 ZConfig::instance().systemArchitecture(),
564 target::Modalias::instance().modaliasList(),
565 ZConfig::instance().multiversionSpec(),
566 "solver-system.xml.gz");
568 // In <setup>: resolver flags,...
569 control.addTagIf( "ignorealreadyrecommended", resolver.ignoreAlreadyRecommended() );
570 control.addTagIf( "onlyRequires", resolver.onlyRequires() );
571 control.addTagIf( "forceResolve", resolver.forceResolve() );
573 control.addTagIf( "cleandepsOnRemove", resolver.cleandepsOnRemove() );
575 control.addTagIf( "allowDowngrade", resolver.allowDowngrade() );
576 control.addTagIf( "allowNameChange", resolver.allowNameChange() );
577 control.addTagIf( "allowArchChange", resolver.allowArchChange() );
578 control.addTagIf( "allowVendorChange", resolver.allowVendorChange() );
580 control.addTagIf( "dupAllowDowngrade", resolver.dupAllowDowngrade() );
581 control.addTagIf( "dupAllowNameChange", resolver.dupAllowNameChange() );
582 control.addTagIf( "dupAllowArchChange", resolver.dupAllowArchChange() );
583 control.addTagIf( "dupAllowVendorChange", resolver.dupAllowVendorChange() );
585 control.closeSetup();
586 // Entering <trial>...
588 for ( const PoolItem & pi : items_to_install )
589 { control.installResolvable( pi ); }
591 for ( const PoolItem & pi : items_locked )
592 { control.lockResolvable( pi ); }
594 for ( const PoolItem & pi : items_keep )
595 { control.keepResolvable( pi ); }
597 for ( const PoolItem & pi : items_to_remove )
598 { control.deleteResolvable( pi ); }
600 control.addDependencies (resolver.extraRequires(), resolver.extraConflicts());
601 control.addDependencies (SystemCheck::instance().requiredSystemCap(),
602 SystemCheck::instance().conflictSystemCap());
603 control.addUpgradeRepos( resolver.upgradeRepos() );
605 control.addTagIf( "distupgrade", resolver.isUpgradeMode() );
606 control.addTagIf( "verify", resolver.isUpdateMode() );
607 control.addTagIf( "update", resolver.isVerifyingMode() );
613 ///////////////////////////////////////////////////////////////////
614 };// namespace detail
615 /////////////////////////////////////////////////////////////////////
616 /////////////////////////////////////////////////////////////////////
617 };// namespace solver
618 ///////////////////////////////////////////////////////////////////////
619 ///////////////////////////////////////////////////////////////////////
621 /////////////////////////////////////////////////////////////////////////