- Prepare ResPool being a singleton ontop of sat::Pool.
[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/PathInfo.h"
21 #include "zypp/Product.h"
22 #include "zypp/Package.h"
23 #include "zypp/Edition.h"
24 #include "zypp/target/store/xml_escape_parser.hpp"
25 #include "zypp/base/String.h"
26 #include "zypp/base/PtrTypes.h"
27 #include "zypp/Capabilities.h"
28
29
30 /////////////////////////////////////////////////////////////////////////
31 namespace zypp
32 { ///////////////////////////////////////////////////////////////////////
33
34   namespace zypp_detail
35   { /////////////////////////////////////////////////////////////////
36     Arch defaultArchitecture();
37     /////////////////////////////////////////////////////////////////
38   } // namespace zypp_detail
39   ///////////////////////////////////////////////////////////////////
40   ///////////////////////////////////////////////////////////////////////
41   namespace solver
42   { /////////////////////////////////////////////////////////////////////
43     /////////////////////////////////////////////////////////////////////
44     namespace detail
45     { ///////////////////////////////////////////////////////////////////
46
47 #define TAB "\t"
48 #define TAB2 "\t\t"
49
50 using namespace std;
51 using namespace zypp::str;
52
53 IMPL_PTR_TYPE(HelixResolvable);
54
55 static std::string xml_escape( const std::string &text )
56 {
57   iobind::parser::xml_escape_parser parser;
58   return parser.escape(text);
59 }
60
61 static std::string xml_tag_enclose( const std::string &text, const std::string &tag, bool escape = false )
62 {
63   string result;
64   result += "<" + tag + ">";
65
66   if ( escape)
67    result += xml_escape(text);
68   else
69    result += text;
70
71   result += "</" + tag + ">";
72   return result;
73 }
74
75
76 template<class T>
77 std::string helixXML( const T &obj ); //undefined
78
79 template<>
80 std::string helixXML( const Edition &edition )
81 {
82     stringstream str;
83     str << xml_tag_enclose(edition.version(), "version");
84     if (!edition.release().empty())
85         str << xml_tag_enclose(edition.release(), "release");
86     if (edition.epoch() != Edition::noepoch)
87         str << xml_tag_enclose(numstring(edition.epoch()), "epoch");
88     return str.str();
89 }
90
91 template<>
92 std::string helixXML( const Arch &arch )
93 {
94     stringstream str;
95     str << xml_tag_enclose(arch.asString(), "arch");
96     return str.str();
97 }
98
99 template<>
100 std::string helixXML( const Capability &cap )
101 {
102     stringstream str;
103     str << "<dep name='" << xml_escape(cap.asString()) << "'  />" << endl;
104
105     return str.str();
106 }
107
108 template<>
109 std::string helixXML( const Capabilities &caps )
110 {
111     stringstream str;
112     Capabilities::const_iterator it = caps.begin();
113     str << endl;
114     for ( ; it != caps.end(); ++it)
115     {
116         str << TAB2 << helixXML((*it));
117     }
118     str << TAB;
119     return str.str();
120 }
121
122 template<>
123 std::string helixXML( const CapabilitySet &caps )
124 {
125     stringstream str;
126     CapabilitySet::const_iterator it = caps.begin();
127     str << endl;
128     for ( ; it != caps.end(); ++it)
129     {
130         str << TAB2 << helixXML((*it));
131     }
132     str << TAB;
133     return str.str();
134 }
135
136
137 template<>
138 std::string helixXML( const Dependencies &dep )
139 {
140     stringstream str;
141     if ( dep[Dep::PROVIDES].size() > 0 )
142         str << TAB << xml_tag_enclose(helixXML(dep[Dep::PROVIDES]), "provides") << endl;
143     if ( dep[Dep::CONFLICTS].size() > 0 )
144         str << TAB << xml_tag_enclose(helixXML(dep[Dep::CONFLICTS]), "conflicts") << endl;
145     if ( dep[Dep::OBSOLETES].size() > 0 )
146         str << TAB << xml_tag_enclose(helixXML(dep[Dep::OBSOLETES]), "obsoletes") << endl;
147     if ( dep[Dep::FRESHENS].size() > 0 )
148         str << TAB << xml_tag_enclose(helixXML(dep[Dep::FRESHENS]), "freshens") << endl;
149     if ( dep[Dep::REQUIRES].size() > 0 )
150         str << TAB << xml_tag_enclose(helixXML(dep[Dep::REQUIRES]), "requires") << endl;
151     if ( dep[Dep::RECOMMENDS].size() > 0 )
152         str << TAB << xml_tag_enclose(helixXML(dep[Dep::RECOMMENDS]), "recommends") << endl;
153     if ( dep[Dep::ENHANCES].size() > 0 )
154         str << TAB << xml_tag_enclose(helixXML(dep[Dep::ENHANCES]), "enhances") << endl;
155     if ( dep[Dep::SUPPLEMENTS].size() > 0 )
156         str << TAB << xml_tag_enclose(helixXML(dep[Dep::SUPPLEMENTS]), "supplements") << endl;
157     if ( dep[Dep::SUGGESTS].size() > 0 )
158         str << TAB << xml_tag_enclose(helixXML(dep[Dep::SUGGESTS]), "suggests") << endl;
159     return str.str();
160 }
161
162 inline string helixXML( const Resolvable::constPtr &obj, Dep deptag_r )
163 {
164   stringstream out;
165   Capabilities caps( obj->dep(deptag_r) );
166   if ( ! caps.empty() )
167     out << "    " << xml_tag_enclose(helixXML(caps), deptag_r.asString()) << endl;
168   return out.str();
169 }
170
171 std::string helixXML( const PoolItem &item )
172 {
173   const Resolvable::constPtr resolvable = item.resolvable();
174   stringstream str;
175   if ( isKind<Language>(resolvable) ) {
176       // language dependencies will be written in another part
177       return str.str();
178   }
179
180   str << "<" << toLower (resolvable->kind().asString()) << ">" << endl;
181   str << TAB << xml_tag_enclose (resolvable->name(), "name", true) << endl;
182   str << TAB << xml_tag_enclose (item->vendor(), "vendor", true) << endl;
183   if ( isKind<Package>(resolvable) ) {
184       str << TAB << "<history>" << endl << TAB << "<update>" << endl;
185       str << TAB2 << helixXML (resolvable->arch()) << endl;
186       str << TAB2 << helixXML (resolvable->edition()) << endl;
187       str << TAB << "</update>" << endl << TAB << "</history>" << endl;
188   } else {
189       str << TAB << helixXML (resolvable->arch()) << endl;
190       str << TAB << helixXML (resolvable->edition()) << endl;
191   }
192   str << helixXML( resolvable, Dep::PROVIDES);
193   str << helixXML( resolvable, Dep::PREREQUIRES);
194   str << helixXML( resolvable, Dep::CONFLICTS);
195   str << helixXML( resolvable, Dep::OBSOLETES);
196   str << helixXML( resolvable, Dep::FRESHENS);
197   str << helixXML( resolvable, Dep::REQUIRES);
198   str << helixXML( resolvable, Dep::RECOMMENDS);
199   str << helixXML( resolvable, Dep::ENHANCES);
200   str << helixXML( resolvable, Dep::SUPPLEMENTS);
201   str << helixXML( resolvable, Dep::SUGGESTS);
202
203   str << "</" << toLower (resolvable->kind().asString()) << ">" << endl;
204   return str.str();
205 }
206
207 //---------------------------------------------------------------------------
208
209 Testcase::Testcase()
210     :dumpPath("/var/log/YaST2/solverTestcase")
211 {
212 }
213
214 Testcase::Testcase(const std::string & path)
215     :dumpPath(path)
216 {
217 }
218
219
220 Testcase::~Testcase()
221 {
222 }
223
224
225 bool Testcase::createTestcase(Resolver & resolver, bool dumpPool, bool runSolver)
226 {
227     PathInfo path (dumpPath);
228
229     if ( !path.isExist() ) {
230         if (zypp::filesystem::mkdir (dumpPath)!=0) {
231             ERR << "Cannot create directory " << dumpPath << endl;
232             return false;
233         }
234     } else {
235         if (!path.isDir()) {
236             ERR << dumpPath << " is not a directory." << endl;
237             return false;
238         }
239         // remove old stuff if pool will be dump
240         if (dumpPool)
241             zypp::filesystem::clean_dir (dumpPath);
242     }
243
244     if (runSolver) {
245         zypp::base::LogControl::instance().logfile( dumpPath +"/y2log" );
246         zypp::base::LogControl::TmpExcessive excessive;
247
248         resolver.reset(true); // true = resetting all valid solverresults
249         resolver.resolvePool();
250
251         zypp::base::LogControl::instance().logfile( "/var/log/YaST2/y2log" );
252     }
253
254     ResPool pool        = resolver.pool();
255     RepositoryTable             repoTable;
256     PoolItemList        items_to_install;
257     PoolItemList        items_to_remove;
258     PoolItemList        items_locked;
259     PoolItemList        items_keep;
260     PoolItemList        language;
261     HelixResolvable_Ptr system = NULL;
262
263     if (dumpPool)
264         system = new HelixResolvable(dumpPath + "/solver-system.xml.gz");
265
266     for ( ResPool::const_iterator it = pool.begin(); it != pool.end(); ++it )
267     {
268         Resolvable::constPtr res = it->resolvable();
269
270         if (isKind<Language>(res)) {
271             if ( it->status().isInstalled()
272                  || it->status().isToBeInstalled()) {
273                 language.push_back (*it);
274             }
275         } else {
276             if ( system && it->status().isInstalled() ) {
277                 // system channel
278                 system->addResolvable (*it);
279             } else {
280                 // repo channels
281                 ResObject::constPtr repoItem = it->resolvable();
282                 Repository repo  = repoItem->repository();
283                 if (dumpPool) {
284                     if (repoTable.find (repo) == repoTable.end()) {
285                         repoTable[repo] = new HelixResolvable(dumpPath + "/"
286                                                               + numstring(repo.numericId())
287                                                               + "-package.xml.gz");
288                     }
289                     repoTable[repo]->addResolvable (*it);
290                 }
291             }
292
293             if ( it->status().isToBeInstalled()
294                  && !(it->status().isBySolver())) {
295                 items_to_install.push_back (*it);
296             }
297             if ( it->status().isKept()
298                  && !(it->status().isBySolver())) {
299                 items_keep.push_back (*it);
300             }
301             if ( it->status().isToBeUninstalled()
302                  && !(it->status().isBySolver())) {
303                 items_to_remove.push_back (*it);
304             }
305             if ( it->status().isLocked()
306                  && !(it->status().isBySolver())) {
307                 items_locked.push_back (*it);
308             }
309         }
310     }
311
312     // writing control file "*-test.xml"
313
314     HelixControl control (dumpPath + "/solver-test.xml",
315                           repoTable,
316                           zypp_detail::defaultArchitecture(),
317                           language);
318
319     for (PoolItemList::const_iterator iter = items_to_install.begin(); iter != items_to_install.end(); iter++) {
320         control.installResolvable (iter->resolvable(), iter->status());
321     }
322
323     for (PoolItemList::const_iterator iter = items_locked.begin(); iter != items_locked.end(); iter++) {
324         control.lockResolvable (iter->resolvable(), iter->status());
325     }
326
327     for (PoolItemList::const_iterator iter = items_keep.begin(); iter != items_keep.end(); iter++) {
328         control.keepResolvable (iter->resolvable(), iter->status());
329     }
330
331     for (PoolItemList::const_iterator iter = items_to_remove.begin(); iter != items_to_remove.end(); iter++) {
332         control.deleteResolvable (iter->resolvable(), iter->status());
333     }
334
335     control.addDependencies (resolver.extraRequires(), resolver.extraConflicts());
336
337     return true;
338 }
339
340 //---------------------------------------------------------------------------
341
342 HelixResolvable::HelixResolvable(const std::string & path)
343     :dumpFile (path)
344 {
345     file = new ofgzstream(path.c_str());
346     if (!file) {
347         ZYPP_THROW (Exception( "Can't open " + path ) );
348     }
349
350     *file << "<channel><subchannel>" << endl;
351 }
352
353 HelixResolvable::~HelixResolvable()
354 {
355     *file << "</subchannel></channel>" << endl;
356 }
357
358
359 void HelixResolvable::addResolvable(const PoolItem item)
360 {
361     *file << helixXML (item);
362 }
363
364 //---------------------------------------------------------------------------
365
366 HelixControl::HelixControl(const std::string & controlPath,
367                            const RepositoryTable & repoTable,
368                            const Arch & systemArchitecture,
369                            const PoolItemList &languages,
370                            const std::string & systemPath)
371     :dumpFile (controlPath)
372 {
373     file = new ofstream(controlPath.c_str());
374     if (!file) {
375         ZYPP_THROW (Exception( "Can't open " + controlPath ) );
376     }
377
378     *file << "<?xml version=\"1.0\"?>" << endl
379           << "<!-- testcase generated by YaST -->" << endl
380           << "<test>" << endl
381           << "<setup arch=\"" << systemArchitecture << "\">" << endl
382           << TAB << "<system file=\"" << systemPath << "\"/>" << endl << endl;
383     for ( RepositoryTable::const_iterator it = repoTable.begin();
384           it != repoTable.end(); ++it ) {
385         Repository repo = it->first;
386         *file << TAB << "<!-- " << endl
387               << TAB << "- alias       : " << repo.info().alias() << endl;
388         for ( RepoInfo::urls_const_iterator itUrl = repo.info().baseUrlsBegin();
389               itUrl != repo.info().baseUrlsEnd();
390               ++itUrl )
391         {
392             *file << TAB << "- url         : " << *itUrl << endl;
393         }
394         *file << TAB << "- path        : " << repo.info().path() << endl;
395         *file << TAB << "- type        : " << repo.info().type() << endl;
396         *file << TAB << " -->" << endl;
397
398         *file << TAB << "<channel file=\"" << numstring(repo.numericId())
399               << "-package.xml.gz\" name=\"" << numstring(repo.numericId())
400               << "\" />" << endl << endl;
401     }
402     for (PoolItemList::const_iterator iter = languages.begin(); iter != languages.end(); iter++) {
403         *file << TAB << "<locale name=\"" <<  iter->resolvable()->name()
404               << "\" />" << endl;
405     }
406     *file << "</setup>" << endl
407           << "<trial>" << endl
408           << "<showpool all=\"yes\"/>" << endl
409           << "<establish/>" << endl
410           << "<showpool all=\"true\" prefix=\">!> ESTABLISHED:\"/>" << endl;
411 }
412
413 HelixControl::HelixControl()
414     :dumpFile ("/var/log/YaST2/solverTestcase/solver-test.xml")
415 {
416     HelixControl (dumpFile);
417 }
418
419 HelixControl::~HelixControl()
420 {
421     *file << "</trial>" << endl
422           << "</test>" << endl;
423 }
424
425 void HelixControl::installResolvable(const ResObject::constPtr &resObject,
426                                      const ResStatus &status)
427 {
428     Repository repo  = resObject->repository();
429     *file << "<install channel=\"" << numstring(repo.numericId()) << "\" kind=\"" << toLower (resObject->kind().asString()) << "\""
430           << " name=\"" << resObject->name() << "\"" << " arch=\"" << resObject->arch().asString() << "\""
431           << " version=\"" << resObject->edition().version() << "\"" << " release=\"" << resObject->edition().release() << "\""
432           << " status=\"" << status << "\""
433           << "/>" << endl;
434 }
435
436 void HelixControl::lockResolvable(const ResObject::constPtr &resObject,
437                                   const ResStatus &status)
438 {
439     Repository repo  = resObject->repository();
440     *file << "<lock channel=\"" << numstring(repo.numericId()) << "\" kind=\"" << toLower (resObject->kind().asString()) << "\""
441           << " name=\"" << resObject->name() << "\"" << " arch=\"" << resObject->arch().asString() << "\""
442           << " version=\"" << resObject->edition().version() << "\"" << " release=\"" << resObject->edition().release() << "\""
443           << " status=\"" << status << "\""
444           << "/>" << endl;
445 }
446
447 void HelixControl::keepResolvable(const ResObject::constPtr &resObject,
448                                   const ResStatus &status)
449 {
450     Repository repo  = resObject->repository();
451     *file << "<keep channel=\"" << numstring(repo.numericId()) << "\" kind=\"" << toLower (resObject->kind().asString()) << "\""
452           << " name=\"" << resObject->name() << "\"" << " arch=\"" << resObject->arch().asString() << "\""
453           << " version=\"" << resObject->edition().version() << "\"" << " release=\"" << resObject->edition().release() << "\""
454           << " status=\"" << status << "\""
455           << "/>" << endl;
456 }
457
458 void HelixControl::deleteResolvable(const ResObject::constPtr &resObject,
459                                     const ResStatus &status)
460 {
461     Repository repo  = resObject->repository();
462     *file << "<uninstall " << " kind=\"" << toLower (resObject->kind().asString()) << "\""
463           << " name=\"" << resObject->name() << "\""
464           << " status=\"" << status << "\""
465           << "/>" << endl;
466 }
467
468 void HelixControl::addDependencies (const CapabilitySet & capRequire, const CapabilitySet & capConflict)
469 {
470     for (CapabilitySet::const_iterator iter = capRequire.begin(); iter != capRequire.end(); iter++) {
471         *file << "<addRequire " <<  " name=\"" << iter->asString() << "\"" << "/>" << endl;
472     }
473     for (CapabilitySet::const_iterator iter = capConflict.begin(); iter != capConflict.end(); iter++) {
474         *file << "<addConflict " << " name=\"" << iter->asString() << "\"" << "/>" << endl;
475     }
476 }
477
478
479       ///////////////////////////////////////////////////////////////////
480     };// namespace detail
481     /////////////////////////////////////////////////////////////////////
482     /////////////////////////////////////////////////////////////////////
483   };// namespace solver
484   ///////////////////////////////////////////////////////////////////////
485   ///////////////////////////////////////////////////////////////////////
486 };// namespace zypp
487 /////////////////////////////////////////////////////////////////////////