libzypp  17.14.0
Testcase.cc
Go to the documentation of this file.
1 /*---------------------------------------------------------------------\
2 | ____ _ __ __ ___ |
3 | |__ / \ / / . \ . \ |
4 | / / \ V /| _/ _/ |
5 | / /__ | | | | | | |
6 | /_____||_| |_| |_| |
7 | |
8 \---------------------------------------------------------------------*/
12 #include <iostream>
13 #include <fstream>
14 #include <sstream>
15 #include <streambuf>
16 
17 #define ZYPP_USE_RESOLVER_INTERNALS
18 
20 #include "zypp/base/Logger.h"
21 #include "zypp/base/LogControl.h"
22 #include "zypp/base/GzStream.h"
23 #include "zypp/base/String.h"
24 #include "zypp/base/PtrTypes.h"
25 #include "zypp/base/NonCopyable.h"
27 
29 
30 #include "zypp/ZConfig.h"
31 #include "zypp/PathInfo.h"
32 #include "zypp/ResPool.h"
33 #include "zypp/Repository.h"
35 
39 
41 namespace zypp
42 {
43 
44  namespace solver
45  {
46 
47  namespace detail
48  {
49 
50 #define TAB "\t"
51 #define TAB2 "\t\t"
52 
53 using namespace std;
54 using namespace zypp::str;
55 
56 //---------------------------------------------------------------------------
57 
58 inline std::string xml_escape( const std::string &text )
59 {
60  return zypp::xml::escape(text);
61 }
62 
63 inline std::string xml_tag_enclose( const std::string &text, const std::string &tag, bool escape = false )
64 {
65  string result;
66  result += "<" + tag + ">";
67 
68  if ( escape)
69  result += xml_escape(text);
70  else
71  result += text;
72 
73  result += "</" + tag + ">";
74  return result;
75 }
76 
77 template<class T>
78 std::string helixXML( const T &obj ); //undefined
79 
80 template<>
81 std::string helixXML( const Edition &edition )
82 {
83  stringstream str;
84  str << xml_tag_enclose(edition.version(), "version");
85  if (!edition.release().empty())
86  str << xml_tag_enclose(edition.release(), "release");
87  if (edition.epoch() != Edition::noepoch)
88  str << xml_tag_enclose(numstring(edition.epoch()), "epoch");
89  return str.str();
90 }
91 
92 template<>
93 std::string helixXML( const Arch &arch )
94 {
95  stringstream str;
96  str << xml_tag_enclose(arch.asString(), "arch");
97  return str.str();
98 }
99 
100 template<>
101 std::string helixXML( const Capability &cap )
102 {
103  stringstream str;
104  CapDetail detail = cap.detail();
105  if (detail.isSimple()) {
106  if (detail.isVersioned()) {
107  str << "<dep name='" << xml_escape(detail.name().asString()) << "'"
108  << " op='" << xml_escape(detail.op().asString()) << "'"
109  << " version='" << xml_escape(detail.ed().version()) << "'";
110  if (!detail.ed().release().empty())
111  str << " release='" << xml_escape(detail.ed().release()) << "'";
112  if (detail.ed().epoch() != Edition::noepoch)
113  str << " epoch='" << xml_escape(numstring(detail.ed().epoch())) << "'";
114  str << " />" << endl;
115  } else {
116  str << "<dep name='" << xml_escape(cap.asString()) << "' />" << endl;
117  }
118  } else if (detail.isExpression()) {
119  if (detail.capRel() == CapDetail::CAP_AND
120  && detail.lhs().detail().isNamed()
121  && detail.rhs().detail().isNamed()) {
122  // packageand dependency
123  str << "<dep name='packageand("
124  << IdString(detail.lhs().id()) << ":"
125  << IdString(detail.rhs().id()) << ")' />" << endl;
126  } else if (detail.capRel() == CapDetail::CAP_NAMESPACE
127  && detail.lhs().id() == NAMESPACE_OTHERPROVIDERS) {
128  str << "<dep name='otherproviders("
129  << IdString(detail.rhs().id()) << ")' />" << endl;
130  } else {
131  // modalias ?
132  IdString packageName;
133  if (detail.capRel() == CapDetail::CAP_AND) {
134  packageName = IdString(detail.lhs().id());
135  detail = detail.rhs().detail();
136  }
137  if (detail.capRel() == CapDetail::CAP_NAMESPACE
138  && detail.lhs().id() == NAMESPACE_MODALIAS) {
139  str << "<dep name='modalias(";
140  if (!packageName.empty())
141  str << packageName << ":";
142  str << IdString(detail.rhs().id()) << ")' />" << endl;
143  } else {
144  str << "<!--- ignoring '" << xml_escape(cap.asString()) << "' -->" << endl;
145  }
146  }
147  } else {
148  str << "<!--- ignoring '" << xml_escape(cap.asString()) << "' -->" << endl;
149  }
150 
151  return str.str();
152 }
153 
154 template<>
155 std::string helixXML( const Capabilities &caps )
156 {
157  stringstream str;
159  str << endl;
160  for ( ; it != caps.end(); ++it)
161  {
162  str << TAB2 << helixXML((*it));
163  }
164  str << TAB;
165  return str.str();
166 }
167 
168 template<>
169 std::string helixXML( const CapabilitySet &caps )
170 {
171  stringstream str;
172  CapabilitySet::const_iterator it = caps.begin();
173  str << endl;
174  for ( ; it != caps.end(); ++it)
175  {
176  str << TAB2 << helixXML((*it));
177  }
178  str << TAB;
179  return str.str();
180 }
181 
182 inline string helixXML( const PoolItem & obj, Dep deptag_r )
183 {
184  stringstream out;
185  Capabilities caps( obj[deptag_r] );
186  if ( ! caps.empty() )
187  out << TAB << xml_tag_enclose(helixXML(caps), deptag_r.asString()) << endl;
188  return out.str();
189 }
190 
191 std::string helixXML( const PoolItem & item )
192 {
193  stringstream str;
194  str << "<" << item.kind() << ">" << endl;
195  str << TAB << xml_tag_enclose( item.name(), "name", true ) << endl;
196  str << TAB << xml_tag_enclose( item.vendor().asString(), "vendor", true ) << endl;
197  str << TAB << xml_tag_enclose( item.buildtime().asSeconds(), "buildtime", true ) << endl;
198  if ( isKind<Package>( item ) ) {
199  str << TAB << "<history>" << endl << TAB << "<update>" << endl;
200  str << TAB2 << helixXML( item.arch() ) << endl;
201  str << TAB2 << helixXML( item.edition() ) << endl;
202  str << TAB << "</update>" << endl << TAB << "</history>" << endl;
203  } else {
204  str << TAB << helixXML( item.arch() ) << endl;
205  str << TAB << helixXML( item.edition() ) << endl;
206  }
207  str << helixXML( item, Dep::PROVIDES );
208  str << helixXML( item, Dep::PREREQUIRES );
209  str << helixXML( item, Dep::CONFLICTS );
210  str << helixXML( item, Dep::OBSOLETES );
211  str << helixXML( item, Dep::REQUIRES );
212  str << helixXML( item, Dep::RECOMMENDS );
213  str << helixXML( item, Dep::ENHANCES );
214  str << helixXML( item, Dep::SUPPLEMENTS );
215  str << helixXML( item, Dep::SUGGESTS );
216 
217  str << "</" << item.kind() << ">" << endl;
218  return str.str();
219 }
220 
222 //
223 // CLASS NAME : HelixResolvable
229 
230  private:
231  std::string dumpFile; // Path of the generated testcase
233 
234  public:
235  HelixResolvable (const std::string & path);
236  ~HelixResolvable ();
237 
238  void addResolvable (const PoolItem item)
239  { *file << helixXML (item); }
240 
241  std::string filename ()
242  { return dumpFile; }
243 };
244 
245 DEFINE_PTR_TYPE(HelixResolvable);
246 IMPL_PTR_TYPE(HelixResolvable);
247 
248 typedef std::map<Repository, HelixResolvable_Ptr> RepositoryTable;
249 
250 HelixResolvable::HelixResolvable(const std::string & path)
251  :dumpFile (path)
252 {
253  file = new ofgzstream(path.c_str());
254  if (!file) {
255  ZYPP_THROW (Exception( "Can't open " + path ) );
256  }
257 
258  *file << "<channel><subchannel>" << endl;
259 }
260 
262 {
263  *file << "</subchannel></channel>" << endl;
264  delete(file);
265 }
266 
268 //
269 // CLASS NAME : HelixControl
275 
276  private:
277  std::string dumpFile; // Path of the generated testcase
278  std::ofstream *file;
279  bool _inSetup;
280 
281  public:
282  HelixControl (const std::string & controlPath,
283  const RepositoryTable & sourceTable,
284  const Arch & systemArchitecture,
285  const target::Modalias::ModaliasList & modaliasList,
286  const std::set<std::string> & multiversionSpec,
287  const std::string & systemPath);
288  ~HelixControl ();
289 
290  void closeSetup()
291  {
292  if ( _inSetup )
293  {
294  *file << "</setup>" << endl << "<trial>" << endl;
295  _inSetup = false;
296  }
297  }
298 
299  void addTagIf( const std::string & tag_r, bool yesno_r = true )
300  {
301  if ( yesno_r )
302  *file << (_inSetup ? TAB : "") << "<" << tag_r << "/>" << endl;
303  }
304 
305  void installResolvable( const PoolItem & pi_r );
306  void lockResolvable( const PoolItem & pi_r );
307  void keepResolvable( const PoolItem & pi_r );
308  void deleteResolvable( const PoolItem & pi_r );
309  void addDependencies (const CapabilitySet &capRequire, const CapabilitySet &capConflict);
310  void addUpgradeRepos( const std::set<Repository> & upgradeRepos_r );
311 
312  std::string filename () { return dumpFile; }
313 };
314 
315 HelixControl::HelixControl(const std::string & controlPath,
316  const RepositoryTable & repoTable,
317  const Arch & systemArchitecture,
318  const target::Modalias::ModaliasList & modaliasList,
319  const std::set<std::string> & multiversionSpec,
320  const std::string & systemPath)
321  :dumpFile (controlPath)
322  ,_inSetup( true )
323 {
324  file = new ofstream(controlPath.c_str());
325  if (!file) {
326  ZYPP_THROW (Exception( "Can't open " + controlPath ) );
327  }
328 
329  *file << "<?xml version=\"1.0\"?>" << endl
330  << "<!-- testcase generated by YaST -->" << endl
331  << "<test>" << endl
332  << "<setup arch=\"" << systemArchitecture << "\">" << endl
333  << TAB << "<system file=\"" << systemPath << "\"/>" << endl << endl;
334  for ( RepositoryTable::const_iterator it = repoTable.begin();
335  it != repoTable.end(); ++it ) {
336  RepoInfo repo = it->first.info();
337  *file << TAB << "<!-- " << endl
338  << TAB << "- alias : " << repo.alias() << endl;
339  for ( RepoInfo::urls_const_iterator itUrl = repo.baseUrlsBegin();
340  itUrl != repo.baseUrlsEnd();
341  ++itUrl )
342  {
343  *file << TAB << "- url : " << *itUrl << endl;
344  }
345  *file << TAB << "- path : " << repo.path() << endl;
346  *file << TAB << "- type : " << repo.type() << endl;
347  *file << TAB << "- generated : " << (it->first.generatedTimestamp()).form( "%Y-%m-%d %H:%M:%S" ) << endl;
348  *file << TAB << "- outdated : " << (it->first.suggestedExpirationTimestamp()).form( "%Y-%m-%d %H:%M:%S" ) << endl;
349  *file << TAB << " -->" << endl;
350 
351  *file << TAB << "<channel file=\"" << str::numstring((long)it->first.id())
352  << "-package.xml.gz\" name=\"" << repo.alias() << "\""
353  << " priority=\"" << repo.priority()
354  << "\" />" << endl << endl;
355  }
356 
357  // HACK: directly access sat::pool
358  const sat::Pool & satpool( sat::Pool::instance() );
359 
360  // RequestedLocales
361  const LocaleSet & addedLocales( satpool.getAddedRequestedLocales() );
362  const LocaleSet & removedLocales( satpool.getRemovedRequestedLocales() );
363  const LocaleSet & requestedLocales( satpool.getRequestedLocales() );
364 
365  for ( Locale l : requestedLocales )
366  {
367  const char * fate = ( addedLocales.count(l) ? "\" fate=\"added" : "" );
368  *file << TAB << "<locale name=\"" << l << fate << "\" />" << endl;
369  }
370  for ( Locale l : removedLocales )
371  {
372  *file << TAB << "<locale name=\"" << l << "\" fate=\"removed\" />" << endl;
373  }
374 
375  // AutoInstalled
376  for ( IdString::IdType n : satpool.autoInstalled() )
377  {
378  *file << TAB << "<autoinst name=\"" << IdString(n) << "\" />" << endl;
379  }
380 
381 
382 
383  for_( it, modaliasList.begin(), modaliasList.end() ) {
384  *file << TAB << "<modalias name=\"" << xml_escape(*it)
385  << "\" />" << endl;
386  }
387 
388  for_( it, multiversionSpec.begin(), multiversionSpec.end() ) {
389  *file << TAB << "<multiversion name=\"" << *it
390  << "\" />" << endl;
391  }
392 
393  // setup continued outside....
394 }
395 
397 {
398  closeSetup(); // in case it is still open
399  *file << "</trial>" << endl
400  << "</test>" << endl;
401  delete(file);
402 }
403 
405 {
406  *file << "<install channel=\"" << pi_r.repoInfo().alias() << "\""
407  << " kind=\"" << pi_r.kind() << "\""
408  << " name=\"" << pi_r.name() << "\""
409  << " arch=\"" << pi_r.arch() << "\""
410  << " version=\"" << pi_r.edition().version() << "\""
411  << " release=\"" << pi_r.edition().release() << "\""
412  << " status=\"" << pi_r.status() << "\""
413  << "/>" << endl;
414 }
415 
417 {
418  *file << "<lock channel=\"" << pi_r.repoInfo().alias() << "\""
419  << " kind=\"" << pi_r.kind() << "\""
420  << " name=\"" << pi_r.name() << "\""
421  << " arch=\"" << pi_r.arch() << "\""
422  << " version=\"" << pi_r.edition().version() << "\""
423  << " release=\"" << pi_r.edition().release() << "\""
424  << " status=\"" << pi_r.status() << "\""
425  << "/>" << endl;
426 }
427 
429 {
430  *file << "<keep channel=\"" << pi_r.repoInfo().alias() << "\""
431  << " kind=\"" << pi_r.kind() << "\""
432  << " name=\"" << pi_r.name() << "\""
433  << " arch=\"" << pi_r.arch() << "\""
434  << " version=\"" << pi_r.edition().version() << "\""
435  << " release=\"" << pi_r.edition().release() << "\""
436  << " status=\"" << pi_r.status() << "\""
437  << "/>" << endl;
438 }
439 
441 {
442  *file << "<uninstall kind=\"" << pi_r.kind() << "\""
443  << " name=\"" << pi_r.name() << "\""
444  << " status=\"" << pi_r.status() << "\""
445  << "/>" << endl;
446 }
447 
448 void HelixControl::addDependencies (const CapabilitySet & capRequire, const CapabilitySet & capConflict)
449 {
450  for (CapabilitySet::const_iterator iter = capRequire.begin(); iter != capRequire.end(); iter++) {
451  *file << "<addRequire " << " name=\"" << iter->asString() << "\"" << "/>" << endl;
452  }
453  for (CapabilitySet::const_iterator iter = capConflict.begin(); iter != capConflict.end(); iter++) {
454  *file << "<addConflict " << " name=\"" << iter->asString() << "\"" << "/>" << endl;
455  }
456 }
457 
458 void HelixControl::addUpgradeRepos( const std::set<Repository> & upgradeRepos_r )
459 {
460  for_( it, upgradeRepos_r.begin(), upgradeRepos_r.end() )
461  {
462  *file << "<upgradeRepo name=\"" << it->alias() << "\"/>" << endl;
463  }
464 }
465 
466 //---------------------------------------------------------------------------
467 
468 Testcase::Testcase()
469  :dumpPath("/var/log/YaST2/solverTestcase")
470 {}
471 
472 Testcase::Testcase(const std::string & path)
473  :dumpPath(path)
474 {}
475 
476 Testcase::~Testcase()
477 {}
478 
479 bool Testcase::createTestcase(Resolver & resolver, bool dumpPool, bool runSolver)
480 {
481  PathInfo path (dumpPath);
482 
483  if ( !path.isExist() ) {
484  if (zypp::filesystem::assert_dir (dumpPath)!=0) {
485  ERR << "Cannot create directory " << dumpPath << endl;
486  return false;
487  }
488  } else {
489  if (!path.isDir()) {
490  ERR << dumpPath << " is not a directory." << endl;
491  return false;
492  }
493  // remove old stuff if pool will be dump
494  if (dumpPool)
495  zypp::filesystem::clean_dir (dumpPath);
496  }
497 
498  if (runSolver) {
500  zypp::base::LogControl::instance().logfile( dumpPath +"/y2log" );
502 
503  resolver.resolvePool();
504  }
505 
506  ResPool pool = resolver.pool();
507  RepositoryTable repoTable;
508  PoolItemList items_to_install;
509  PoolItemList items_to_remove;
510  PoolItemList items_locked;
511  PoolItemList items_keep;
512  HelixResolvable_Ptr system = NULL;
513 
514  if (dumpPool)
515  system = new HelixResolvable(dumpPath + "/solver-system.xml.gz");
516 
517  for ( const PoolItem & pi : pool )
518  {
519  if ( system && pi.status().isInstalled() ) {
520  // system channel
521  system->addResolvable( pi );
522  } else {
523  // repo channels
524  Repository repo = pi.repository();
525  if (dumpPool) {
526  if (repoTable.find (repo) == repoTable.end()) {
527  repoTable[repo] = new HelixResolvable(dumpPath + "/"
528  + str::numstring((long)repo.id())
529  + "-package.xml.gz");
530  }
531  repoTable[repo]->addResolvable( pi );
532  }
533  }
534 
535  if ( pi.status().isToBeInstalled()
536  && !(pi.status().isBySolver())) {
537  items_to_install.push_back( pi );
538  }
539  if ( pi.status().isKept()
540  && !(pi.status().isBySolver())) {
541  items_keep.push_back( pi );
542  }
543  if ( pi.status().isToBeUninstalled()
544  && !(pi.status().isBySolver())) {
545  items_to_remove.push_back( pi );
546  }
547  if ( pi.status().isLocked()
548  && !(pi.status().isBySolver())) {
549  items_locked.push_back( pi );
550  }
551  }
552 
553  // writing control file "*-test.xml"
554  HelixControl control (dumpPath + "/solver-test.xml",
555  repoTable,
556  ZConfig::instance().systemArchitecture(),
557  target::Modalias::instance().modaliasList(),
558  ZConfig::instance().multiversionSpec(),
559  "solver-system.xml.gz");
560 
561  // In <setup>: resolver flags,...
562  control.addTagIf( "ignorealreadyrecommended", resolver.ignoreAlreadyRecommended() );
563  control.addTagIf( "onlyRequires", resolver.onlyRequires() );
564  control.addTagIf( "forceResolve", resolver.forceResolve() );
565 
566  control.addTagIf( "cleandepsOnRemove", resolver.cleandepsOnRemove() );
567 
568  control.addTagIf( "allowDowngrade", resolver.allowDowngrade() );
569  control.addTagIf( "allowNameChange", resolver.allowNameChange() );
570  control.addTagIf( "allowArchChange", resolver.allowArchChange() );
571  control.addTagIf( "allowVendorChange", resolver.allowVendorChange() );
572 
573  control.addTagIf( "dupAllowDowngrade", resolver.dupAllowDowngrade() );
574  control.addTagIf( "dupAllowNameChange", resolver.dupAllowNameChange() );
575  control.addTagIf( "dupAllowArchChange", resolver.dupAllowArchChange() );
576  control.addTagIf( "dupAllowVendorChange", resolver.dupAllowVendorChange() );
577 
578  control.closeSetup();
579  // Entering <trial>...
580 
581  for ( const PoolItem & pi : items_to_install )
582  { control.installResolvable( pi ); }
583 
584  for ( const PoolItem & pi : items_locked )
585  { control.lockResolvable( pi ); }
586 
587  for ( const PoolItem & pi : items_keep )
588  { control.keepResolvable( pi ); }
589 
590  for ( const PoolItem & pi : items_to_remove )
591  { control.deleteResolvable( pi ); }
592 
593  control.addDependencies (resolver.extraRequires(), resolver.extraConflicts());
594  control.addDependencies (SystemCheck::instance().requiredSystemCap(),
595  SystemCheck::instance().conflictSystemCap());
596  control.addUpgradeRepos( resolver.upgradeRepos() );
597 
598  control.addTagIf( "distupgrade", resolver.isUpgradeMode() );
599  control.addTagIf( "update", resolver.isUpdateMode() );
600  control.addTagIf( "verify", resolver.isVerifyingMode() );
601 
602  return true;
603 }
604 
605 
607  };// namespace detail
610  };// namespace solver
613 };// namespace zypp