libzypp Repository class has been exchanged by the SAT repo
[platform/upstream/libzypp.git] / zypp / solver / detail / Testcase.cc
1 /*---------------------------------------------------------------------\
2 |                          ____ _   __ __ ___                          |
3 |                         |__  / \ / / . \ . \                         |
4 |                           / / \ V /|  _/  _/                         |
5 |                          / /__ | | | | | |                           |
6 |                         /_____||_| |_| |_|                           |
7 |                                                                      |
8 \---------------------------------------------------------------------*/
9 /** \file       zypp/solver/detail/Testcase.cc
10  *
11 */
12 #include <iostream>
13 #include <fstream>
14 #include <sstream>
15 #include <streambuf>
16
17 #include "zypp/solver/detail/Testcase.h"
18 #include "zypp/base/Logger.h"
19 #include "zypp/base/LogControl.h"
20 #include "zypp/ZConfig.h"
21 #include "zypp/PathInfo.h"
22 #include "zypp/Product.h"
23 #include "zypp/Package.h"
24 #include "zypp/Edition.h"
25 #include "zypp/parser/xml_escape_parser.hpp"
26 #include "zypp/base/String.h"
27 #include "zypp/base/PtrTypes.h"
28 #include "zypp/Capabilities.h"
29 #include "zypp/sat/Solvable.h"
30 #include "zypp/sat/detail/PoolImpl.h"
31
32
33 /////////////////////////////////////////////////////////////////////////
34 namespace zypp
35 { ///////////////////////////////////////////////////////////////////////
36   ///////////////////////////////////////////////////////////////////////
37   namespace solver
38   { /////////////////////////////////////////////////////////////////////
39     /////////////////////////////////////////////////////////////////////
40     namespace detail
41     { ///////////////////////////////////////////////////////////////////
42
43 #define TAB "\t"
44 #define TAB2 "\t\t"
45
46 using namespace std;
47 using namespace zypp::str;
48
49 IMPL_PTR_TYPE(HelixResolvable);
50
51 static std::string xml_escape( const std::string &text )
52 {
53   iobind::parser::xml_escape_parser parser;
54   return parser.escape(text);
55 }
56
57 static std::string xml_tag_enclose( const std::string &text, const std::string &tag, bool escape = false )
58 {
59   string result;
60   result += "<" + tag + ">";
61
62   if ( escape)
63    result += xml_escape(text);
64   else
65    result += text;
66
67   result += "</" + tag + ">";
68   return result;
69 }
70
71
72 template<class T>
73 std::string helixXML( const T &obj ); //undefined
74
75 template<>
76 std::string helixXML( const Edition &edition )
77 {
78     stringstream str;
79     str << xml_tag_enclose(edition.version(), "version");
80     if (!edition.release().empty())
81         str << xml_tag_enclose(edition.release(), "release");
82     if (edition.epoch() != Edition::noepoch)
83         str << xml_tag_enclose(numstring(edition.epoch()), "epoch");
84     return str.str();
85 }
86
87 template<>
88 std::string helixXML( const Arch &arch )
89 {
90     stringstream str;
91     str << xml_tag_enclose(arch.asString(), "arch");
92     return str.str();
93 }
94
95 template<>
96 std::string helixXML( const Capability &cap )
97 {
98     stringstream str;
99     CapDetail detail = cap.detail();
100     if (detail.isSimple()) {
101         if (detail.isVersioned()) {
102             str << "<dep name='" << xml_escape(detail.name().asString()) << "'"
103                 << " op='" << xml_escape(detail.op().asString()) << "'"
104                 << " version='" <<  xml_escape(detail.ed().version()) << "'";
105             if (!detail.ed().release().empty())
106                 str << " release='" << xml_escape(detail.ed().release()) << "'";
107             if (detail.ed().epoch() != Edition::noepoch)
108                 str << " epoch='" << xml_escape(numstring(detail.ed().epoch())) << "'";    
109             str << " />" << endl;       
110         } else {
111             str << "<dep name='" << xml_escape(cap.asString()) << "' />" << endl;
112         }
113     } else if (detail.isExpression()) {
114         if (detail.capRel() == CapDetail::CAP_AND
115             && detail.lhs().detail().isNamed()
116             && detail.rhs().detail().isNamed()) {
117             // packageand dependency
118             str << "<dep name='packageand("
119                 << IdString(detail.lhs().id()) << ","           
120                 << IdString(detail.rhs().id()) << ")' />" << endl;
121         } else {
122             // modalias ?
123             IdString packageName;
124             if (detail.capRel() == CapDetail::CAP_AND) {
125                 packageName = IdString(detail.lhs().id());
126                 detail = detail.rhs().detail();
127             }
128             if (detail.capRel() == CapDetail::CAP_NAMESPACE
129                 && detail.lhs().id() == NAMESPACE_MODALIAS) {
130                 str << "<dep name='modalias(";
131                 if (!packageName.empty())
132                     str << packageName << ":";
133                 str << IdString(detail.rhs().id()) << ")' />" << endl;
134             } else {
135                 str << "<!--- ignoring '" << xml_escape(cap.asString()) << "' -->" << endl;     
136                 MIL << "ignoring " << cap << " cause this format will be supported" << endl;
137             }
138         }
139     } else {
140         str << "<!--- ignoring '" << xml_escape(cap.asString()) << "' -->" << endl;     
141         MIL << "ignoring " << cap << " cause this format will be supported" << endl;
142     }
143
144     return str.str();
145 }
146
147 template<>
148 std::string helixXML( const Capabilities &caps )
149 {
150     stringstream str;
151     Capabilities::const_iterator it = caps.begin();
152     str << endl;
153     for ( ; it != caps.end(); ++it)
154     {
155         str << TAB2 << helixXML((*it));
156     }
157     str << TAB;
158     return str.str();
159 }
160
161 template<>
162 std::string helixXML( const CapabilitySet &caps )
163 {
164     stringstream str;
165     CapabilitySet::const_iterator it = caps.begin();
166     str << endl;
167     for ( ; it != caps.end(); ++it)
168     {
169         str << TAB2 << helixXML((*it));
170     }
171     str << TAB;
172     return str.str();
173 }
174
175
176 template<>
177 std::string helixXML( const Dependencies &dep )
178 {
179     stringstream str;
180     if ( dep[Dep::PROVIDES].size() > 0 )
181         str << TAB << xml_tag_enclose(helixXML(dep[Dep::PROVIDES]), "provides") << endl;
182     if ( dep[Dep::CONFLICTS].size() > 0 )
183         str << TAB << xml_tag_enclose(helixXML(dep[Dep::CONFLICTS]), "conflicts") << endl;
184     if ( dep[Dep::OBSOLETES].size() > 0 )
185         str << TAB << xml_tag_enclose(helixXML(dep[Dep::OBSOLETES]), "obsoletes") << endl;
186     if ( dep[Dep::FRESHENS].size() > 0 )
187         str << TAB << xml_tag_enclose(helixXML(dep[Dep::FRESHENS]), "freshens") << endl;
188     if ( dep[Dep::REQUIRES].size() > 0 )
189         str << TAB << xml_tag_enclose(helixXML(dep[Dep::REQUIRES]), "requires") << endl;
190     if ( dep[Dep::RECOMMENDS].size() > 0 )
191         str << TAB << xml_tag_enclose(helixXML(dep[Dep::RECOMMENDS]), "recommends") << endl;
192     if ( dep[Dep::ENHANCES].size() > 0 )
193         str << TAB << xml_tag_enclose(helixXML(dep[Dep::ENHANCES]), "enhances") << endl;
194     if ( dep[Dep::SUPPLEMENTS].size() > 0 )
195         str << TAB << xml_tag_enclose(helixXML(dep[Dep::SUPPLEMENTS]), "supplements") << endl;
196     if ( dep[Dep::SUGGESTS].size() > 0 )
197         str << TAB << xml_tag_enclose(helixXML(dep[Dep::SUGGESTS]), "suggests") << endl;
198     return str.str();
199 }
200
201 inline string helixXML( const Resolvable::constPtr &obj, Dep deptag_r )
202 {
203   stringstream out;
204   Capabilities caps( obj->dep(deptag_r) );
205   if ( ! caps.empty() )
206     out << TAB << xml_tag_enclose(helixXML(caps), deptag_r.asString()) << endl;
207   return out.str();
208 }
209
210 std::string helixXML( const PoolItem &item )
211 {
212   const Resolvable::constPtr resolvable = item.resolvable();
213   stringstream str;
214   str << "<" << toLower (resolvable->kind().asString()) << ">" << endl;
215   str << TAB << xml_tag_enclose (resolvable->name(), "name", true) << endl;
216   str << TAB << xml_tag_enclose (item->vendor(), "vendor", true) << endl;
217   if ( isKind<Package>(resolvable) ) {
218       str << TAB << "<history>" << endl << TAB << "<update>" << endl;
219       str << TAB2 << helixXML (resolvable->arch()) << endl;
220       str << TAB2 << helixXML (resolvable->edition()) << endl;
221       str << TAB << "</update>" << endl << TAB << "</history>" << endl;
222   } else {
223       str << TAB << helixXML (resolvable->arch()) << endl;
224       str << TAB << helixXML (resolvable->edition()) << endl;
225   }
226   str << helixXML( resolvable, Dep::PROVIDES);
227   str << helixXML( resolvable, Dep::PREREQUIRES);
228   str << helixXML( resolvable, Dep::CONFLICTS);
229   str << helixXML( resolvable, Dep::OBSOLETES);
230   str << helixXML( resolvable, Dep::FRESHENS);
231   str << helixXML( resolvable, Dep::REQUIRES);
232   str << helixXML( resolvable, Dep::RECOMMENDS);
233   str << helixXML( resolvable, Dep::ENHANCES);
234   str << helixXML( resolvable, Dep::SUPPLEMENTS);
235   str << helixXML( resolvable, Dep::SUGGESTS);
236
237   str << "</" << toLower (resolvable->kind().asString()) << ">" << endl;
238   return str.str();
239 }
240
241 //---------------------------------------------------------------------------
242
243 Testcase::Testcase()
244     :dumpPath("/var/log/YaST2/solverTestcase")
245 {
246 }
247
248 Testcase::Testcase(const std::string & path)
249     :dumpPath(path)
250 {
251 }
252
253
254 Testcase::~Testcase()
255 {
256 }
257
258
259 bool Testcase::createTestcasePool(const ResPool &pool)
260 {
261     PathInfo path (dumpPath);
262
263     if ( !path.isExist() ) {
264         if (zypp::filesystem::mkdir (dumpPath)!=0) {
265             ERR << "Cannot create directory " << dumpPath << endl;
266             return false;
267         }
268     } else {
269         if (!path.isDir()) {
270             ERR << dumpPath << " is not a directory." << endl;
271             return false;
272         }
273         // remove old stuff
274         zypp::filesystem::clean_dir (dumpPath);
275     }
276     
277     RepositoryTable             repoTable;
278     HelixResolvable     system (dumpPath + "/solver-system.xml.gz");    
279
280     for ( ResPool::const_iterator it = pool.begin(); it != pool.end(); ++it )
281     {
282         if ( it->status().isInstalled() ) {
283             // system channel
284             system.addResolvable (*it);
285         } else {
286             // repo channels
287             Repository repo  = it->resolvable()->satSolvable().repository();
288             if (repoTable.find (repo) == repoTable.end()) {
289                 repoTable[repo] = new HelixResolvable(dumpPath + "/"
290                                                       + str::numstring((long)repo.id())
291                                                       + "-package.xml.gz");
292             }
293             repoTable[repo]->addResolvable (*it);
294         }
295     }   
296     return true;
297 }
298
299 bool Testcase::createTestcase(Resolver & resolver, bool dumpPool, bool runSolver)
300 {
301     PathInfo path (dumpPath);
302
303     if ( !path.isExist() ) {
304         if (zypp::filesystem::mkdir (dumpPath)!=0) {
305             ERR << "Cannot create directory " << dumpPath << endl;
306             return false;
307         }
308     } else {
309         if (!path.isDir()) {
310             ERR << dumpPath << " is not a directory." << endl;
311             return false;
312         }
313         // remove old stuff if pool will be dump
314         if (dumpPool)
315             zypp::filesystem::clean_dir (dumpPath);
316     }
317
318     if (runSolver) {
319         zypp::base::LogControl::instance().logfile( dumpPath +"/y2log" );
320         zypp::base::LogControl::TmpExcessive excessive;
321
322         resolver.reset(true); // true = resetting all valid solverresults
323         resolver.resolvePool();
324
325         zypp::base::LogControl::instance().logfile( "/var/log/YaST2/y2log" );
326     }
327
328     ResPool pool        = resolver.pool();
329     RepositoryTable     repoTable;
330     PoolItemList        items_to_install;
331     PoolItemList        items_to_remove;
332     PoolItemList        items_locked;
333     PoolItemList        items_keep;
334     PoolItemList        language;
335     HelixResolvable_Ptr system = NULL;
336
337     if (dumpPool)
338         system = new HelixResolvable(dumpPath + "/solver-system.xml.gz");
339
340     for ( ResPool::const_iterator it = pool.begin(); it != pool.end(); ++it )
341     {
342         Resolvable::constPtr res = it->resolvable();
343
344 #warning NO MORE LANGUAGE RESOLVABLE
345         // - use pools list of requested locales and pass it as 'LocaleList language'
346         // - restore the list via Pool::setRequestedLocales.
347 #if 0
348         if (isKind<Language>(res)) {
349             if ( it->status().isInstalled()
350                  || it->status().isToBeInstalled()) {
351                 language.push_back (*it);
352             }
353         } else {
354 #endif
355             if ( system && it->status().isInstalled() ) {
356                 // system channel
357                 system->addResolvable (*it);
358             } else {
359                 // repo channels
360                 Repository repo  = it->resolvable()->satSolvable().repository();
361                 if (dumpPool) {
362                     if (repoTable.find (repo) == repoTable.end()) {
363                         repoTable[repo] = new HelixResolvable(dumpPath + "/"
364                                                               + str::numstring((long)repo.id())
365                                                               + "-package.xml.gz");
366                     }
367                     repoTable[repo]->addResolvable (*it);
368                 }
369             }
370
371             if ( it->status().isToBeInstalled()
372                  && !(it->status().isBySolver())) {
373                 items_to_install.push_back (*it);
374             }
375             if ( it->status().isKept()
376                  && !(it->status().isBySolver())) {
377                 items_keep.push_back (*it);
378             }
379             if ( it->status().isToBeUninstalled()
380                  && !(it->status().isBySolver())) {
381                 items_to_remove.push_back (*it);
382             }
383             if ( it->status().isLocked()
384                  && !(it->status().isBySolver())) {
385                 items_locked.push_back (*it);
386             }
387     }
388
389     // writing control file "*-test.xml"
390
391     HelixControl control (dumpPath + "/solver-test.xml",
392                           repoTable,
393                           ZConfig::instance().systemArchitecture(),
394                           language);
395
396     for (PoolItemList::const_iterator iter = items_to_install.begin(); iter != items_to_install.end(); iter++) {
397         control.installResolvable (iter->resolvable(), iter->status());
398     }
399
400     for (PoolItemList::const_iterator iter = items_locked.begin(); iter != items_locked.end(); iter++) {
401         control.lockResolvable (iter->resolvable(), iter->status());
402     }
403
404     for (PoolItemList::const_iterator iter = items_keep.begin(); iter != items_keep.end(); iter++) {
405         control.keepResolvable (iter->resolvable(), iter->status());
406     }
407
408     for (PoolItemList::const_iterator iter = items_to_remove.begin(); iter != items_to_remove.end(); iter++) {
409         control.deleteResolvable (iter->resolvable(), iter->status());
410     }
411
412     control.addDependencies (resolver.extraRequires(), resolver.extraConflicts());
413
414     return true;
415 }
416
417 //---------------------------------------------------------------------------
418
419 HelixResolvable::HelixResolvable(const std::string & path)
420     :dumpFile (path)
421 {
422     file = new ofgzstream(path.c_str());
423     if (!file) {
424         ZYPP_THROW (Exception( "Can't open " + path ) );
425     }
426
427     *file << "<channel><subchannel>" << endl;
428 }
429
430 HelixResolvable::~HelixResolvable()
431 {
432     *file << "</subchannel></channel>" << endl;
433     delete(file);
434 }
435
436
437 void HelixResolvable::addResolvable(const PoolItem item)
438 {
439     *file << helixXML (item);
440 }
441
442 //---------------------------------------------------------------------------
443
444 HelixControl::HelixControl(const std::string & controlPath,
445                            const RepositoryTable & repoTable,
446                            const Arch & systemArchitecture,
447                            const PoolItemList &languages,
448                            const std::string & systemPath)
449     :dumpFile (controlPath)
450 {
451     file = new ofstream(controlPath.c_str());
452     if (!file) {
453         ZYPP_THROW (Exception( "Can't open " + controlPath ) );
454     }
455
456     *file << "<?xml version=\"1.0\"?>" << endl
457           << "<!-- testcase generated by YaST -->" << endl
458           << "<test>" << endl
459           << "<setup arch=\"" << systemArchitecture << "\">" << endl
460           << TAB << "<system file=\"" << systemPath << "\"/>" << endl << endl;
461     for ( RepositoryTable::const_iterator it = repoTable.begin();
462           it != repoTable.end(); ++it ) {
463         RepoInfo repo = it->first.info();
464         *file << TAB << "<!-- " << endl
465               << TAB << "- alias       : " << repo.alias() << endl;
466         for ( RepoInfo::urls_const_iterator itUrl = repo.baseUrlsBegin();
467               itUrl != repo.baseUrlsEnd();
468               ++itUrl )
469         {
470             *file << TAB << "- url         : " << *itUrl << endl;
471         }
472         *file << TAB << "- path        : " << repo.path() << endl;
473         *file << TAB << "- type        : " << repo.type() << endl;
474         *file << TAB << " -->" << endl;
475
476         *file << TAB << "<channel file=\"" << str::numstring((long)it->first.id())
477               << "-package.xml.gz\" name=\"" << repo.alias()
478               << "\" />" << endl << endl;
479     }
480     for (PoolItemList::const_iterator iter = languages.begin(); iter != languages.end(); iter++) {
481         *file << TAB << "<locale name=\"" <<  iter->resolvable()->name()
482               << "\" />" << endl;
483     }
484     *file << "</setup>" << endl
485           << "<trial>" << endl
486           << "<showpool all=\"yes\"/>" << endl;
487 }
488
489 HelixControl::HelixControl()
490     :dumpFile ("/var/log/YaST2/solverTestcase/solver-test.xml")
491 {
492     HelixControl (dumpFile);
493 }
494
495 HelixControl::~HelixControl()
496 {
497     *file << "</trial>" << endl
498           << "</test>" << endl;
499     delete(file);    
500 }
501
502 void HelixControl::installResolvable(const ResObject::constPtr &resObject,
503                                      const ResStatus &status)
504 {
505     *file << "<install channel=\"" << resObject->repoInfo().alias() << "\" kind=\"" << toLower (resObject->kind().asString()) << "\""
506           << " name=\"" << resObject->name() << "\"" << " arch=\"" << resObject->arch().asString() << "\""
507           << " version=\"" << resObject->edition().version() << "\"" << " release=\"" << resObject->edition().release() << "\""
508           << " status=\"" << status << "\""
509           << "/>" << endl;
510 }
511
512 void HelixControl::lockResolvable(const ResObject::constPtr &resObject,
513                                   const ResStatus &status)
514 {
515     *file << "<lock channel=\"" << resObject->repoInfo().alias() << "\" kind=\"" << toLower (resObject->kind().asString()) << "\""
516           << " name=\"" << resObject->name() << "\"" << " arch=\"" << resObject->arch().asString() << "\""
517           << " version=\"" << resObject->edition().version() << "\"" << " release=\"" << resObject->edition().release() << "\""
518           << " status=\"" << status << "\""
519           << "/>" << endl;
520 }
521
522 void HelixControl::keepResolvable(const ResObject::constPtr &resObject,
523                                   const ResStatus &status)
524 {
525     *file << "<keep channel=\"" << resObject->repoInfo().alias() << "\" kind=\"" << toLower (resObject->kind().asString()) << "\""
526           << " name=\"" << resObject->name() << "\"" << " arch=\"" << resObject->arch().asString() << "\""
527           << " version=\"" << resObject->edition().version() << "\"" << " release=\"" << resObject->edition().release() << "\""
528           << " status=\"" << status << "\""
529           << "/>" << endl;
530 }
531
532 void HelixControl::deleteResolvable(const ResObject::constPtr &resObject,
533                                     const ResStatus &status)
534 {
535     *file << "<uninstall " << " kind=\"" << toLower (resObject->kind().asString()) << "\""
536           << " name=\"" << resObject->name() << "\""
537           << " status=\"" << status << "\""
538           << "/>" << endl;
539 }
540
541 void HelixControl::addDependencies (const CapabilitySet & capRequire, const CapabilitySet & capConflict)
542 {
543     for (CapabilitySet::const_iterator iter = capRequire.begin(); iter != capRequire.end(); iter++) {
544         *file << "<addRequire " <<  " name=\"" << iter->asString() << "\"" << "/>" << endl;
545     }
546     for (CapabilitySet::const_iterator iter = capConflict.begin(); iter != capConflict.end(); iter++) {
547         *file << "<addConflict " << " name=\"" << iter->asString() << "\"" << "/>" << endl;
548     }
549 }
550
551
552       ///////////////////////////////////////////////////////////////////
553     };// namespace detail
554     /////////////////////////////////////////////////////////////////////
555     /////////////////////////////////////////////////////////////////////
556   };// namespace solver
557   ///////////////////////////////////////////////////////////////////////
558   ///////////////////////////////////////////////////////////////////////
559 };// namespace zypp
560 /////////////////////////////////////////////////////////////////////////