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