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