logging ResStatus into testcase
[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 std::string helixXML( const PoolItem_Ref &item )
163 {
164   const Resolvable::constPtr resolvable = item.resolvable();
165   stringstream str;
166   if ( isKind<Language>(resolvable) ) {
167       // language dependencies will be written in another part
168       return str.str();
169   }
170   
171   str << "<" << toLower (resolvable->kind().asString()) << ">" << endl;
172   str << TAB << xml_tag_enclose (resolvable->name(), "name", true) << endl;
173   str << TAB << xml_tag_enclose (item->vendor(), "vendor", true) << endl;    
174   if ( isKind<Package>(resolvable) ) {
175       str << TAB << "<history>" << endl << TAB << "<update>" << endl;
176       str << TAB2 << helixXML (resolvable->arch()) << endl;
177       str << TAB2 << helixXML (resolvable->edition()) << endl;      
178       str << TAB << "</update>" << endl << TAB << "</history>" << endl;
179   } else {
180       str << TAB << helixXML (resolvable->arch()) << endl;      
181       str << TAB << helixXML (resolvable->edition()) << endl;            
182   }
183   str << helixXML (resolvable->deps());              
184
185   str << "</" << toLower (resolvable->kind().asString()) << ">" << endl;  
186   return str.str();
187 }
188
189 //---------------------------------------------------------------------------
190
191 Testcase::Testcase()
192     :dumpPath("/var/log/YaST2/solverTestcase")    
193 {
194 }
195
196 Testcase::Testcase(const std::string & path)
197     :dumpPath(path)
198 {
199 }
200         
201
202 Testcase::~Testcase()
203 {
204 }
205
206
207 bool Testcase::createTestcase(Resolver & resolver, bool dumpPool, bool runSolver)
208 {
209     PathInfo path (dumpPath);
210
211     if ( !path.isExist() ) {
212         if (zypp::filesystem::mkdir (dumpPath)!=0) {
213             ERR << "Cannot create directory " << dumpPath << endl;
214             return false;
215         }
216     } else {
217         if (!path.isDir()) {
218             ERR << dumpPath << " is not a directory." << endl;
219             return false;
220         }
221         // remove old stuff if pool will be dump
222         if (dumpPool)   
223             zypp::filesystem::clean_dir (dumpPath);
224     }
225
226     if (runSolver) {    
227         zypp::base::LogControl::instance().logfile( dumpPath +"/y2log" );
228         zypp::base::LogControl::TmpExcessive excessive;
229
230         resolver.reset(true); // true = resetting all valid solverresults
231         resolver.resolvePool();
232
233         zypp::base::LogControl::instance().logfile( "/var/log/YaST2/y2log" );
234     }
235
236     ResPool pool        = resolver.pool();
237     RepositoryTable             repoTable;
238     PoolItemList        items_to_install;
239     PoolItemList        items_to_remove;
240     PoolItemList        items_locked;
241     PoolItemList        items_keep;    
242     PoolItemList        language;
243     HelixResolvable_Ptr system = NULL;
244
245     if (dumpPool)
246         system = new HelixResolvable(dumpPath + "/solver-system.xml");    
247
248     for ( ResPool::const_iterator it = pool.begin(); it != pool.end(); ++it )
249     {
250         Resolvable::constPtr res = it->resolvable();
251
252         if (isKind<Language>(res)) {
253             if ( it->status().isInstalled()
254                  || it->status().isToBeInstalled()) {
255                 language.push_back (*it);               
256             }
257         } else {
258             if ( system && it->status().isInstalled() ) {
259                 // system channel
260                 system->addResolvable (*it);
261             } else {
262                 // repo channels
263                 ResObject::constPtr repoItem = it->resolvable();
264                 Repository repo  = repoItem->repository();
265                 if (dumpPool) {
266                     if (repoTable.find (repo) == repoTable.end()) {
267                         repoTable[repo] = new HelixResolvable(dumpPath + "/"
268                                                               + numstring(repo.numericId())
269                                                               + "-package.xml");
270                     }
271                     repoTable[repo]->addResolvable (*it);
272                 }
273             }
274         
275             if ( it->status().isToBeInstalled()
276                  && !(it->status().isBySolver())) {
277                 items_to_install.push_back (*it);
278             }
279             if ( it->status().isKept()
280                  && !(it->status().isBySolver())) {
281                 items_keep.push_back (*it);
282             }       
283             if ( it->status().isToBeUninstalled()
284                  && !(it->status().isBySolver())) {
285                 items_to_remove.push_back (*it);
286             }
287             if ( it->status().isLocked()
288                  && !(it->status().isBySolver())) {
289                 items_locked.push_back (*it);
290             }
291         }
292     }
293
294     // writing control file "*-test.xml"
295
296     HelixControl control (dumpPath + "/solver-test.xml",
297                           repoTable,
298                           zypp_detail::defaultArchitecture(),
299                           language);
300
301     for (PoolItemList::const_iterator iter = items_to_install.begin(); iter != items_to_install.end(); iter++) {
302         control.installResolvable (iter->resolvable(), iter->status()); 
303     }
304
305     for (PoolItemList::const_iterator iter = items_locked.begin(); iter != items_locked.end(); iter++) {
306         control.lockResolvable (iter->resolvable(), iter->status());    
307     }
308     
309     for (PoolItemList::const_iterator iter = items_keep.begin(); iter != items_keep.end(); iter++) {
310         control.keepResolvable (iter->resolvable(), iter->status());    
311     }
312
313     for (PoolItemList::const_iterator iter = items_to_remove.begin(); iter != items_to_remove.end(); iter++) {
314         control.deleteResolvable (iter->resolvable(), iter->status());  
315     }
316
317     control.addDependencies (resolver.extraRequires(), resolver.extraConflicts());
318
319     return true;
320 }
321
322 //---------------------------------------------------------------------------
323
324 HelixResolvable::HelixResolvable(const std::string & path)
325     :dumpFile (path)    
326 {
327     file = new ofgzstream(path.c_str());
328     if (!file) {
329         ZYPP_THROW (Exception( "Can't open " + path ) );
330     }
331
332     *file << "<channel><subchannel>" << endl;
333 }
334
335 HelixResolvable::~HelixResolvable()
336 {
337     *file << "</subchannel></channel>" << endl;
338 }
339     
340
341 void HelixResolvable::addResolvable(const PoolItem_Ref item)
342 {
343     *file << helixXML (item);
344 }
345
346 //---------------------------------------------------------------------------
347
348 HelixControl::HelixControl(const std::string & controlPath,
349                            const RepositoryTable & repoTable,
350                            const Arch & systemArchitecture,
351                            const PoolItemList &languages,
352                            const std::string & systemPath)
353     :dumpFile (controlPath) 
354 {
355     file = new ofstream(controlPath.c_str());
356     if (!file) {
357         ZYPP_THROW (Exception( "Can't open " + controlPath ) );
358     }
359
360     *file << "<?xml version=\"1.0\"?>" << endl
361           << "<!-- testcase generated by YaST -->" << endl
362           << "<test>" << endl
363           << "<setup arch=\"" << systemArchitecture << "\">" << endl
364           << TAB << "<system file=\"" << systemPath << "\"/>" << endl << endl;
365     for ( RepositoryTable::const_iterator it = repoTable.begin();
366           it != repoTable.end(); ++it ) {
367         Repository repo = it->first;
368         *file << TAB << "<!-- " << endl
369               << TAB << "- alias       : " << repo.info().alias() << endl;
370         for ( RepoInfo::urls_const_iterator itUrl = repo.info().baseUrlsBegin();
371               itUrl != repo.info().baseUrlsEnd();
372               ++itUrl )
373         {
374             *file << TAB << "- url         : " << *itUrl << endl;
375         }       
376         *file << TAB << "- path        : " << repo.info().path() << endl;
377         *file << TAB << "- type        : " << repo.info().type() << endl;       
378         *file << TAB << " -->" << endl;
379         
380         *file << TAB << "<channel file=\"" << numstring(repo.numericId())
381               << "-package.xml\" name=\"" << numstring(repo.numericId())
382               << "\" />" << endl << endl;
383     }
384     for (PoolItemList::const_iterator iter = languages.begin(); iter != languages.end(); iter++) {
385         *file << TAB << "<locale name=\"" <<  iter->resolvable()->name()
386               << "\" />" << endl;
387     }    
388     *file << "</setup>" << endl
389           << "<trial>" << endl
390           << "<showpool all=\"yes\"/>" << endl
391           << "<establish/>" << endl
392           << "<showpool all=\"true\" prefix=\">!> ESTABLISHED:\"/>" << endl;
393 }
394
395 HelixControl::HelixControl()
396     :dumpFile ("/var/log/YaST2/solverTestcase/solver-test.xml")
397 {
398     HelixControl (dumpFile);
399 }
400
401 HelixControl::~HelixControl()
402 {
403     *file << "</trial>" << endl
404           << "</test>" << endl;
405 }
406
407 void HelixControl::installResolvable(const ResObject::constPtr &resObject,
408                                      const ResStatus &status)
409 {
410     Repository repo  = resObject->repository();
411     *file << "<install channel=\"" << numstring(repo.numericId()) << "\" kind=\"" << toLower (resObject->kind().asString()) << "\""
412           << " name=\"" << resObject->name() << "\"" << " arch=\"" << resObject->arch().asString() << "\""
413           << " version=\"" << resObject->edition().version() << "\"" << " release=\"" << resObject->edition().release() << "\""
414           << " status=\"" << status << "\"" 
415           << "/>" << endl;
416 }
417
418 void HelixControl::lockResolvable(const ResObject::constPtr &resObject,
419                                   const ResStatus &status)
420 {
421     Repository repo  = resObject->repository();
422     *file << "<lock channel=\"" << numstring(repo.numericId()) << "\" kind=\"" << toLower (resObject->kind().asString()) << "\""
423           << " name=\"" << resObject->name() << "\"" << " arch=\"" << resObject->arch().asString() << "\""
424           << " version=\"" << resObject->edition().version() << "\"" << " release=\"" << resObject->edition().release() << "\""
425           << " status=\"" << status << "\""     
426           << "/>" << endl;
427 }
428
429 void HelixControl::keepResolvable(const ResObject::constPtr &resObject,
430                                   const ResStatus &status)
431 {
432     Repository repo  = resObject->repository();
433     *file << "<keep channel=\"" << numstring(repo.numericId()) << "\" kind=\"" << toLower (resObject->kind().asString()) << "\""
434           << " name=\"" << resObject->name() << "\"" << " arch=\"" << resObject->arch().asString() << "\""
435           << " version=\"" << resObject->edition().version() << "\"" << " release=\"" << resObject->edition().release() << "\""
436           << " status=\"" << status << "\""     
437           << "/>" << endl;
438 }
439     
440 void HelixControl::deleteResolvable(const ResObject::constPtr &resObject,
441                                     const ResStatus &status)
442 {
443     Repository repo  = resObject->repository();    
444     *file << "<uninstall " << " kind=\"" << toLower (resObject->kind().asString()) << "\""
445           << " name=\"" << resObject->name() << "\""
446           << " status=\"" << status << "\"" 
447           << "/>" << endl;    
448 }
449
450 void HelixControl::addDependencies (const CapabilitySet & capRequire, const CapabilitySet & capConflict)
451 {
452     for (CapabilitySet::const_iterator iter = capRequire.begin(); iter != capRequire.end(); iter++) {
453         *file << "<addRequire " <<  " name=\"" << iter->asString() << "\"" << "/>" << endl;    
454     }
455     for (CapabilitySet::const_iterator iter = capConflict.begin(); iter != capConflict.end(); iter++) {
456         *file << "<addConflict " << " name=\"" << iter->asString() << "\"" << "/>" << endl;    
457     }    
458 }
459
460
461       ///////////////////////////////////////////////////////////////////
462     };// namespace detail
463     /////////////////////////////////////////////////////////////////////
464     /////////////////////////////////////////////////////////////////////
465   };// namespace solver
466   ///////////////////////////////////////////////////////////////////////
467   ///////////////////////////////////////////////////////////////////////
468 };// namespace zypp
469 /////////////////////////////////////////////////////////////////////////