- fix packageand() in testcase generation
[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/XmlEscape.h"
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 #include "zypp/solver/detail/SystemCheck.h"
32
33
34 /////////////////////////////////////////////////////////////////////////
35 namespace zypp
36 { ///////////////////////////////////////////////////////////////////////
37   ///////////////////////////////////////////////////////////////////////
38   namespace solver
39   { /////////////////////////////////////////////////////////////////////
40     /////////////////////////////////////////////////////////////////////
41     namespace detail
42     { ///////////////////////////////////////////////////////////////////
43
44 #define TAB "\t"
45 #define TAB2 "\t\t"
46
47 using namespace std;
48 using namespace zypp::str;
49
50 IMPL_PTR_TYPE(HelixResolvable);
51
52 inline std::string xml_escape( const std::string &text )
53 {
54   return zypp::xml::escape(text);
55 }
56
57 inline 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             }
137         }
138     } else {
139         str << "<!--- ignoring '" << xml_escape(cap.asString()) << "' -->" << endl;
140     }
141
142     return str.str();
143 }
144
145 template<>
146 std::string helixXML( const Capabilities &caps )
147 {
148     stringstream str;
149     Capabilities::const_iterator it = caps.begin();
150     str << endl;
151     for ( ; it != caps.end(); ++it)
152     {
153         str << TAB2 << helixXML((*it));
154     }
155     str << TAB;
156     return str.str();
157 }
158
159 template<>
160 std::string helixXML( const CapabilitySet &caps )
161 {
162     stringstream str;
163     CapabilitySet::const_iterator it = caps.begin();
164     str << endl;
165     for ( ; it != caps.end(); ++it)
166     {
167         str << TAB2 << helixXML((*it));
168     }
169     str << TAB;
170     return str.str();
171 }
172
173 inline string helixXML( const Resolvable::constPtr &obj, Dep deptag_r )
174 {
175   stringstream out;
176   Capabilities caps( obj->dep(deptag_r) );
177   if ( ! caps.empty() )
178     out << TAB << xml_tag_enclose(helixXML(caps), deptag_r.asString()) << endl;
179   return out.str();
180 }
181
182 std::string helixXML( const PoolItem &item )
183 {
184   const Resolvable::constPtr resolvable = item.resolvable();
185   stringstream str;
186   str << "<" << toLower (resolvable->kind().asString()) << ">" << endl;
187   str << TAB << xml_tag_enclose (resolvable->name(), "name", true) << endl;
188   str << TAB << xml_tag_enclose (item->vendor(), "vendor", true) << endl;
189   str << TAB << xml_tag_enclose (item->buildtime().asSeconds(), "buildtime", true) << endl;
190   if ( isKind<Package>(resolvable) ) {
191       str << TAB << "<history>" << endl << TAB << "<update>" << endl;
192       str << TAB2 << helixXML (resolvable->arch()) << endl;
193       str << TAB2 << helixXML (resolvable->edition()) << endl;
194       str << TAB << "</update>" << endl << TAB << "</history>" << endl;
195   } else {
196       str << TAB << helixXML (resolvable->arch()) << endl;
197       str << TAB << helixXML (resolvable->edition()) << endl;
198   }
199   str << helixXML( resolvable, Dep::PROVIDES);
200   str << helixXML( resolvable, Dep::PREREQUIRES);
201   str << helixXML( resolvable, Dep::CONFLICTS);
202   str << helixXML( resolvable, Dep::OBSOLETES);
203   str << helixXML( resolvable, Dep::REQUIRES);
204   str << helixXML( resolvable, Dep::RECOMMENDS);
205   str << helixXML( resolvable, Dep::ENHANCES);
206   str << helixXML( resolvable, Dep::SUPPLEMENTS);
207   str << helixXML( resolvable, Dep::SUGGESTS);
208
209   str << "</" << toLower (resolvable->kind().asString()) << ">" << endl;
210   return str.str();
211 }
212
213 //---------------------------------------------------------------------------
214
215 Testcase::Testcase()
216     :dumpPath("/var/log/YaST2/solverTestcase")
217 {
218 }
219
220 Testcase::Testcase(const std::string & path)
221     :dumpPath(path)
222 {
223 }
224
225
226 Testcase::~Testcase()
227 {
228 }
229
230
231 bool Testcase::createTestcasePool(const ResPool &pool)
232 {
233     PathInfo path (dumpPath);
234
235     if ( !path.isExist() ) {
236         if (zypp::filesystem::mkdir (dumpPath)!=0) {
237             ERR << "Cannot create directory " << dumpPath << endl;
238             return false;
239         }
240     } else {
241         if (!path.isDir()) {
242             ERR << dumpPath << " is not a directory." << endl;
243             return false;
244         }
245         // remove old stuff
246         zypp::filesystem::clean_dir (dumpPath);
247     }
248
249     RepositoryTable             repoTable;
250     HelixResolvable     system (dumpPath + "/solver-system.xml.gz");
251
252     for ( ResPool::const_iterator it = pool.begin(); it != pool.end(); ++it )
253     {
254         if ( it->status().isInstalled() ) {
255             // system channel
256             system.addResolvable (*it);
257         } else {
258             // repo channels
259             Repository repo  = it->resolvable()->satSolvable().repository();
260             if (repoTable.find (repo) == repoTable.end()) {
261                 repoTable[repo] = new HelixResolvable(dumpPath + "/"
262                                                       + str::numstring((long)repo.id())
263                                                       + "-package.xml.gz");
264             }
265             repoTable[repo]->addResolvable (*it);
266         }
267     }
268     return true;
269 }
270
271 bool Testcase::createTestcase(Resolver & resolver, bool dumpPool, bool runSolver)
272 {
273     PathInfo path (dumpPath);
274
275     if ( !path.isExist() ) {
276         if (zypp::filesystem::mkdir (dumpPath)!=0) {
277             ERR << "Cannot create directory " << dumpPath << endl;
278             return false;
279         }
280     } else {
281         if (!path.isDir()) {
282             ERR << dumpPath << " is not a directory." << endl;
283             return false;
284         }
285         // remove old stuff if pool will be dump
286         if (dumpPool)
287             zypp::filesystem::clean_dir (dumpPath);
288     }
289
290     if (runSolver) {
291         zypp::base::LogControl::TmpLineWriter tempRedirect;
292         zypp::base::LogControl::instance().logfile( dumpPath +"/y2log" );
293         zypp::base::LogControl::TmpExcessive excessive;
294
295         resolver.resolvePool();
296     }
297
298     ResPool pool        = resolver.pool();
299     RepositoryTable     repoTable;
300     PoolItemList        items_to_install;
301     PoolItemList        items_to_remove;
302     PoolItemList        items_locked;
303     PoolItemList        items_keep;
304     HelixResolvable_Ptr system = NULL;
305
306     if (dumpPool)
307         system = new HelixResolvable(dumpPath + "/solver-system.xml.gz");
308
309     for ( ResPool::const_iterator it = pool.begin(); it != pool.end(); ++it )
310     {
311         Resolvable::constPtr res = it->resolvable();
312
313         if ( system && it->status().isInstalled() ) {
314             // system channel
315             system->addResolvable (*it);
316         } else {
317             // repo channels
318             Repository repo  = it->resolvable()->satSolvable().repository();
319             if (dumpPool) {
320                 if (repoTable.find (repo) == repoTable.end()) {
321                     repoTable[repo] = new HelixResolvable(dumpPath + "/"
322                                                           + str::numstring((long)repo.id())
323                                                           + "-package.xml.gz");
324                 }
325                 repoTable[repo]->addResolvable (*it);
326             }
327         }
328
329         if ( it->status().isToBeInstalled()
330              && !(it->status().isBySolver())) {
331             items_to_install.push_back (*it);
332         }
333         if ( it->status().isKept()
334              && !(it->status().isBySolver())) {
335             items_keep.push_back (*it);
336         }
337         if ( it->status().isToBeUninstalled()
338              && !(it->status().isBySolver())) {
339             items_to_remove.push_back (*it);
340         }
341         if ( it->status().isLocked()
342              && !(it->status().isBySolver())) {
343             items_locked.push_back (*it);
344         }
345     }
346
347     // writing control file "*-test.xml"
348     HelixControl control (dumpPath + "/solver-test.xml",
349                           repoTable,
350                           ZConfig::instance().systemArchitecture(),
351                           pool.getRequestedLocales(),
352                           "solver-system.xml.gz",
353                           resolver.forceResolve(),
354                           resolver.onlyRequires(),
355                           resolver.ignorealreadyrecommended() );
356
357     for (PoolItemList::const_iterator iter = items_to_install.begin(); iter != items_to_install.end(); iter++) {
358         control.installResolvable (iter->resolvable(), iter->status());
359     }
360
361     for (PoolItemList::const_iterator iter = items_locked.begin(); iter != items_locked.end(); iter++) {
362         control.lockResolvable (iter->resolvable(), iter->status());
363     }
364
365     for (PoolItemList::const_iterator iter = items_keep.begin(); iter != items_keep.end(); iter++) {
366         control.keepResolvable (iter->resolvable(), iter->status());
367     }
368
369     for (PoolItemList::const_iterator iter = items_to_remove.begin(); iter != items_to_remove.end(); iter++) {
370         control.deleteResolvable (iter->resolvable(), iter->status());
371     }
372
373     control.addDependencies (resolver.extraRequires(), resolver.extraConflicts());
374     control.addDependencies (SystemCheck::instance().requiredSystemCap(),
375                              SystemCheck::instance().conflictSystemCap());
376
377     if (resolver.isUpgradeMode())
378         control.distupgrade ();
379     if (resolver.isUpdateMode())
380         control.update();
381     if (resolver.isVerifyingMode())
382         control.verifySystem();
383
384     return true;
385 }
386
387 //---------------------------------------------------------------------------
388
389 HelixResolvable::HelixResolvable(const std::string & path)
390     :dumpFile (path)
391 {
392     file = new ofgzstream(path.c_str());
393     if (!file) {
394         ZYPP_THROW (Exception( "Can't open " + path ) );
395     }
396
397     *file << "<channel><subchannel>" << endl;
398 }
399
400 HelixResolvable::~HelixResolvable()
401 {
402     *file << "</subchannel></channel>" << endl;
403     delete(file);
404 }
405
406
407 void HelixResolvable::addResolvable(const PoolItem item)
408 {
409     *file << helixXML (item);
410 }
411
412 //---------------------------------------------------------------------------
413
414 HelixControl::HelixControl(const std::string & controlPath,
415                            const RepositoryTable & repoTable,
416                            const Arch & systemArchitecture,
417                            const LocaleSet &languages,
418                            const std::string & systemPath,
419                            const bool forceResolve,
420                            const bool onlyRequires,
421                            const bool ignorealreadyrecommended)
422     :dumpFile (controlPath)
423 {
424     file = new ofstream(controlPath.c_str());
425     if (!file) {
426         ZYPP_THROW (Exception( "Can't open " + controlPath ) );
427     }
428
429     *file << "<?xml version=\"1.0\"?>" << endl
430           << "<!-- testcase generated by YaST -->" << endl
431           << "<test>" << endl
432           << "<setup arch=\"" << systemArchitecture << "\">" << endl
433           << TAB << "<system file=\"" << systemPath << "\"/>" << endl << endl;
434     for ( RepositoryTable::const_iterator it = repoTable.begin();
435           it != repoTable.end(); ++it ) {
436         RepoInfo repo = it->first.info();
437         *file << TAB << "<!-- " << endl
438               << TAB << "- alias       : " << repo.alias() << endl;
439         for ( RepoInfo::urls_const_iterator itUrl = repo.baseUrlsBegin();
440               itUrl != repo.baseUrlsEnd();
441               ++itUrl )
442         {
443             *file << TAB << "- url         : " << *itUrl << endl;
444         }
445         *file << TAB << "- path        : " << repo.path() << endl;
446         *file << TAB << "- type        : " << repo.type() << endl;
447         *file << TAB << "- generated   : " << (it->first.generatedTimestamp()).form( "%Y-%m-%d %H:%M:%S" ) << endl;
448         *file << TAB << "- outdated    : " << (it->first.suggestedExpirationTimestamp()).form( "%Y-%m-%d %H:%M:%S" ) << endl;
449         *file << TAB << " -->" << endl;
450
451         *file << TAB << "<channel file=\"" << str::numstring((long)it->first.id())
452               << "-package.xml.gz\" name=\"" << repo.alias() << "\""
453               << " priority=\"" << repo.priority() 
454               << "\" />" << endl << endl;
455     }
456     for (LocaleSet::const_iterator iter = languages.begin(); iter != languages.end(); iter++) {
457         *file << TAB << "<locale name=\"" <<  iter->code()
458               << "\" />" << endl;
459     }
460
461     if (forceResolve)
462         *file << TAB << "<forceResolve/>" << endl;
463     if (onlyRequires)
464         *file << TAB << "<onlyRequires/>" << endl;
465
466     if (ignorealreadyrecommended)
467         *file << TAB << "<ignorealreadyrecommended/>" << endl;
468     
469     *file << "</setup>" << endl
470           << "<trial>" << endl;
471 }
472
473 HelixControl::HelixControl()
474     :dumpFile ("/var/log/YaST2/solverTestcase/solver-test.xml")
475 {
476     HelixControl (dumpFile);
477 }
478
479 HelixControl::~HelixControl()
480 {
481     *file << "</trial>" << endl
482           << "</test>" << endl;
483     delete(file);
484 }
485
486 void HelixControl::installResolvable(const ResObject::constPtr &resObject,
487                                      const ResStatus &status)
488 {
489     *file << "<install channel=\"" << resObject->repoInfo().alias() << "\" kind=\"" << toLower (resObject->kind().asString()) << "\""
490           << " name=\"" << resObject->name() << "\"" << " arch=\"" << resObject->arch().asString() << "\""
491           << " version=\"" << resObject->edition().version() << "\"" << " release=\"" << resObject->edition().release() << "\""
492           << " status=\"" << status << "\""
493           << "/>" << endl;
494 }
495
496 void HelixControl::lockResolvable(const ResObject::constPtr &resObject,
497                                   const ResStatus &status)
498 {
499     *file << "<lock channel=\"" << resObject->repoInfo().alias() << "\" kind=\"" << toLower (resObject->kind().asString()) << "\""
500           << " name=\"" << resObject->name() << "\"" << " arch=\"" << resObject->arch().asString() << "\""
501           << " version=\"" << resObject->edition().version() << "\"" << " release=\"" << resObject->edition().release() << "\""
502           << " status=\"" << status << "\""
503           << "/>" << endl;
504 }
505
506 void HelixControl::keepResolvable(const ResObject::constPtr &resObject,
507                                   const ResStatus &status)
508 {
509     *file << "<keep channel=\"" << resObject->repoInfo().alias() << "\" kind=\"" << toLower (resObject->kind().asString()) << "\""
510           << " name=\"" << resObject->name() << "\"" << " arch=\"" << resObject->arch().asString() << "\""
511           << " version=\"" << resObject->edition().version() << "\"" << " release=\"" << resObject->edition().release() << "\""
512           << " status=\"" << status << "\""
513           << "/>" << endl;
514 }
515
516 void HelixControl::deleteResolvable(const ResObject::constPtr &resObject,
517                                     const ResStatus &status)
518 {
519     *file << "<uninstall " << " kind=\"" << toLower (resObject->kind().asString()) << "\""
520           << " name=\"" << resObject->name() << "\""
521           << " status=\"" << status << "\""
522           << "/>" << endl;
523 }
524
525 void HelixControl::addDependencies (const CapabilitySet & capRequire, const CapabilitySet & capConflict)
526 {
527     for (CapabilitySet::const_iterator iter = capRequire.begin(); iter != capRequire.end(); iter++) {
528         *file << "<addRequire " <<  " name=\"" << iter->asString() << "\"" << "/>" << endl;
529     }
530     for (CapabilitySet::const_iterator iter = capConflict.begin(); iter != capConflict.end(); iter++) {
531         *file << "<addConflict " << " name=\"" << iter->asString() << "\"" << "/>" << endl;
532     }
533 }
534
535 void HelixControl::distupgrade()
536 {
537     *file << "<distupgrade/>" << endl;
538 }
539
540 void HelixControl::verifySystem()
541 {
542     *file << "<verify/>" << endl;
543 }
544
545 void HelixControl::update()
546 {
547     *file << "<update/>" << endl;
548 }
549
550
551       ///////////////////////////////////////////////////////////////////
552     };// namespace detail
553     /////////////////////////////////////////////////////////////////////
554     /////////////////////////////////////////////////////////////////////
555   };// namespace solver
556   ///////////////////////////////////////////////////////////////////////
557   ///////////////////////////////////////////////////////////////////////
558 };// namespace zypp
559 /////////////////////////////////////////////////////////////////////////