interface for generating testcases
[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/capability/VersionedCap.h"
26 #include "zypp/base/String.h"
27 #include "zypp/base/PtrTypes.h"
28
29
30 /////////////////////////////////////////////////////////////////////////
31 namespace zypp
32 { ///////////////////////////////////////////////////////////////////////
33   ///////////////////////////////////////////////////////////////////////
34   namespace solver
35   { /////////////////////////////////////////////////////////////////////
36     /////////////////////////////////////////////////////////////////////
37     namespace detail
38     { ///////////////////////////////////////////////////////////////////
39         
40 #define TAB "\t"
41 #define TAB2 "\t\t"
42         
43 using namespace std;
44 using namespace zypp::capability;
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         << xml_tag_enclose(edition.release(), "release")
79         << xml_tag_enclose(numstring(edition.epoch()), "epoch");    
80     return str.str();
81 }
82
83 template<> 
84 std::string helixXML( const Arch &arch )
85 {
86     stringstream str;
87     str << xml_tag_enclose(arch.asString(), "arch");        
88     return str.str();    
89 }
90
91 template<> 
92 std::string helixXML( const Capability &cap )
93 {
94     stringstream str;
95     if (isKind<VersionedCap>(cap)
96         && cap.op() != Rel::NONE
97         && cap.op() != Rel::ANY
98         && !cap.edition().version().empty()) {
99         // version capability
100         str << "<dep name='" << cap.index() << "' op='" << xml_escape(cap.op().asString()) <<
101             "' version='" << cap.edition().version() << "' release='" << cap.edition().release() << "' />" << endl;
102     } else {
103         // anything else
104         str << "<dep name='" << cap.asString() << "' />" << endl;       
105     }
106     return str.str();    
107 }
108
109 template<> 
110 std::string helixXML( const CapSet &caps )
111 {
112     stringstream str;
113     CapSet::iterator it = caps.begin();
114     str << endl;
115     for ( ; it != caps.end(); ++it)
116     {
117         str << TAB2 << helixXML((*it));
118     }
119     str << TAB;
120     return str.str();
121 }
122
123 template<> 
124 std::string helixXML( const Dependencies &dep )
125 {
126     stringstream str;
127     if ( dep[Dep::PROVIDES].size() > 0 )
128         str << TAB << xml_tag_enclose(helixXML(dep[Dep::PROVIDES]), "provides") << endl;
129     if ( dep[Dep::CONFLICTS].size() > 0 )
130         str << TAB << xml_tag_enclose(helixXML(dep[Dep::CONFLICTS]), "conflicts") << endl;
131     if ( dep[Dep::OBSOLETES].size() > 0 )
132         str << TAB << xml_tag_enclose(helixXML(dep[Dep::OBSOLETES]), "obsoletes") << endl;
133     if ( dep[Dep::FRESHENS].size() > 0 )
134         str << TAB << xml_tag_enclose(helixXML(dep[Dep::FRESHENS]), "freshens") << endl;
135     if ( dep[Dep::REQUIRES].size() > 0 )
136         str << TAB << xml_tag_enclose(helixXML(dep[Dep::REQUIRES]), "requires") << endl;  
137     if ( dep[Dep::RECOMMENDS].size() > 0 )
138         str << TAB << xml_tag_enclose(helixXML(dep[Dep::RECOMMENDS]), "recommends") << endl;
139     if ( dep[Dep::ENHANCES].size() > 0 )
140         str << TAB << xml_tag_enclose(helixXML(dep[Dep::ENHANCES]), "enhances") << endl;
141     if ( dep[Dep::SUPPLEMENTS].size() > 0 )
142         str << TAB << xml_tag_enclose(helixXML(dep[Dep::SUPPLEMENTS]), "supplements") << endl;
143     if ( dep[Dep::SUGGESTS].size() > 0 )
144         str << TAB << xml_tag_enclose(helixXML(dep[Dep::SUGGESTS]), "suggests") << endl;
145     return str.str();    
146 }
147
148 std::string helixXML( const Resolvable::constPtr &resolvable )
149 {
150   stringstream str;
151   str << "<" << toLower (resolvable->kind().asString()) << ">" << endl;
152   str << TAB << xml_tag_enclose (resolvable->name(), "name") << endl;  
153   if ( isKind<Package>(resolvable) ) {
154       str << TAB << "<history>" << endl << TAB << "<update>" << endl;
155       str << TAB2 << helixXML (resolvable->arch()) << endl;
156       str << TAB2 << helixXML (resolvable->edition()) << endl;      
157       str << TAB << "</update>" << endl << TAB << "</history>" << endl;
158   } else {
159       str << TAB << helixXML (resolvable->arch()) << endl;      
160       str << TAB << helixXML (resolvable->edition()) << endl;            
161   }
162   str << helixXML (resolvable->deps());              
163
164   str << "</" << toLower (resolvable->kind().asString()) << ">" << endl;  
165   return str.str();
166 }
167
168 //---------------------------------------------------------------------------
169
170 Testcase::Testcase()
171     :dumpPath("/var/log/YaST2/solverTestcase")    
172 {
173 }
174
175 Testcase::Testcase(const std::string & path)
176     :dumpPath(path)
177 {
178 }
179         
180
181 Testcase::~Testcase()
182 {
183 }
184
185 bool Testcase::createTestcase(Resolver & resolver)
186 {
187     PathInfo path (dumpPath);
188
189     if ( !path.isExist() ) {
190         if (zypp::filesystem::mkdir (dumpPath)!=0) {
191             ERR << "Cannot create directory " << dumpPath << endl;
192             return false;
193         }
194     } else {
195         if (!path.isDir()) {
196             ERR << dumpPath << " is not a directory." << endl;
197             return false;
198         }
199     }
200     zypp::base::LogControl::instance().logfile( dumpPath +"/y2log" );
201     zypp::base::LogControl::TmpExcessive excessive; // ZYPP_FULLLOG=1
202     
203     resolver.resolveDependencies();
204
205     ResPool pool        = resolver.pool();
206     SourceTable         sourceTable;
207     PoolItemList        items_to_install;
208     PoolItemList        items_to_remove;    
209
210     HelixResolvable     system (dumpPath + "/solver-system.xml");    
211
212     for ( ResPool::const_iterator it = pool.begin(); it != pool.end(); ++it )
213     {
214         Resolvable::constPtr res = it->resolvable();
215         
216         if ( it->status().isInstalled() ) {
217             // system channel
218             system.addResolvable (res);
219         } else {
220             // source channels
221             ResObject::constPtr sourceItem = it->resolvable();
222             Source_Ref source  = sourceItem->source();
223             if (sourceTable.find (source) == sourceTable.end()) {
224                 sourceTable[source] = new HelixResolvable(dumpPath + "/"
225                                                           + numstring(source.numericId())
226                                                           + "-package.xml");
227             }
228             sourceTable[source]->addResolvable (res);
229         }
230         
231         if ( it->status().isToBeInstalled()
232              && !(it->status().isBySolver())) {
233             items_to_install.push_back (*it);
234         }
235         if ( it->status().isToBeUninstalled()
236              && !(it->status().isBySolver())) {
237             items_to_remove.push_back (*it);
238         }
239     }
240
241     // writing control file "*-test.xml"
242
243     HelixControl control (dumpPath + "/solver-test.xml",
244                           sourceTable);
245
246     for (PoolItemList::const_iterator iter = items_to_install.begin(); iter != items_to_install.end(); iter++) {
247         control.installResolvable (iter->resolvable()); 
248     }
249
250     for (PoolItemList::const_iterator iter = items_to_remove.begin(); iter != items_to_remove.end(); iter++) {
251         control.deleteResolvable (iter->resolvable());  
252     }
253
254     return true;
255 }
256
257 //---------------------------------------------------------------------------
258
259 HelixResolvable::HelixResolvable(const std::string & path)
260     :dumpFile (path)    
261 {
262     file = new ofstream(path.c_str());
263     if (!file) {
264         ZYPP_THROW (Exception( "Can't open " + path ) );
265     }
266
267     *file << "<channel><subchannel>" << endl;
268 }
269
270 HelixResolvable::~HelixResolvable()
271 {
272     *file << "</subchannel></channel>" << endl;
273 }
274     
275
276 void HelixResolvable::addResolvable(const Resolvable::constPtr &resolvable)
277 {
278     *file << helixXML (resolvable);
279 }
280
281 //---------------------------------------------------------------------------
282
283 HelixControl::HelixControl(const std::string & controlPath,
284                            const SourceTable & sourceTable,
285                            const std::string & systemPath)
286     :dumpFile (controlPath) 
287 {
288     file = new ofstream(controlPath.c_str());
289     if (!file) {
290         ZYPP_THROW (Exception( "Can't open " + controlPath ) );
291     }
292
293     *file << "<?xml version=\"1.0\"?>" << endl
294           << "<!-- testcase generated by YaST -->" << endl
295           << "<test>" << endl
296           << "<setup>" << endl
297           << TAB << "<system file=\"" << systemPath << "\"/>" << endl;
298     for ( SourceTable::const_iterator it = sourceTable.begin();
299           it != sourceTable.end(); ++it ) {
300         Source_Ref source = it->first;
301         *file << TAB << "<channel file=\"" << numstring(source.numericId())
302               << "-package.xml\" name=\"" << numstring(source.numericId())
303               << "\" />" << endl;
304     }
305     *file << "</setup>" << endl
306           << "<trial>" << endl
307           << "<showpool all=\"yes\"/>" << endl
308           << "<establish/>" << endl
309           << "<showpool all=\"true\" prefix=\">!> ESTABLISHED:\"/>" << endl;
310 }
311
312 HelixControl::HelixControl()
313     :dumpFile ("/var/log/YaST2/solverTestcase/solver-test.xml")
314 {
315     HelixControl (dumpFile);
316 }
317
318 HelixControl::~HelixControl()
319 {
320     *file << "</trial>" << endl
321           << "</test>" << endl;
322 }
323
324 void HelixControl::installResolvable(const ResObject::constPtr &resObject)
325 {
326     Source_Ref source  = resObject->source();
327     *file << "<install channel=\"" << numstring(source.numericId()) << "\" kind=\"" << toLower (resObject->kind().asString()) << "\""
328           << " name=\"" << resObject->name() << "\"" << "/>" << endl;
329 }
330     
331 void HelixControl::deleteResolvable(const ResObject::constPtr &resObject)
332 {
333     Source_Ref source  = resObject->source();    
334     *file << "<uninstall channel=\"" << numstring(source.numericId()) << "\" kind=\"" << toLower (resObject->kind().asString()) << "\""
335           << " name=\"" << resObject->name() << "\"" << "/>" << endl;    
336 }
337
338
339       ///////////////////////////////////////////////////////////////////
340     };// namespace detail
341     /////////////////////////////////////////////////////////////////////
342     /////////////////////////////////////////////////////////////////////
343   };// namespace solver
344   ///////////////////////////////////////////////////////////////////////
345   ///////////////////////////////////////////////////////////////////////
346 };// namespace zypp
347 /////////////////////////////////////////////////////////////////////////