00001
00002
00003
00004
00005
00006
00007
00008
00012 #include <iostream>
00013 #include <fstream>
00014 #include <sstream>
00015 #include <streambuf>
00016
00017 #include "zypp/solver/detail/Testcase.h"
00018 #include "zypp/base/Logger.h"
00019 #include "zypp/base/LogControl.h"
00020 #include "zypp/ZConfig.h"
00021 #include "zypp/PathInfo.h"
00022 #include "zypp/Product.h"
00023 #include "zypp/Package.h"
00024 #include "zypp/Edition.h"
00025 #include "zypp/parser/xml/XmlEscape.h"
00026 #include "zypp/base/String.h"
00027 #include "zypp/base/PtrTypes.h"
00028 #include "zypp/Capabilities.h"
00029 #include "zypp/sat/Solvable.h"
00030 #include "zypp/sat/detail/PoolImpl.h"
00031 #include "zypp/solver/detail/SystemCheck.h"
00032
00033
00035 namespace zypp
00036 {
00037
00038 namespace solver
00039 {
00040
00041 namespace detail
00042 {
00043
00044 #define TAB "\t"
00045 #define TAB2 "\t\t"
00046
00047 using namespace std;
00048 using namespace zypp::str;
00049
00050 IMPL_PTR_TYPE(HelixResolvable);
00051
00052 inline std::string xml_escape( const std::string &text )
00053 {
00054 return zypp::xml::escape(text);
00055 }
00056
00057 inline std::string xml_tag_enclose( const std::string &text, const std::string &tag, bool escape = false )
00058 {
00059 string result;
00060 result += "<" + tag + ">";
00061
00062 if ( escape)
00063 result += xml_escape(text);
00064 else
00065 result += text;
00066
00067 result += "</" + tag + ">";
00068 return result;
00069 }
00070
00071
00072 template<class T>
00073 std::string helixXML( const T &obj );
00074
00075 template<>
00076 std::string helixXML( const Edition &edition )
00077 {
00078 stringstream str;
00079 str << xml_tag_enclose(edition.version(), "version");
00080 if (!edition.release().empty())
00081 str << xml_tag_enclose(edition.release(), "release");
00082 if (edition.epoch() != Edition::noepoch)
00083 str << xml_tag_enclose(numstring(edition.epoch()), "epoch");
00084 return str.str();
00085 }
00086
00087 template<>
00088 std::string helixXML( const Arch &arch )
00089 {
00090 stringstream str;
00091 str << xml_tag_enclose(arch.asString(), "arch");
00092 return str.str();
00093 }
00094
00095 template<>
00096 std::string helixXML( const Capability &cap )
00097 {
00098 stringstream str;
00099 CapDetail detail = cap.detail();
00100 if (detail.isSimple()) {
00101 if (detail.isVersioned()) {
00102 str << "<dep name='" << xml_escape(detail.name().asString()) << "'"
00103 << " op='" << xml_escape(detail.op().asString()) << "'"
00104 << " version='" << xml_escape(detail.ed().version()) << "'";
00105 if (!detail.ed().release().empty())
00106 str << " release='" << xml_escape(detail.ed().release()) << "'";
00107 if (detail.ed().epoch() != Edition::noepoch)
00108 str << " epoch='" << xml_escape(numstring(detail.ed().epoch())) << "'";
00109 str << " />" << endl;
00110 } else {
00111 str << "<dep name='" << xml_escape(cap.asString()) << "' />" << endl;
00112 }
00113 } else if (detail.isExpression()) {
00114 if (detail.capRel() == CapDetail::CAP_AND
00115 && detail.lhs().detail().isNamed()
00116 && detail.rhs().detail().isNamed()) {
00117
00118 str << "<dep name='packageand("
00119 << IdString(detail.lhs().id()) << ":"
00120 << IdString(detail.rhs().id()) << ")' />" << endl;
00121 } else {
00122
00123 IdString packageName;
00124 if (detail.capRel() == CapDetail::CAP_AND) {
00125 packageName = IdString(detail.lhs().id());
00126 detail = detail.rhs().detail();
00127 }
00128 if (detail.capRel() == CapDetail::CAP_NAMESPACE
00129 && detail.lhs().id() == NAMESPACE_MODALIAS) {
00130 str << "<dep name='modalias(";
00131 if (!packageName.empty())
00132 str << packageName << ":";
00133 str << IdString(detail.rhs().id()) << ")' />" << endl;
00134 } else {
00135 str << "<!--- ignoring '" << xml_escape(cap.asString()) << "' -->" << endl;
00136 }
00137 }
00138 } else {
00139 str << "<!--- ignoring '" << xml_escape(cap.asString()) << "' -->" << endl;
00140 }
00141
00142 return str.str();
00143 }
00144
00145 template<>
00146 std::string helixXML( const Capabilities &caps )
00147 {
00148 stringstream str;
00149 Capabilities::const_iterator it = caps.begin();
00150 str << endl;
00151 for ( ; it != caps.end(); ++it)
00152 {
00153 str << TAB2 << helixXML((*it));
00154 }
00155 str << TAB;
00156 return str.str();
00157 }
00158
00159 template<>
00160 std::string helixXML( const CapabilitySet &caps )
00161 {
00162 stringstream str;
00163 CapabilitySet::const_iterator it = caps.begin();
00164 str << endl;
00165 for ( ; it != caps.end(); ++it)
00166 {
00167 str << TAB2 << helixXML((*it));
00168 }
00169 str << TAB;
00170 return str.str();
00171 }
00172
00173 inline string helixXML( const Resolvable::constPtr &obj, Dep deptag_r )
00174 {
00175 stringstream out;
00176 Capabilities caps( obj->dep(deptag_r) );
00177 if ( ! caps.empty() )
00178 out << TAB << xml_tag_enclose(helixXML(caps), deptag_r.asString()) << endl;
00179 return out.str();
00180 }
00181
00182 std::string helixXML( const PoolItem &item )
00183 {
00184 const Resolvable::constPtr resolvable = item.resolvable();
00185 stringstream str;
00186 str << "<" << toLower (resolvable->kind().asString()) << ">" << endl;
00187 str << TAB << xml_tag_enclose (resolvable->name(), "name", true) << endl;
00188 str << TAB << xml_tag_enclose (item->vendor(), "vendor", true) << endl;
00189 str << TAB << xml_tag_enclose (item->buildtime().asSeconds(), "buildtime", true) << endl;
00190 if ( isKind<Package>(resolvable) ) {
00191 str << TAB << "<history>" << endl << TAB << "<update>" << endl;
00192 str << TAB2 << helixXML (resolvable->arch()) << endl;
00193 str << TAB2 << helixXML (resolvable->edition()) << endl;
00194 str << TAB << "</update>" << endl << TAB << "</history>" << endl;
00195 } else {
00196 str << TAB << helixXML (resolvable->arch()) << endl;
00197 str << TAB << helixXML (resolvable->edition()) << endl;
00198 }
00199 str << helixXML( resolvable, Dep::PROVIDES);
00200 str << helixXML( resolvable, Dep::PREREQUIRES);
00201 str << helixXML( resolvable, Dep::CONFLICTS);
00202 str << helixXML( resolvable, Dep::OBSOLETES);
00203 str << helixXML( resolvable, Dep::REQUIRES);
00204 str << helixXML( resolvable, Dep::RECOMMENDS);
00205 str << helixXML( resolvable, Dep::ENHANCES);
00206 str << helixXML( resolvable, Dep::SUPPLEMENTS);
00207 str << helixXML( resolvable, Dep::SUGGESTS);
00208
00209 str << "</" << toLower (resolvable->kind().asString()) << ">" << endl;
00210 return str.str();
00211 }
00212
00213
00214
00215 Testcase::Testcase()
00216 :dumpPath("/var/log/YaST2/solverTestcase")
00217 {
00218 }
00219
00220 Testcase::Testcase(const std::string & path)
00221 :dumpPath(path)
00222 {
00223 }
00224
00225
00226 Testcase::~Testcase()
00227 {
00228 }
00229
00230
00231 bool Testcase::createTestcasePool(const ResPool &pool)
00232 {
00233 PathInfo path (dumpPath);
00234
00235 if ( !path.isExist() ) {
00236 if (zypp::filesystem::mkdir (dumpPath)!=0) {
00237 ERR << "Cannot create directory " << dumpPath << endl;
00238 return false;
00239 }
00240 } else {
00241 if (!path.isDir()) {
00242 ERR << dumpPath << " is not a directory." << endl;
00243 return false;
00244 }
00245
00246 zypp::filesystem::clean_dir (dumpPath);
00247 }
00248
00249 RepositoryTable repoTable;
00250 HelixResolvable system (dumpPath + "/solver-system.xml.gz");
00251
00252 for ( ResPool::const_iterator it = pool.begin(); it != pool.end(); ++it )
00253 {
00254 if ( it->status().isInstalled() ) {
00255
00256 system.addResolvable (*it);
00257 } else {
00258
00259 Repository repo = it->resolvable()->satSolvable().repository();
00260 if (repoTable.find (repo) == repoTable.end()) {
00261 repoTable[repo] = new HelixResolvable(dumpPath + "/"
00262 + str::numstring((long)repo.id())
00263 + "-package.xml.gz");
00264 }
00265 repoTable[repo]->addResolvable (*it);
00266 }
00267 }
00268 return true;
00269 }
00270
00271 bool Testcase::createTestcase(Resolver & resolver, bool dumpPool, bool runSolver)
00272 {
00273 PathInfo path (dumpPath);
00274
00275 if ( !path.isExist() ) {
00276 if (zypp::filesystem::assert_dir (dumpPath)!=0) {
00277 ERR << "Cannot create directory " << dumpPath << endl;
00278 return false;
00279 }
00280 } else {
00281 if (!path.isDir()) {
00282 ERR << dumpPath << " is not a directory." << endl;
00283 return false;
00284 }
00285
00286 if (dumpPool)
00287 zypp::filesystem::clean_dir (dumpPath);
00288 }
00289
00290 if (runSolver) {
00291 zypp::base::LogControl::TmpLineWriter tempRedirect;
00292 zypp::base::LogControl::instance().logfile( dumpPath +"/y2log" );
00293 zypp::base::LogControl::TmpExcessive excessive;
00294
00295 resolver.resolvePool();
00296 }
00297
00298 ResPool pool = resolver.pool();
00299 RepositoryTable repoTable;
00300 PoolItemList items_to_install;
00301 PoolItemList items_to_remove;
00302 PoolItemList items_locked;
00303 PoolItemList items_keep;
00304 HelixResolvable_Ptr system = NULL;
00305
00306 if (dumpPool)
00307 system = new HelixResolvable(dumpPath + "/solver-system.xml.gz");
00308
00309 for ( ResPool::const_iterator it = pool.begin(); it != pool.end(); ++it )
00310 {
00311 Resolvable::constPtr res = it->resolvable();
00312
00313 if ( system && it->status().isInstalled() ) {
00314
00315 system->addResolvable (*it);
00316 } else {
00317
00318 Repository repo = it->resolvable()->satSolvable().repository();
00319 if (dumpPool) {
00320 if (repoTable.find (repo) == repoTable.end()) {
00321 repoTable[repo] = new HelixResolvable(dumpPath + "/"
00322 + str::numstring((long)repo.id())
00323 + "-package.xml.gz");
00324 }
00325 repoTable[repo]->addResolvable (*it);
00326 }
00327 }
00328
00329 if ( it->status().isToBeInstalled()
00330 && !(it->status().isBySolver())) {
00331 items_to_install.push_back (*it);
00332 }
00333 if ( it->status().isKept()
00334 && !(it->status().isBySolver())) {
00335 items_keep.push_back (*it);
00336 }
00337 if ( it->status().isToBeUninstalled()
00338 && !(it->status().isBySolver())) {
00339 items_to_remove.push_back (*it);
00340 }
00341 if ( it->status().isLocked()
00342 && !(it->status().isBySolver())) {
00343 items_locked.push_back (*it);
00344 }
00345 }
00346
00347
00348 HelixControl control (dumpPath + "/solver-test.xml",
00349 repoTable,
00350 ZConfig::instance().systemArchitecture(),
00351 pool.getRequestedLocales(),
00352 "solver-system.xml.gz",
00353 resolver.forceResolve(),
00354 resolver.onlyRequires(),
00355 resolver.ignoreAlreadyRecommended() );
00356
00357 for (PoolItemList::const_iterator iter = items_to_install.begin(); iter != items_to_install.end(); iter++) {
00358 control.installResolvable (iter->resolvable(), iter->status());
00359 }
00360
00361 for (PoolItemList::const_iterator iter = items_locked.begin(); iter != items_locked.end(); iter++) {
00362 control.lockResolvable (iter->resolvable(), iter->status());
00363 }
00364
00365 for (PoolItemList::const_iterator iter = items_keep.begin(); iter != items_keep.end(); iter++) {
00366 control.keepResolvable (iter->resolvable(), iter->status());
00367 }
00368
00369 for (PoolItemList::const_iterator iter = items_to_remove.begin(); iter != items_to_remove.end(); iter++) {
00370 control.deleteResolvable (iter->resolvable(), iter->status());
00371 }
00372
00373 control.addDependencies (resolver.extraRequires(), resolver.extraConflicts());
00374 control.addDependencies (SystemCheck::instance().requiredSystemCap(),
00375 SystemCheck::instance().conflictSystemCap());
00376 control.addUpgradeRepos( resolver.upgradeRepos() );
00377
00378 if (resolver.isUpgradeMode())
00379 control.distupgrade ();
00380 if (resolver.isUpdateMode())
00381 control.update();
00382 if (resolver.isVerifyingMode())
00383 control.verifySystem();
00384
00385 return true;
00386 }
00387
00388
00389
00390 HelixResolvable::HelixResolvable(const std::string & path)
00391 :dumpFile (path)
00392 {
00393 file = new ofgzstream(path.c_str());
00394 if (!file) {
00395 ZYPP_THROW (Exception( "Can't open " + path ) );
00396 }
00397
00398 *file << "<channel><subchannel>" << endl;
00399 }
00400
00401 HelixResolvable::~HelixResolvable()
00402 {
00403 *file << "</subchannel></channel>" << endl;
00404 delete(file);
00405 }
00406
00407
00408 void HelixResolvable::addResolvable(const PoolItem item)
00409 {
00410 *file << helixXML (item);
00411 }
00412
00413
00414
00415 HelixControl::HelixControl(const std::string & controlPath,
00416 const RepositoryTable & repoTable,
00417 const Arch & systemArchitecture,
00418 const LocaleSet &languages,
00419 const std::string & systemPath,
00420 const bool forceResolve,
00421 const bool onlyRequires,
00422 const bool ignorealreadyrecommended)
00423 :dumpFile (controlPath)
00424 {
00425 file = new ofstream(controlPath.c_str());
00426 if (!file) {
00427 ZYPP_THROW (Exception( "Can't open " + controlPath ) );
00428 }
00429
00430 *file << "<?xml version=\"1.0\"?>" << endl
00431 << "<!-- testcase generated by YaST -->" << endl
00432 << "<test>" << endl
00433 << "<setup arch=\"" << systemArchitecture << "\">" << endl
00434 << TAB << "<system file=\"" << systemPath << "\"/>" << endl << endl;
00435 for ( RepositoryTable::const_iterator it = repoTable.begin();
00436 it != repoTable.end(); ++it ) {
00437 RepoInfo repo = it->first.info();
00438 *file << TAB << "<!-- " << endl
00439 << TAB << "- alias : " << repo.alias() << endl;
00440 for ( RepoInfo::urls_const_iterator itUrl = repo.baseUrlsBegin();
00441 itUrl != repo.baseUrlsEnd();
00442 ++itUrl )
00443 {
00444 *file << TAB << "- url : " << *itUrl << endl;
00445 }
00446 *file << TAB << "- path : " << repo.path() << endl;
00447 *file << TAB << "- type : " << repo.type() << endl;
00448 *file << TAB << "- generated : " << (it->first.generatedTimestamp()).form( "%Y-%m-%d %H:%M:%S" ) << endl;
00449 *file << TAB << "- outdated : " << (it->first.suggestedExpirationTimestamp()).form( "%Y-%m-%d %H:%M:%S" ) << endl;
00450 *file << TAB << " -->" << endl;
00451
00452 *file << TAB << "<channel file=\"" << str::numstring((long)it->first.id())
00453 << "-package.xml.gz\" name=\"" << repo.alias() << "\""
00454 << " priority=\"" << repo.priority()
00455 << "\" />" << endl << endl;
00456 }
00457 for (LocaleSet::const_iterator iter = languages.begin(); iter != languages.end(); iter++) {
00458 *file << TAB << "<locale name=\"" << iter->code()
00459 << "\" />" << endl;
00460 }
00461
00462 if (forceResolve)
00463 *file << TAB << "<forceResolve/>" << endl;
00464 if (onlyRequires)
00465 *file << TAB << "<onlyRequires/>" << endl;
00466
00467 if (ignorealreadyrecommended)
00468 *file << TAB << "<ignorealreadyrecommended/>" << endl;
00469
00470 *file << "</setup>" << endl
00471 << "<trial>" << endl;
00472 }
00473
00474 HelixControl::HelixControl()
00475 :dumpFile ("/var/log/YaST2/solverTestcase/solver-test.xml")
00476 {
00477 HelixControl (dumpFile);
00478 }
00479
00480 HelixControl::~HelixControl()
00481 {
00482 *file << "</trial>" << endl
00483 << "</test>" << endl;
00484 delete(file);
00485 }
00486
00487 void HelixControl::installResolvable(const ResObject::constPtr &resObject,
00488 const ResStatus &status)
00489 {
00490 *file << "<install channel=\"" << resObject->repoInfo().alias() << "\" kind=\"" << toLower (resObject->kind().asString()) << "\""
00491 << " name=\"" << resObject->name() << "\"" << " arch=\"" << resObject->arch().asString() << "\""
00492 << " version=\"" << resObject->edition().version() << "\"" << " release=\"" << resObject->edition().release() << "\""
00493 << " status=\"" << status << "\""
00494 << "/>" << endl;
00495 }
00496
00497 void HelixControl::lockResolvable(const ResObject::constPtr &resObject,
00498 const ResStatus &status)
00499 {
00500 *file << "<lock channel=\"" << resObject->repoInfo().alias() << "\" kind=\"" << toLower (resObject->kind().asString()) << "\""
00501 << " name=\"" << resObject->name() << "\"" << " arch=\"" << resObject->arch().asString() << "\""
00502 << " version=\"" << resObject->edition().version() << "\"" << " release=\"" << resObject->edition().release() << "\""
00503 << " status=\"" << status << "\""
00504 << "/>" << endl;
00505 }
00506
00507 void HelixControl::keepResolvable(const ResObject::constPtr &resObject,
00508 const ResStatus &status)
00509 {
00510 *file << "<keep channel=\"" << resObject->repoInfo().alias() << "\" kind=\"" << toLower (resObject->kind().asString()) << "\""
00511 << " name=\"" << resObject->name() << "\"" << " arch=\"" << resObject->arch().asString() << "\""
00512 << " version=\"" << resObject->edition().version() << "\"" << " release=\"" << resObject->edition().release() << "\""
00513 << " status=\"" << status << "\""
00514 << "/>" << endl;
00515 }
00516
00517 void HelixControl::deleteResolvable(const ResObject::constPtr &resObject,
00518 const ResStatus &status)
00519 {
00520 *file << "<uninstall " << " kind=\"" << toLower (resObject->kind().asString()) << "\""
00521 << " name=\"" << resObject->name() << "\""
00522 << " status=\"" << status << "\""
00523 << "/>" << endl;
00524 }
00525
00526 void HelixControl::addDependencies (const CapabilitySet & capRequire, const CapabilitySet & capConflict)
00527 {
00528 for (CapabilitySet::const_iterator iter = capRequire.begin(); iter != capRequire.end(); iter++) {
00529 *file << "<addRequire " << " name=\"" << iter->asString() << "\"" << "/>" << endl;
00530 }
00531 for (CapabilitySet::const_iterator iter = capConflict.begin(); iter != capConflict.end(); iter++) {
00532 *file << "<addConflict " << " name=\"" << iter->asString() << "\"" << "/>" << endl;
00533 }
00534 }
00535
00536 void HelixControl::addUpgradeRepos( const std::set<Repository> & upgradeRepos_r )
00537 {
00538 for_( it, upgradeRepos_r.begin(), upgradeRepos_r.end() )
00539 {
00540 *file << "<upgradeRepo name=\"" << it->alias() << "\"/>" << endl;
00541 }
00542 }
00543
00544 void HelixControl::distupgrade()
00545 {
00546 *file << "<distupgrade/>" << endl;
00547 }
00548
00549 void HelixControl::verifySystem()
00550 {
00551 *file << "<verify/>" << endl;
00552 }
00553
00554 void HelixControl::update()
00555 {
00556 *file << "<update/>" << endl;
00557 }
00558
00559
00561 };
00564 };
00567 };