- allow downgrade if buildtime is the same esp. if both are 0
[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 inline string helixXML( const Resolvable::constPtr &obj, Dep deptag_r )
176 {
177   stringstream out;
178   Capabilities caps( obj->dep(deptag_r) );
179   if ( ! caps.empty() )
180     out << TAB << xml_tag_enclose(helixXML(caps), deptag_r.asString()) << endl;
181   return out.str();
182 }
183
184 std::string helixXML( const PoolItem &item )
185 {
186   const Resolvable::constPtr resolvable = item.resolvable();
187   stringstream str;
188   str << "<" << toLower (resolvable->kind().asString()) << ">" << endl;
189   str << TAB << xml_tag_enclose (resolvable->name(), "name", true) << endl;
190   str << TAB << xml_tag_enclose (item->vendor(), "vendor", true) << endl;
191   str << TAB << xml_tag_enclose (item->buildtime().asSeconds(), "buildtime", true) << endl;  
192   if ( isKind<Package>(resolvable) ) {
193       str << TAB << "<history>" << endl << TAB << "<update>" << endl;
194       str << TAB2 << helixXML (resolvable->arch()) << endl;
195       str << TAB2 << helixXML (resolvable->edition()) << endl;
196       str << TAB << "</update>" << endl << TAB << "</history>" << endl;
197   } else {
198       str << TAB << helixXML (resolvable->arch()) << endl;
199       str << TAB << helixXML (resolvable->edition()) << endl;
200   }
201   str << helixXML( resolvable, Dep::PROVIDES);
202   str << helixXML( resolvable, Dep::PREREQUIRES);
203   str << helixXML( resolvable, Dep::CONFLICTS);
204   str << helixXML( resolvable, Dep::OBSOLETES);
205   str << helixXML( resolvable, Dep::FRESHENS);
206   str << helixXML( resolvable, Dep::REQUIRES);
207   str << helixXML( resolvable, Dep::RECOMMENDS);
208   str << helixXML( resolvable, Dep::ENHANCES);
209   str << helixXML( resolvable, Dep::SUPPLEMENTS);
210   str << helixXML( resolvable, Dep::SUGGESTS);
211
212   str << "</" << toLower (resolvable->kind().asString()) << ">" << endl;
213   return str.str();
214 }
215
216 //---------------------------------------------------------------------------
217
218 Testcase::Testcase()
219     :dumpPath("/var/log/YaST2/solverTestcase")
220 {
221 }
222
223 Testcase::Testcase(const std::string & path)
224     :dumpPath(path)
225 {
226 }
227
228
229 Testcase::~Testcase()
230 {
231 }
232
233
234 bool Testcase::createTestcasePool(const ResPool &pool)
235 {
236     PathInfo path (dumpPath);
237
238     if ( !path.isExist() ) {
239         if (zypp::filesystem::mkdir (dumpPath)!=0) {
240             ERR << "Cannot create directory " << dumpPath << endl;
241             return false;
242         }
243     } else {
244         if (!path.isDir()) {
245             ERR << dumpPath << " is not a directory." << endl;
246             return false;
247         }
248         // remove old stuff
249         zypp::filesystem::clean_dir (dumpPath);
250     }
251     
252     RepositoryTable             repoTable;
253     HelixResolvable     system (dumpPath + "/solver-system.xml.gz");    
254
255     for ( ResPool::const_iterator it = pool.begin(); it != pool.end(); ++it )
256     {
257         if ( it->status().isInstalled() ) {
258             // system channel
259             system.addResolvable (*it);
260         } else {
261             // repo channels
262             Repository repo  = it->resolvable()->satSolvable().repository();
263             if (repoTable.find (repo) == repoTable.end()) {
264                 repoTable[repo] = new HelixResolvable(dumpPath + "/"
265                                                       + str::numstring((long)repo.id())
266                                                       + "-package.xml.gz");
267             }
268             repoTable[repo]->addResolvable (*it);
269         }
270     }   
271     return true;
272 }
273
274 bool Testcase::createTestcase(Resolver & resolver, bool dumpPool, bool runSolver)
275 {
276     PathInfo path (dumpPath);
277
278     if ( !path.isExist() ) {
279         if (zypp::filesystem::mkdir (dumpPath)!=0) {
280             ERR << "Cannot create directory " << dumpPath << endl;
281             return false;
282         }
283     } else {
284         if (!path.isDir()) {
285             ERR << dumpPath << " is not a directory." << endl;
286             return false;
287         }
288         // remove old stuff if pool will be dump
289         if (dumpPool)
290             zypp::filesystem::clean_dir (dumpPath);
291     }
292
293     if (runSolver) {
294         zypp::base::LogControl::instance().logfile( dumpPath +"/y2log" );
295         zypp::base::LogControl::TmpExcessive excessive;
296
297         resolver.reset(true); // true = resetting all valid solverresults
298         resolver.resolvePool();
299
300         zypp::base::LogControl::instance().logfile( "/var/log/YaST2/y2log" );
301     }
302
303     ResPool pool        = resolver.pool();
304     RepositoryTable     repoTable;
305     PoolItemList        items_to_install;
306     PoolItemList        items_to_remove;
307     PoolItemList        items_locked;
308     PoolItemList        items_keep;
309     HelixResolvable_Ptr system = NULL;
310
311     if (dumpPool)
312         system = new HelixResolvable(dumpPath + "/solver-system.xml.gz");
313
314     for ( ResPool::const_iterator it = pool.begin(); it != pool.end(); ++it )
315     {
316         Resolvable::constPtr res = it->resolvable();
317
318         if ( system && it->status().isInstalled() ) {
319             // system channel
320             system->addResolvable (*it);
321         } else {
322             // repo channels
323             Repository repo  = it->resolvable()->satSolvable().repository();
324             if (dumpPool) {
325                 if (repoTable.find (repo) == repoTable.end()) {
326                     repoTable[repo] = new HelixResolvable(dumpPath + "/"
327                                                           + str::numstring((long)repo.id())
328                                                           + "-package.xml.gz");
329                 }
330                 repoTable[repo]->addResolvable (*it);
331             }
332         }
333
334         if ( it->status().isToBeInstalled()
335              && !(it->status().isBySolver())) {
336             items_to_install.push_back (*it);
337         }
338         if ( it->status().isKept()
339              && !(it->status().isBySolver())) {
340             items_keep.push_back (*it);
341         }
342         if ( it->status().isToBeUninstalled()
343              && !(it->status().isBySolver())) {
344             items_to_remove.push_back (*it);
345         }
346         if ( it->status().isLocked()
347              && !(it->status().isBySolver())) {
348             items_locked.push_back (*it);
349         }
350     }
351
352     // writing control file "*-test.xml"
353     HelixControl control (dumpPath + "/solver-test.xml",
354                           repoTable,
355                           ZConfig::instance().systemArchitecture(),
356                           pool.getRequestedLocales(),
357                           "solver-system.xml.gz",
358                           resolver.forceResolve());
359
360     for (PoolItemList::const_iterator iter = items_to_install.begin(); iter != items_to_install.end(); iter++) {
361         control.installResolvable (iter->resolvable(), iter->status());
362     }
363
364     for (PoolItemList::const_iterator iter = items_locked.begin(); iter != items_locked.end(); iter++) {
365         control.lockResolvable (iter->resolvable(), iter->status());
366     }
367
368     for (PoolItemList::const_iterator iter = items_keep.begin(); iter != items_keep.end(); iter++) {
369         control.keepResolvable (iter->resolvable(), iter->status());
370     }
371
372     for (PoolItemList::const_iterator iter = items_to_remove.begin(); iter != items_to_remove.end(); iter++) {
373         control.deleteResolvable (iter->resolvable(), iter->status());
374     }
375
376     control.addDependencies (resolver.extraRequires(), resolver.extraConflicts());
377
378     return true;
379 }
380
381 //---------------------------------------------------------------------------
382
383 HelixResolvable::HelixResolvable(const std::string & path)
384     :dumpFile (path)
385 {
386     file = new ofgzstream(path.c_str());
387     if (!file) {
388         ZYPP_THROW (Exception( "Can't open " + path ) );
389     }
390
391     *file << "<channel><subchannel>" << endl;
392 }
393
394 HelixResolvable::~HelixResolvable()
395 {
396     *file << "</subchannel></channel>" << endl;
397     delete(file);
398 }
399
400
401 void HelixResolvable::addResolvable(const PoolItem item)
402 {
403     *file << helixXML (item);
404 }
405
406 //---------------------------------------------------------------------------
407
408 HelixControl::HelixControl(const std::string & controlPath,
409                            const RepositoryTable & repoTable,
410                            const Arch & systemArchitecture,
411                            const LocaleSet &languages,
412                            const std::string & systemPath,
413                            const bool forceResolve)
414     :dumpFile (controlPath)
415 {
416     file = new ofstream(controlPath.c_str());
417     if (!file) {
418         ZYPP_THROW (Exception( "Can't open " + controlPath ) );
419     }
420
421     *file << "<?xml version=\"1.0\"?>" << endl
422           << "<!-- testcase generated by YaST -->" << endl
423           << "<test>" << endl
424           << "<setup arch=\"" << systemArchitecture << "\">" << endl
425           << TAB << "<system file=\"" << systemPath << "\"/>" << endl << endl;
426     for ( RepositoryTable::const_iterator it = repoTable.begin();
427           it != repoTable.end(); ++it ) {
428         RepoInfo repo = it->first.info();
429         *file << TAB << "<!-- " << endl
430               << TAB << "- alias       : " << repo.alias() << endl;
431         for ( RepoInfo::urls_const_iterator itUrl = repo.baseUrlsBegin();
432               itUrl != repo.baseUrlsEnd();
433               ++itUrl )
434         {
435             *file << TAB << "- url         : " << *itUrl << endl;
436         }
437         *file << TAB << "- path        : " << repo.path() << endl;
438         *file << TAB << "- type        : " << repo.type() << endl;
439         *file << TAB << " -->" << endl;
440
441         *file << TAB << "<channel file=\"" << str::numstring((long)it->first.id())
442               << "-package.xml.gz\" name=\"" << repo.alias()
443               << "\" />" << endl << endl;
444     }
445     for (LocaleSet::const_iterator iter = languages.begin(); iter != languages.end(); iter++) {
446         *file << TAB << "<locale name=\"" <<  iter->code()
447               << "\" />" << endl;
448     }
449     
450     if (forceResolve)
451         *file << TAB << "<forceResolve/>" << endl;
452     
453     *file << "</setup>" << endl
454           << "<trial>" << endl
455           << "<showpool all=\"yes\"/>" << endl;
456 }
457
458 HelixControl::HelixControl()
459     :dumpFile ("/var/log/YaST2/solverTestcase/solver-test.xml")
460 {
461     HelixControl (dumpFile);
462 }
463
464 HelixControl::~HelixControl()
465 {
466     *file << "</trial>" << endl
467           << "</test>" << endl;
468     delete(file);    
469 }
470
471 void HelixControl::installResolvable(const ResObject::constPtr &resObject,
472                                      const ResStatus &status)
473 {
474     *file << "<install channel=\"" << resObject->repoInfo().alias() << "\" kind=\"" << toLower (resObject->kind().asString()) << "\""
475           << " name=\"" << resObject->name() << "\"" << " arch=\"" << resObject->arch().asString() << "\""
476           << " version=\"" << resObject->edition().version() << "\"" << " release=\"" << resObject->edition().release() << "\""
477           << " status=\"" << status << "\""
478           << "/>" << endl;
479 }
480
481 void HelixControl::lockResolvable(const ResObject::constPtr &resObject,
482                                   const ResStatus &status)
483 {
484     *file << "<lock channel=\"" << resObject->repoInfo().alias() << "\" kind=\"" << toLower (resObject->kind().asString()) << "\""
485           << " name=\"" << resObject->name() << "\"" << " arch=\"" << resObject->arch().asString() << "\""
486           << " version=\"" << resObject->edition().version() << "\"" << " release=\"" << resObject->edition().release() << "\""
487           << " status=\"" << status << "\""
488           << "/>" << endl;
489 }
490
491 void HelixControl::keepResolvable(const ResObject::constPtr &resObject,
492                                   const ResStatus &status)
493 {
494     *file << "<keep channel=\"" << resObject->repoInfo().alias() << "\" kind=\"" << toLower (resObject->kind().asString()) << "\""
495           << " name=\"" << resObject->name() << "\"" << " arch=\"" << resObject->arch().asString() << "\""
496           << " version=\"" << resObject->edition().version() << "\"" << " release=\"" << resObject->edition().release() << "\""
497           << " status=\"" << status << "\""
498           << "/>" << endl;
499 }
500
501 void HelixControl::deleteResolvable(const ResObject::constPtr &resObject,
502                                     const ResStatus &status)
503 {
504     *file << "<uninstall " << " kind=\"" << toLower (resObject->kind().asString()) << "\""
505           << " name=\"" << resObject->name() << "\""
506           << " status=\"" << status << "\""
507           << "/>" << endl;
508 }
509
510 void HelixControl::addDependencies (const CapabilitySet & capRequire, const CapabilitySet & capConflict)
511 {
512     for (CapabilitySet::const_iterator iter = capRequire.begin(); iter != capRequire.end(); iter++) {
513         *file << "<addRequire " <<  " name=\"" << iter->asString() << "\"" << "/>" << endl;
514     }
515     for (CapabilitySet::const_iterator iter = capConflict.begin(); iter != capConflict.end(); iter++) {
516         *file << "<addConflict " << " name=\"" << iter->asString() << "\"" << "/>" << endl;
517     }
518 }
519
520
521       ///////////////////////////////////////////////////////////////////
522     };// namespace detail
523     /////////////////////////////////////////////////////////////////////
524     /////////////////////////////////////////////////////////////////////
525   };// namespace solver
526   ///////////////////////////////////////////////////////////////////////
527   ///////////////////////////////////////////////////////////////////////
528 };// namespace zypp
529 /////////////////////////////////////////////////////////////////////////