Imported Upstream version 0.7.5
[platform/upstream/libsolv.git] / ext / testcase.c
1 /*
2  * Copyright (c) 2012, Novell Inc.
3  *
4  * This program is licensed under the BSD license, read LICENSE.BSD
5  * for further information
6  */
7
8 #include <sys/types.h>
9 #include <sys/stat.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <errno.h>
14
15 #include "pool.h"
16 #include "poolarch.h"
17 #include "poolvendor.h"
18 #include "evr.h"
19 #include "repo.h"
20 #include "repo_solv.h"
21 #include "solver.h"
22 #include "solverdebug.h"
23 #include "chksum.h"
24 #include "testcase.h"
25 #include "selection.h"
26 #include "solv_xfopen.h"
27 #if ENABLE_TESTCASE_HELIXREPO
28 #include "ext/repo_helix.h"
29 #endif
30
31 #ifdef _WIN32
32   #include <direct.h>
33 #endif
34
35 /* see repo_testcase.c */
36 struct oplist {
37   Id flags;
38   const char *opname;
39 };
40 extern struct oplist oplist[];
41
42
43 static struct job2str {
44   Id job;
45   const char *str;
46 } job2str[] = {
47   { SOLVER_NOOP,           "noop" },
48   { SOLVER_INSTALL,        "install" },
49   { SOLVER_ERASE,          "erase" },
50   { SOLVER_UPDATE,         "update" },
51   { SOLVER_WEAKENDEPS,     "weakendeps" },
52   { SOLVER_MULTIVERSION,   "multiversion" },
53   { SOLVER_MULTIVERSION,   "noobsoletes" },     /* old name */
54   { SOLVER_LOCK,           "lock" },
55   { SOLVER_DISTUPGRADE,    "distupgrade" },
56   { SOLVER_VERIFY,         "verify" },
57   { SOLVER_DROP_ORPHANED,  "droporphaned" },
58   { SOLVER_USERINSTALLED,  "userinstalled" },
59   { SOLVER_ALLOWUNINSTALL, "allowuninstall" },
60   { SOLVER_FAVOR,          "favor" },
61   { SOLVER_DISFAVOR,       "disfavor" },
62   { 0, 0 }
63 };
64
65 static struct jobflags2str {
66   Id flag;
67   const char *str;
68 } jobflags2str[] = {
69   { SOLVER_WEAK,      "weak" },
70   { SOLVER_ESSENTIAL, "essential" },
71   { SOLVER_CLEANDEPS, "cleandeps" },
72   { SOLVER_ORUPDATE,  "orupdate" },
73   { SOLVER_FORCEBEST, "forcebest" },
74   { SOLVER_TARGETED,  "targeted" },
75   { SOLVER_NOTBYUSER, "notbyuser" },
76   { SOLVER_SETEV,     "setev" },
77   { SOLVER_SETEVR,    "setevr" },
78   { SOLVER_SETARCH,   "setarch" },
79   { SOLVER_SETVENDOR, "setvendor" },
80   { SOLVER_SETREPO,   "setrepo" },
81   { SOLVER_NOAUTOSET, "noautoset" },
82   { 0, 0 }
83 };
84
85 static struct resultflags2str {
86   Id flag;
87   const char *str;
88 } resultflags2str[] = {
89   { TESTCASE_RESULT_TRANSACTION,        "transaction" },
90   { TESTCASE_RESULT_PROBLEMS,           "problems" },
91   { TESTCASE_RESULT_ORPHANED,           "orphaned" },
92   { TESTCASE_RESULT_RECOMMENDED,        "recommended" },
93   { TESTCASE_RESULT_UNNEEDED,           "unneeded" },
94   { TESTCASE_RESULT_ALTERNATIVES,       "alternatives" },
95   { TESTCASE_RESULT_RULES,              "rules" },
96   { TESTCASE_RESULT_GENID,              "genid" },
97   { TESTCASE_RESULT_REASON,             "reason" },
98   { TESTCASE_RESULT_CLEANDEPS,          "cleandeps" },
99   { TESTCASE_RESULT_JOBS,               "jobs" },
100   { TESTCASE_RESULT_USERINSTALLED,      "userinstalled" },
101   { 0, 0 }
102 };
103
104 static struct solverflags2str {
105   Id flag;
106   const char *str;
107   int def;
108 } solverflags2str[] = {
109   { SOLVER_FLAG_ALLOW_DOWNGRADE,            "allowdowngrade", 0 },
110   { SOLVER_FLAG_ALLOW_NAMECHANGE,           "allownamechange", 1 },
111   { SOLVER_FLAG_ALLOW_ARCHCHANGE,           "allowarchchange", 0 },
112   { SOLVER_FLAG_ALLOW_VENDORCHANGE,         "allowvendorchange", 0 },
113   { SOLVER_FLAG_ALLOW_UNINSTALL,            "allowuninstall", 0 },
114   { SOLVER_FLAG_NO_UPDATEPROVIDE,           "noupdateprovide", 0 },
115   { SOLVER_FLAG_SPLITPROVIDES,              "splitprovides", 0 },
116   { SOLVER_FLAG_IGNORE_RECOMMENDED,         "ignorerecommended", 0 },
117   { SOLVER_FLAG_ADD_ALREADY_RECOMMENDED,    "addalreadyrecommended", 0 },
118   { SOLVER_FLAG_NO_INFARCHCHECK,            "noinfarchcheck", 0 },
119   { SOLVER_FLAG_KEEP_EXPLICIT_OBSOLETES,    "keepexplicitobsoletes", 0 },
120   { SOLVER_FLAG_BEST_OBEY_POLICY,           "bestobeypolicy", 0 },
121   { SOLVER_FLAG_NO_AUTOTARGET,              "noautotarget", 0 },
122   { SOLVER_FLAG_DUP_ALLOW_DOWNGRADE,        "dupallowdowngrade", 1 },
123   { SOLVER_FLAG_DUP_ALLOW_ARCHCHANGE,       "dupallowarchchange", 1 },
124   { SOLVER_FLAG_DUP_ALLOW_VENDORCHANGE,     "dupallowvendorchange", 1 },
125   { SOLVER_FLAG_DUP_ALLOW_NAMECHANGE,       "dupallownamechange", 1 },
126   { SOLVER_FLAG_KEEP_ORPHANS,               "keeporphans", 0 },
127   { SOLVER_FLAG_BREAK_ORPHANS,              "breakorphans", 0 },
128   { SOLVER_FLAG_FOCUS_INSTALLED,            "focusinstalled", 0 },
129   { SOLVER_FLAG_YUM_OBSOLETES,              "yumobsoletes", 0 },
130   { SOLVER_FLAG_NEED_UPDATEPROVIDE,         "needupdateprovide", 0 },
131   { SOLVER_FLAG_URPM_REORDER,               "urpmreorder", 0 },
132   { SOLVER_FLAG_FOCUS_BEST,                 "focusbest", 0 },
133   { SOLVER_FLAG_STRONG_RECOMMENDS,          "strongrecommends", 0 },
134   { SOLVER_FLAG_INSTALL_ALSO_UPDATES,       "installalsoupdates", 0 },
135   { SOLVER_FLAG_ONLY_NAMESPACE_RECOMMENDED, "onlynamespacerecommended", 0 },
136   { 0, 0, 0 }
137 };
138
139 static struct poolflags2str {
140   Id flag;
141   const char *str;
142   int def;
143 } poolflags2str[] = {
144   { POOL_FLAG_PROMOTEEPOCH,                 "promoteepoch", 0 },
145   { POOL_FLAG_FORBIDSELFCONFLICTS,          "forbidselfconflicts", 0 },
146   { POOL_FLAG_OBSOLETEUSESPROVIDES,         "obsoleteusesprovides", 0 },
147   { POOL_FLAG_IMPLICITOBSOLETEUSESPROVIDES, "implicitobsoleteusesprovides", 0 },
148   { POOL_FLAG_OBSOLETEUSESCOLORS,           "obsoleteusescolors", 0 },
149   { POOL_FLAG_IMPLICITOBSOLETEUSESCOLORS,   "implicitobsoleteusescolors", 0 },
150   { POOL_FLAG_NOINSTALLEDOBSOLETES,         "noinstalledobsoletes", 0 },
151   { POOL_FLAG_HAVEDISTEPOCH,                "havedistepoch", 0 },
152   { POOL_FLAG_NOOBSOLETESMULTIVERSION,      "noobsoletesmultiversion", 0 },
153   { POOL_FLAG_ADDFILEPROVIDESFILTERED,      "addfileprovidesfiltered", 0 },
154   { POOL_FLAG_NOWHATPROVIDESAUX,            "nowhatprovidesaux", 0 },
155   { 0, 0, 0 }
156 };
157
158 static struct disttype2str {
159   Id type;
160   const char *str;
161 } disttype2str[] = {
162   { DISTTYPE_RPM,  "rpm" },
163   { DISTTYPE_DEB,  "deb" },
164   { DISTTYPE_ARCH, "arch" },
165   { DISTTYPE_HAIKU, "haiku" },
166   { DISTTYPE_CONDA, "conda" },
167   { 0, 0 }
168 };
169
170 static struct selflags2str {
171   Id flag;
172   const char *str;
173 } selflags2str[] = {
174   { SELECTION_NAME, "name" },
175   { SELECTION_PROVIDES, "provides" },
176   { SELECTION_FILELIST, "filelist" },
177   { SELECTION_CANON, "canon" },
178   { SELECTION_DOTARCH, "dotarch" },
179   { SELECTION_REL, "rel" },
180   { SELECTION_INSTALLED_ONLY, "installedonly" },
181   { SELECTION_GLOB, "glob" },
182   { SELECTION_FLAT, "flat" },
183   { SELECTION_NOCASE, "nocase" },
184   { SELECTION_SOURCE_ONLY, "sourceonly" },
185   { SELECTION_WITH_SOURCE, "withsource" },
186   { SELECTION_SKIP_KIND, "skipkind" },
187   { SELECTION_MATCH_DEPSTR, "depstr" },
188   { SELECTION_WITH_DISABLED, "withdisabled" },
189   { SELECTION_WITH_BADARCH, "withbadarch" },
190   { SELECTION_ADD, "add" },
191   { SELECTION_SUBTRACT, "subtract" },
192   { SELECTION_FILTER, "filter" },
193   { 0, 0 }
194 };
195
196 static const char *features[] = {
197 #ifdef ENABLE_LINKED_PKGS
198   "linked_packages",
199 #endif
200 #ifdef ENABLE_COMPLEX_DEPS
201   "complex_deps",
202 #endif
203 #if ENABLE_TESTCASE_HELIXREPO
204   "testcase_helixrepo",
205 #endif
206   0
207 };
208
209 typedef struct strqueue {
210   char **str;
211   int nstr;
212 } Strqueue;
213
214 #define STRQUEUE_BLOCK 63
215
216 static void
217 strqueue_init(Strqueue *q)
218 {
219   q->str = 0;
220   q->nstr = 0;
221 }
222
223 static void
224 strqueue_free(Strqueue *q)
225 {
226   int i;
227   for (i = 0; i < q->nstr; i++)
228     solv_free(q->str[i]);
229   q->str = solv_free(q->str);
230   q->nstr = 0;
231 }
232
233 static void
234 strqueue_push(Strqueue *q, const char *s)
235 {
236   q->str = solv_extend(q->str, q->nstr, 1, sizeof(*q->str), STRQUEUE_BLOCK);
237   q->str[q->nstr++] = solv_strdup(s);
238 }
239
240 static void
241 strqueue_pushjoin(Strqueue *q, const char *s1, const char *s2, const char *s3)
242 {
243   q->str = solv_extend(q->str, q->nstr, 1, sizeof(*q->str), STRQUEUE_BLOCK);
244   q->str[q->nstr++] = solv_dupjoin(s1, s2, s3);
245 }
246
247 static int
248 strqueue_sort_cmp(const void *ap, const void *bp, void *dp)
249 {
250   const char *a = *(const char **)ap;
251   const char *b = *(const char **)bp;
252   return strcmp(a ? a : "", b ? b : "");
253 }
254
255 static void
256 strqueue_sort(Strqueue *q)
257 {
258   if (q->nstr > 1)
259     solv_sort(q->str, q->nstr, sizeof(*q->str), strqueue_sort_cmp, 0);
260 }
261
262 static void
263 strqueue_sort_u(Strqueue *q)
264 {
265   int i, j;
266   strqueue_sort(q);
267   for (i = j = 0; i < q->nstr; i++)
268     if (!j || strqueue_sort_cmp(q->str + i, q->str + j - 1, 0) != 0)
269       q->str[j++] = q->str[i];
270   q->nstr = j;
271 }
272
273 static char *
274 strqueue_join(Strqueue *q)
275 {
276   int i, l = 0;
277   char *r, *rp;
278   for (i = 0; i < q->nstr; i++)
279     if (q->str[i])
280       l += strlen(q->str[i]) + 1;
281   l++;  /* trailing \0 */
282   r = solv_malloc(l);
283   rp = r;
284   for (i = 0; i < q->nstr; i++)
285     if (q->str[i])
286       {
287         strcpy(rp, q->str[i]);
288         rp += strlen(rp);
289         *rp++ = '\n';
290       }
291   *rp = 0;
292   return r;
293 }
294
295 static void
296 strqueue_split(Strqueue *q, const char *s)
297 {
298   const char *p;
299   if (!s)
300     return;
301   while ((p = strchr(s, '\n')) != 0)
302     {
303       q->str = solv_extend(q->str, q->nstr, 1, sizeof(*q->str), STRQUEUE_BLOCK);
304       q->str[q->nstr] = solv_malloc(p - s + 1);
305       if (p > s)
306         memcpy(q->str[q->nstr], s, p - s);
307       q->str[q->nstr][p - s] = 0;
308       q->nstr++;
309       s = p + 1;
310     }
311   if (*s)
312     strqueue_push(q, s);
313 }
314
315 static void
316 strqueue_diff(Strqueue *sq1, Strqueue *sq2, Strqueue *osq)
317 {
318   int i = 0, j = 0;
319   while (i < sq1->nstr && j < sq2->nstr)
320     {
321       int r = strqueue_sort_cmp(sq1->str + i, sq2->str + j, 0);
322       if (!r)
323         i++, j++;
324       else if (r < 0)
325         strqueue_pushjoin(osq, "-", sq1->str[i++], 0);
326       else
327         strqueue_pushjoin(osq, "+", sq2->str[j++], 0);
328     }
329   while (i < sq1->nstr)
330     strqueue_pushjoin(osq, "-", sq1->str[i++], 0);
331   while (j < sq2->nstr)
332     strqueue_pushjoin(osq, "+", sq2->str[j++], 0);
333 }
334
335 /**********************************************************/
336
337 const char *
338 testcase_repoid2str(Pool *pool, Id repoid)
339 {
340   Repo *repo = pool_id2repo(pool, repoid);
341   if (repo->name)
342     {
343       char *r = pool_tmpjoin(pool, repo->name, 0, 0);
344       char *rp;
345       for (rp = r; *rp; rp++)
346         if (*rp == ' ' || *rp == '\t')
347           *rp = '_';
348       return r;
349     }
350   else
351     {
352       char buf[20];
353       sprintf(buf, "#%d", repoid);
354       return pool_tmpjoin(pool, buf, 0, 0);
355     }
356 }
357
358 const char *
359 testcase_solvid2str(Pool *pool, Id p)
360 {
361   Solvable *s = pool->solvables + p;
362   const char *n, *e, *a;
363   char *str, buf[20];
364
365   if (p == SYSTEMSOLVABLE)
366     return "@SYSTEM";
367   n = pool_id2str(pool, s->name);
368   e = pool_id2str(pool, s->evr);
369   a = pool_id2str(pool, s->arch);
370   str = pool_alloctmpspace(pool, strlen(n) + strlen(e) + strlen(a) + 3);
371   sprintf(str, "%s-%s", n, e);
372   if (solvable_lookup_type(s, SOLVABLE_BUILDFLAVOR))
373     {
374       Queue flavorq;
375       int i;
376
377       queue_init(&flavorq);
378       solvable_lookup_idarray(s, SOLVABLE_BUILDFLAVOR, &flavorq);
379       for (i = 0; i < flavorq.count; i++)
380         str = pool_tmpappend(pool, str, "-", pool_id2str(pool, flavorq.elements[i]));
381       queue_free(&flavorq);
382     }
383   if (s->arch)
384     str = pool_tmpappend(pool, str, ".", a);
385   if (!s->repo)
386     return pool_tmpappend(pool, str, "@", 0);
387   if (s->repo->name)
388     {
389       int l = strlen(str);
390       str = pool_tmpappend(pool, str, "@", s->repo->name);
391       for (; str[l]; l++)
392         if (str[l] == ' ' || str[l] == '\t')
393           str[l] = '_';
394       return str;
395     }
396   sprintf(buf, "@#%d", s->repo->repoid);
397   return pool_tmpappend(pool, str, buf, 0);
398 }
399
400 Repo *
401 testcase_str2repo(Pool *pool, const char *str)
402 {
403   int repoid;
404   Repo *repo = 0;
405   if (str[0] == '#' && (str[1] >= '0' && str[1] <= '9'))
406     {
407       int j;
408       repoid = 0;
409       for (j = 1; str[j] >= '0' && str[j] <= '9'; j++)
410         repoid = repoid * 10 + (str[j] - '0');
411       if (!str[j] && repoid > 0 && repoid < pool->nrepos)
412         repo = pool_id2repo(pool, repoid);
413     }
414   if (!repo)
415     {
416       FOR_REPOS(repoid, repo)
417         {
418           int i, l;
419           if (!repo->name)
420             continue;
421           l = strlen(repo->name);
422           for (i = 0; i < l; i++)
423             {
424               int c = repo->name[i];
425               if (c == ' ' || c == '\t')
426                 c = '_';
427               if (c != str[i])
428                 break;
429             }
430           if (i == l && !str[l])
431             break;
432         }
433       if (repoid >= pool->nrepos)
434         repo = 0;
435     }
436   return repo;
437 }
438
439 /* check evr and buildflavors */
440 static int
441 str2solvid_check(Pool *pool, Solvable *s, const char *start, const char *end, Id evrid)
442 {
443   if (!solvable_lookup_type(s, SOLVABLE_BUILDFLAVOR))
444     {
445       /* just check the evr */
446       return evrid && s->evr == evrid;
447     }
448   else
449     {
450       Queue flavorq;
451       int i;
452
453       queue_init(&flavorq);
454       solvable_lookup_idarray(s, SOLVABLE_BUILDFLAVOR, &flavorq);
455       queue_unshift(&flavorq, s->evr);
456       for (i = 0; i < flavorq.count; i++)
457         {
458           const char *part = pool_id2str(pool, flavorq.elements[i]);
459           size_t partl = strlen(part);
460           if (start + partl > end || strncmp(start, part, partl) != 0)
461             break;
462           start += partl;
463           if (i + 1 < flavorq.count)
464             {
465               if (start >= end || *start != '-')
466                 break;
467               start++;
468             }
469         }
470       if (i < flavorq.count)
471         {
472           queue_free(&flavorq);
473           return 0;
474         }
475       queue_free(&flavorq);
476       return start == end;
477     }
478 }
479
480 Id
481 testcase_str2solvid(Pool *pool, const char *str)
482 {
483   int i, l = strlen(str);
484   int repostart;
485   Repo *repo;
486   Id arch;
487
488   if (!l)
489     return 0;
490   if (*str == '@' && !strcmp(str, "@SYSTEM"))
491     return SYSTEMSOLVABLE;
492   repo = 0;
493   for (i = l - 1; i >= 0; i--)
494     if (str[i] == '@' && (repo = testcase_str2repo(pool, str + i + 1)) != 0)
495       break;
496   if (i < 0)
497     i = l;
498   repostart = i;
499   /* now find the arch (if present) */
500   arch = 0;
501   for (i = repostart - 1; i > 0; i--)
502     if (str[i] == '.')
503       {
504         arch = pool_strn2id(pool, str + i + 1, repostart - (i + 1), 0);
505         if (arch)
506           repostart = i;
507         break;
508       }
509   /* now find the name */
510   for (i = repostart - 1; i > 0; i--)
511     {
512       if (str[i] == '-')
513         {
514           Id nid, evrid, p, pp;
515           nid = pool_strn2id(pool, str, i, 0);
516           if (!nid)
517             continue;
518           evrid = pool_strn2id(pool, str + i + 1, repostart - (i + 1), 0);
519           /* first check whatprovides */
520           FOR_PROVIDES(p, pp, nid)
521             {
522               Solvable *s = pool->solvables + p;
523               if (s->name != nid)
524                 continue;
525               if (repo && s->repo != repo)
526                 continue;
527               if (arch && s->arch != arch)
528                 continue;
529               if (str2solvid_check(pool, s, str + i + 1, str + repostart, evrid))
530                 return p;
531             }
532           /* maybe it's not installable and thus not in whatprovides. do a slow search */
533           if (repo)
534             {
535               Solvable *s;
536               FOR_REPO_SOLVABLES(repo, p, s)
537                 {
538                   if (s->name != nid)
539                     continue;
540                   if (arch && s->arch != arch)
541                     continue;
542                   if (str2solvid_check(pool, s, str + i + 1, str + repostart, evrid))
543                     return p;
544                 }
545             }
546           else
547             {
548               FOR_POOL_SOLVABLES(p)
549                 {
550                   Solvable *s = pool->solvables + p;
551                   if (s->name != nid)
552                     continue;
553                   if (arch && s->arch != arch)
554                     continue;
555                   if (str2solvid_check(pool, s, str + i + 1, str + repostart, evrid))
556                     return p;
557                 }
558             }
559         }
560     }
561   return 0;
562 }
563
564 const char *
565 testcase_job2str(Pool *pool, Id how, Id what)
566 {
567   char *ret;
568   const char *jobstr;
569   const char *selstr;
570   const char *pkgstr;
571   int i, o;
572   Id select = how & SOLVER_SELECTMASK;
573
574   for (i = 0; job2str[i].str; i++)
575     if ((how & SOLVER_JOBMASK) == job2str[i].job)
576       break;
577   jobstr = job2str[i].str ? job2str[i].str : "unknown";
578   if (select == SOLVER_SOLVABLE)
579     {
580       selstr = " pkg ";
581       pkgstr = testcase_solvid2str(pool, what);
582     }
583   else if (select == SOLVER_SOLVABLE_NAME)
584     {
585       selstr = " name ";
586       pkgstr = testcase_dep2str(pool, what);
587     }
588   else if (select == SOLVER_SOLVABLE_PROVIDES)
589     {
590       selstr = " provides ";
591       pkgstr = testcase_dep2str(pool, what);
592     }
593   else if (select == SOLVER_SOLVABLE_ONE_OF)
594     {
595       Id p;
596       selstr = " oneof ";
597       pkgstr = 0;
598       while ((p = pool->whatprovidesdata[what++]) != 0)
599         {
600           const char *s = testcase_solvid2str(pool, p);
601           if (pkgstr)
602             {
603               pkgstr = pool_tmpappend(pool, pkgstr, " ", s);
604               pool_freetmpspace(pool, s);
605             }
606           else
607             pkgstr = s;
608         }
609       if (!pkgstr)
610         pkgstr = "nothing";
611     }
612   else if (select == SOLVER_SOLVABLE_REPO)
613     {
614       Repo *repo = pool_id2repo(pool, what);
615       selstr = " repo ";
616       if (!repo->name)
617         {
618           char buf[20];
619           sprintf(buf, "#%d", repo->repoid);
620           pkgstr = pool_tmpjoin(pool, buf, 0, 0);
621         }
622       else
623         pkgstr = pool_tmpjoin(pool, repo->name, 0, 0);
624     }
625   else if (select == SOLVER_SOLVABLE_ALL)
626     {
627       selstr = " all ";
628       pkgstr = "packages";
629     }
630   else
631     {
632       selstr = " unknown ";
633       pkgstr = "unknown";
634     }
635   ret = pool_tmpjoin(pool, jobstr, selstr, pkgstr);
636   o = strlen(ret);
637   ret = pool_tmpappend(pool, ret, " ", 0);
638   for (i = 0; jobflags2str[i].str; i++)
639     if ((how & jobflags2str[i].flag) != 0)
640       ret = pool_tmpappend(pool, ret, ",", jobflags2str[i].str);
641   if (!ret[o + 1])
642     ret[o] = 0;
643   else
644     {
645       ret[o + 1] = '[';
646       ret = pool_tmpappend(pool, ret, "]", 0);
647     }
648   return ret;
649 }
650
651 static int
652 str2selflags(Pool *pool, char *s)       /* modifies the string! */
653 {
654   int i, selflags = 0;
655   while (s)
656     {
657       char *se = strchr(s, ',');
658       if (se)
659         *se++ = 0;
660       for (i = 0; selflags2str[i].str; i++)
661         if (!strcmp(s, selflags2str[i].str))
662           {
663             selflags |= selflags2str[i].flag;
664             break;
665           }
666       if (!selflags2str[i].str)
667         pool_error(pool, 0, "str2job: unknown selection flag '%s'", s);
668       s = se;
669     }
670   return selflags;
671 }
672
673 static int
674 str2jobflags(Pool *pool, char *s)       /* modifies the string */
675 {
676   int i, jobflags = 0;
677   while (s)
678     {
679       char *se = strchr(s, ',');
680       if (se)
681         *se++ = 0;
682       for (i = 0; jobflags2str[i].str; i++)
683         if (!strcmp(s, jobflags2str[i].str))
684           {
685             jobflags |= jobflags2str[i].flag;
686             break;
687           }
688       if (!jobflags2str[i].str)
689         pool_error(pool, 0, "str2job: unknown job flag '%s'", s);
690       s = se;
691     }
692   return jobflags;
693 }
694
695 static Id
696 testcase_str2jobsel(Pool *pool, const char *caller, char **pieces, int npieces, Id *whatp)
697 {
698   Id job, what;
699   if (!strcmp(pieces[0], "pkg") && npieces == 2)
700     {
701       job = SOLVER_SOLVABLE;
702       what = testcase_str2solvid(pool, pieces[1]);
703       if (!what)
704         return pool_error(pool, -1, "%s: unknown package '%s'", caller, pieces[1]);
705     }
706   else if (!strcmp(pieces[0], "name") || !strcmp(pieces[0], "provides"))
707     {
708       /* join em again for dep2str... */
709       char *sp;
710       for (sp = pieces[1]; sp < pieces[npieces - 1]; sp++)
711         if (*sp == 0)
712           *sp = ' ';
713       what = 0;
714       if (pieces[0][0] == 'p' && strncmp(pieces[1], "namespace:", 10) == 0)
715         {
716           char *spe = strchr(pieces[1], '(');
717           int l = strlen(pieces[1]);
718           if (spe && pieces[1][l - 1] == ')')
719             {
720               /* special namespace provides */
721               if (strcmp(spe, "(<NULL>)") != 0)
722                 {
723                   pieces[1][l - 1] = 0;
724                   what = testcase_str2dep(pool, spe + 1);
725                   pieces[1][l - 1] = ')';
726                 }
727               what = pool_rel2id(pool, pool_strn2id(pool, pieces[1], spe - pieces[1], 1), what, REL_NAMESPACE, 1);
728             }
729         }
730       if (!what)
731         what = testcase_str2dep(pool, pieces[1]);
732       if (pieces[0][0] == 'n')
733         job = SOLVER_SOLVABLE_NAME;
734       else
735         job = SOLVER_SOLVABLE_PROVIDES;
736     }
737   else if (!strcmp(pieces[0], "oneof"))
738     {
739       Queue q;
740       job = SOLVER_SOLVABLE_ONE_OF;
741       queue_init(&q);
742       if (npieces > 1 && strcmp(pieces[1], "nothing") != 0)
743         {
744           int i;
745           for (i = 1; i < npieces; i++)
746             {
747               Id p = testcase_str2solvid(pool, pieces[i]);
748               if (!p)
749                 {
750                   queue_free(&q);
751                   return pool_error(pool, -1, "%s: unknown package '%s'", caller, pieces[i]);
752                 }
753               queue_push(&q, p);
754             }
755         }
756       what = pool_queuetowhatprovides(pool, &q);
757       queue_free(&q);
758     }
759   else if (!strcmp(pieces[0], "repo") && npieces == 2)
760     {
761       Repo *repo = testcase_str2repo(pool, pieces[1]);
762       if (!repo)
763         return pool_error(pool, -1, "%s: unknown repo '%s'", caller, pieces[1]);
764       job = SOLVER_SOLVABLE_REPO;
765       what = repo->repoid;
766     }
767   else if (!strcmp(pieces[0], "all") && npieces == 2 && !strcmp(pieces[1], "packages"))
768     {
769       job = SOLVER_SOLVABLE_ALL;
770       what = 0;
771     }
772   else
773     {
774       /* join em again for the error message... */
775       char *sp;
776       for (sp = pieces[0]; sp < pieces[npieces - 1]; sp++)
777         if (*sp == 0)
778           *sp = ' ';
779       return pool_error(pool, -1, "%s: bad line '%s'", caller, pieces[0]);
780     }
781   *whatp = what;
782   return job;
783 }
784
785 Id
786 testcase_str2job(Pool *pool, const char *str, Id *whatp)
787 {
788   int i;
789   Id job, jobsel;
790   Id what;
791   char *s;
792   char **pieces = 0;
793   int npieces = 0;
794
795   *whatp = 0;
796   /* so we can patch it */
797   s = pool_tmpjoin(pool, str, 0, 0);
798   /* split it in pieces */
799   for (;;)
800     {
801       while (*s == ' ' || *s == '\t')
802         s++;
803       if (!*s)
804         break;
805       pieces = solv_extend(pieces, npieces, 1, sizeof(*pieces), 7);
806       pieces[npieces++] = s;
807       while (*s && *s != ' ' && *s != '\t')
808         s++;
809       if (*s)
810         *s++ = 0;
811     }
812   if (npieces < 3)
813     {
814       pool_error(pool, -1, "str2job: bad line '%s'", str);
815       solv_free(pieces);
816       return -1;
817     }
818
819   for (i = 0; job2str[i].str; i++)
820     if (!strcmp(pieces[0], job2str[i].str))
821       break;
822   if (!job2str[i].str)
823     {
824       pool_error(pool, -1, "str2job: unknown job '%s'", str);
825       solv_free(pieces);
826       return -1;
827     }
828   job = job2str[i].job;
829   what = 0;
830   if (npieces > 3)
831     {
832       char *flags = pieces[npieces - 1];
833       if (*flags == '[' && flags[strlen(flags) - 1] == ']')
834         {
835           npieces--;
836           flags++;
837           flags[strlen(flags) - 1] = 0;
838           job |= str2jobflags(pool, flags);
839         }
840     }
841   jobsel = testcase_str2jobsel(pool, "str2job", pieces + 1, npieces - 1, &what);
842   solv_free(pieces);
843   if (jobsel == -1)
844     return -1;
845   *whatp = what;
846   return job | jobsel;
847 }
848
849 #define SELECTIONJOB_MATCHDEPS          1
850 #define SELECTIONJOB_MATCHDEPID         2
851 #define SELECTIONJOB_MATCHSOLVABLE      3
852
853 static int
854 addselectionjob(Pool *pool, char **pieces, int npieces, Queue *jobqueue, int type, int keyname)
855 {
856   Id job;
857   int i, r = 0;
858   int selflags;
859   Queue sel;
860   char *sp;
861
862   for (i = 0; job2str[i].str; i++)
863     if (!strcmp(pieces[0], job2str[i].str))
864       break;
865   if (!job2str[i].str)
866     return pool_error(pool, -1, "selstr2job: unknown job '%s'", pieces[0]);
867   job = job2str[i].job;
868   if (npieces > 3)
869     {
870       char *flags = pieces[npieces - 1];
871       if (*flags == '[' && flags[strlen(flags) - 1] == ']')
872         {
873           npieces--;
874           flags++;
875           flags[strlen(flags) - 1] = 0;
876           job |= str2jobflags(pool, flags);
877         }
878     }
879   if (npieces < 4)
880     return pool_error(pool, -1, "selstr2job: no selection flags");
881   selflags = str2selflags(pool, pieces[npieces - 1]);
882   /* re-join pieces */
883   for (sp = pieces[2]; sp < pieces[npieces - 2]; sp++)
884     if (*sp == 0)
885       *sp = ' ';
886   queue_init(&sel);
887   if (selflags & (SELECTION_ADD | SELECTION_SUBTRACT | SELECTION_FILTER))
888     {
889       for (i = 0; i < jobqueue->count; i += 2)
890         queue_push2(&sel, jobqueue->elements[i] & (SOLVER_SELECTMASK | SOLVER_SETMASK), jobqueue->elements[i + 1]);
891       queue_empty(jobqueue);
892     }
893   if (!type)
894     r = selection_make(pool, &sel, pieces[2], selflags);
895   else if (type == SELECTIONJOB_MATCHDEPS)
896     r = selection_make_matchdeps(pool, &sel, pieces[2], selflags, keyname, 0);
897   else if (type == SELECTIONJOB_MATCHDEPID)
898     r = selection_make_matchdepid(pool, &sel, testcase_str2dep(pool, pieces[2]), selflags, keyname, 0);
899   else if (type == SELECTIONJOB_MATCHSOLVABLE)
900     r = selection_make_matchsolvable(pool, &sel, testcase_str2solvid(pool, pieces[2]), selflags, keyname, 0);
901   for (i = 0; i < sel.count; i += 2)
902     queue_push2(jobqueue, job | sel.elements[i], sel.elements[i + 1]);
903   queue_free(&sel);
904   return r;
905 }
906
907 const char *
908 testcase_getpoolflags(Pool *pool)
909 {
910   const char *str = 0;
911   int i, v;
912   for (i = 0; poolflags2str[i].str; i++)
913     {
914       v = pool_get_flag(pool, poolflags2str[i].flag);
915       if (v == poolflags2str[i].def)
916         continue;
917       str = pool_tmpappend(pool, str, v ? " " : " !", poolflags2str[i].str);
918     }
919   return str ? str + 1 : "";
920 }
921
922 int
923 testcase_setpoolflags(Pool *pool, const char *str)
924 {
925   const char *p = str, *s;
926   int i, v;
927   for (;;)
928     {
929       while (*p == ' ' || *p == '\t' || *p == ',')
930         p++;
931       v = 1;
932       if (*p == '!')
933         {
934           p++;
935           v = 0;
936         }
937       if (!*p)
938         break;
939       s = p;
940       while (*p && *p != ' ' && *p != '\t' && *p != ',')
941         p++;
942       for (i = 0; poolflags2str[i].str; i++)
943         if (!strncmp(poolflags2str[i].str, s, p - s) && poolflags2str[i].str[p - s] == 0)
944           break;
945       if (!poolflags2str[i].str)
946         return pool_error(pool, 0, "setpoolflags: unknown flag '%.*s'", (int)(p - s), s);
947       pool_set_flag(pool, poolflags2str[i].flag, v);
948     }
949   return 1;
950 }
951
952 void
953 testcase_resetpoolflags(Pool *pool)
954 {
955   int i;
956   for (i = 0; poolflags2str[i].str; i++)
957     pool_set_flag(pool, poolflags2str[i].flag, poolflags2str[i].def);
958 }
959
960 const char *
961 testcase_getsolverflags(Solver *solv)
962 {
963   Pool *pool = solv->pool;
964   const char *str = 0;
965   int i, v;
966   for (i = 0; solverflags2str[i].str; i++)
967     {
968       v = solver_get_flag(solv, solverflags2str[i].flag);
969       if (v == solverflags2str[i].def)
970         continue;
971       str = pool_tmpappend(pool, str, v ? " " : " !", solverflags2str[i].str);
972     }
973   return str ? str + 1 : "";
974 }
975
976 int
977 testcase_setsolverflags(Solver *solv, const char *str)
978 {
979   const char *p = str, *s;
980   int i, v;
981   for (;;)
982     {
983       while (*p == ' ' || *p == '\t' || *p == ',')
984         p++;
985       v = 1;
986       if (*p == '!')
987         {
988           p++;
989           v = 0;
990         }
991       if (!*p)
992         break;
993       s = p;
994       while (*p && *p != ' ' && *p != '\t' && *p != ',')
995         p++;
996       for (i = 0; solverflags2str[i].str; i++)
997         if (!strncmp(solverflags2str[i].str, s, p - s) && solverflags2str[i].str[p - s] == 0)
998           break;
999       if (!solverflags2str[i].str)
1000         return pool_error(solv->pool, 0, "setsolverflags: unknown flag '%.*s'", (int)(p - s), s);
1001       if (solver_set_flag(solv, solverflags2str[i].flag, v) == -1)
1002         return pool_error(solv->pool, 0, "setsolverflags: unsupported flag '%s'", solverflags2str[i].str);
1003     }
1004   return 1;
1005 }
1006
1007 void
1008 testcase_resetsolverflags(Solver *solv)
1009 {
1010   int i;
1011   for (i = 0; solverflags2str[i].str; i++)
1012     solver_set_flag(solv, solverflags2str[i].flag, solverflags2str[i].def);
1013 }
1014
1015 static const char *
1016 testcase_ruleid(Solver *solv, Id rid)
1017 {
1018   Strqueue sq;
1019   Queue q;
1020   int i;
1021   Chksum *chk;
1022   const unsigned char *md5;
1023   int md5l;
1024   const char *s;
1025
1026   queue_init(&q);
1027   strqueue_init(&sq);
1028   solver_ruleliterals(solv, rid, &q);
1029   for (i = 0; i < q.count; i++)
1030     {
1031       Id p = q.elements[i];
1032       s = testcase_solvid2str(solv->pool, p > 0 ? p : -p);
1033       if (p < 0)
1034         s = pool_tmpjoin(solv->pool, "!", s, 0);
1035       strqueue_push(&sq, s);
1036     }
1037   queue_free(&q);
1038   strqueue_sort_u(&sq);
1039   chk = solv_chksum_create(REPOKEY_TYPE_MD5);
1040   for (i = 0; i < sq.nstr; i++)
1041     solv_chksum_add(chk, sq.str[i], strlen(sq.str[i]) + 1);
1042   md5 = solv_chksum_get(chk, &md5l);
1043   s = pool_bin2hex(solv->pool, md5, md5l);
1044   chk = solv_chksum_free(chk, 0);
1045   strqueue_free(&sq);
1046   return s;
1047 }
1048
1049 static const char *
1050 testcase_problemid(Solver *solv, Id problem)
1051 {
1052   Strqueue sq;
1053   Queue q;
1054   Chksum *chk;
1055   const unsigned char *md5;
1056   int i, md5l;
1057   const char *s;
1058
1059   /* we build a hash of all rules that define the problem */
1060   queue_init(&q);
1061   strqueue_init(&sq);
1062   solver_findallproblemrules(solv, problem, &q);
1063   for (i = 0; i < q.count; i++)
1064     strqueue_push(&sq, testcase_ruleid(solv, q.elements[i]));
1065   queue_free(&q);
1066   strqueue_sort_u(&sq);
1067   chk = solv_chksum_create(REPOKEY_TYPE_MD5);
1068   for (i = 0; i < sq.nstr; i++)
1069     solv_chksum_add(chk, sq.str[i], strlen(sq.str[i]) + 1);
1070   md5 = solv_chksum_get(chk, &md5l);
1071   s = pool_bin2hex(solv->pool, md5, 4);
1072   chk = solv_chksum_free(chk, 0);
1073   strqueue_free(&sq);
1074   return s;
1075 }
1076
1077 static const char *
1078 testcase_solutionid(Solver *solv, Id problem, Id solution)
1079 {
1080   Id intid;
1081   Chksum *chk;
1082   const unsigned char *md5;
1083   int md5l;
1084   const char *s;
1085
1086   intid = solver_solutionelement_internalid(solv, problem, solution);
1087   /* internal stuff! handle with care! */
1088   if (intid < 0)
1089     {
1090       /* it's a job */
1091       s = testcase_job2str(solv->pool, solv->job.elements[-intid - 1], solv->job.elements[-intid]);
1092     }
1093   else
1094     {
1095       /* it's a rule */
1096       s = testcase_ruleid(solv, intid);
1097     }
1098   chk = solv_chksum_create(REPOKEY_TYPE_MD5);
1099   solv_chksum_add(chk, s, strlen(s) + 1);
1100   md5 = solv_chksum_get(chk, &md5l);
1101   s = pool_bin2hex(solv->pool, md5, 4);
1102   chk = solv_chksum_free(chk, 0);
1103   return s;
1104 }
1105
1106 static const char *
1107 testcase_alternativeid(Solver *solv, int type, Id id, Id from)
1108 {
1109   const char *s;
1110   Pool *pool = solv->pool;
1111   Chksum *chk;
1112   const unsigned char *md5;
1113   int md5l;
1114   chk = solv_chksum_create(REPOKEY_TYPE_MD5);
1115   if (type == SOLVER_ALTERNATIVE_TYPE_RECOMMENDS)
1116     {
1117       s = testcase_solvid2str(pool, from);
1118       solv_chksum_add(chk, s, strlen(s) + 1);
1119       s = testcase_dep2str(pool, id);
1120       solv_chksum_add(chk, s, strlen(s) + 1);
1121     }
1122   else if (type == SOLVER_ALTERNATIVE_TYPE_RULE)
1123     {
1124       s = testcase_ruleid(solv, id);
1125       solv_chksum_add(chk, s, strlen(s) + 1);
1126     }
1127   md5 = solv_chksum_get(chk, &md5l);
1128   s = pool_bin2hex(pool, md5, 4);
1129   chk = solv_chksum_free(chk, 0);
1130   return s;
1131 }
1132
1133 static struct class2str {
1134   Id class;
1135   const char *str;
1136 } class2str[] = {
1137   { SOLVER_TRANSACTION_ERASE,          "erase" },
1138   { SOLVER_TRANSACTION_INSTALL,        "install" },
1139   { SOLVER_TRANSACTION_REINSTALLED,    "reinstall" },
1140   { SOLVER_TRANSACTION_DOWNGRADED,     "downgrade" },
1141   { SOLVER_TRANSACTION_CHANGED,        "change" },
1142   { SOLVER_TRANSACTION_UPGRADED,       "upgrade" },
1143   { SOLVER_TRANSACTION_OBSOLETED,      "obsolete" },
1144   { SOLVER_TRANSACTION_MULTIINSTALL,   "multiinstall" },
1145   { SOLVER_TRANSACTION_MULTIREINSTALL, "multireinstall" },
1146   { 0, 0 }
1147 };
1148
1149 static struct reason2str {
1150   Id reason;
1151   const char *str;
1152 } reason2str[] = {
1153   { SOLVER_REASON_UNRELATED,            "unrelated" },
1154   { SOLVER_REASON_UNIT_RULE,            "unit" },
1155   { SOLVER_REASON_KEEP_INSTALLED,       "keep" },
1156   { SOLVER_REASON_RESOLVE_JOB,          "job" },
1157   { SOLVER_REASON_UPDATE_INSTALLED,     "update" },
1158   { SOLVER_REASON_CLEANDEPS_ERASE,      "cleandeps" },
1159   { SOLVER_REASON_RESOLVE,              "resolve" },
1160   { SOLVER_REASON_WEAKDEP,              "weakdep" },
1161   { SOLVER_REASON_RESOLVE_ORPHAN,       "orphan" },
1162
1163   { SOLVER_REASON_RECOMMENDED,          "recommended" },
1164   { SOLVER_REASON_SUPPLEMENTED,         "supplemented" },
1165   { 0, 0 }
1166 };
1167
1168 static const char *
1169 testcase_reason2str(Id reason)
1170 {
1171   int i;
1172   for (i = 0; reason2str[i].str; i++)
1173     if (reason == reason2str[i].reason)
1174       return reason2str[i].str;
1175   return "?";
1176 }
1177
1178 static struct rclass2str {
1179   Id rclass;
1180   const char *str;
1181 } rclass2str[] = {
1182   { SOLVER_RULE_PKG, "pkg" },
1183   { SOLVER_RULE_UPDATE, "update" },
1184   { SOLVER_RULE_FEATURE, "feature" },
1185   { SOLVER_RULE_JOB, "job" },
1186   { SOLVER_RULE_DISTUPGRADE, "distupgrade" },
1187   { SOLVER_RULE_INFARCH, "infarch" },
1188   { SOLVER_RULE_CHOICE, "choice" },
1189   { SOLVER_RULE_LEARNT, "learnt" },
1190   { SOLVER_RULE_BEST, "best" },
1191   { SOLVER_RULE_YUMOBS, "yumobs" },
1192   { SOLVER_RULE_RECOMMENDS, "recommends" },
1193   { 0, 0 }
1194 };
1195
1196 static const char *
1197 testcase_rclass2str(Id rclass)
1198 {
1199   int i;
1200   for (i = 0; rclass2str[i].str; i++)
1201     if (rclass == rclass2str[i].rclass)
1202       return rclass2str[i].str;
1203   return "unknown";
1204 }
1205
1206 static int
1207 dump_genid(Pool *pool, Strqueue *sq, Id id, int cnt)
1208 {
1209   struct oplist *op;
1210   char cntbuf[20];
1211   const char *s;
1212
1213   if (ISRELDEP(id))
1214     {
1215       Reldep *rd = GETRELDEP(pool, id);
1216       for (op = oplist; op->flags; op++)
1217         if (rd->flags == op->flags)
1218           break;
1219       cnt = dump_genid(pool, sq, rd->name, cnt);
1220       cnt = dump_genid(pool, sq, rd->evr, cnt);
1221       sprintf(cntbuf, "genid %2d: genid ", cnt++);
1222       s = pool_tmpjoin(pool, cntbuf, "op ", op->flags ? op->opname : "unknown");
1223     }
1224   else
1225     {
1226       sprintf(cntbuf, "genid %2d: genid ", cnt++);
1227       s = pool_tmpjoin(pool, cntbuf, id ? "lit " : "null", id ? pool_id2str(pool, id) : 0);
1228     }
1229   strqueue_push(sq, s);
1230   return cnt;
1231 }
1232
1233 char *
1234 testcase_solverresult(Solver *solv, int resultflags)
1235 {
1236   Pool *pool = solv->pool;
1237   int i, j;
1238   Id p, op;
1239   const char *s;
1240   char *result;
1241   Strqueue sq;
1242
1243   strqueue_init(&sq);
1244   if ((resultflags & TESTCASE_RESULT_TRANSACTION) != 0)
1245     {
1246       Transaction *trans = solver_create_transaction(solv);
1247       Queue q;
1248
1249       queue_init(&q);
1250       for (i = 0; class2str[i].str; i++)
1251         {
1252           queue_empty(&q);
1253           transaction_classify_pkgs(trans, SOLVER_TRANSACTION_KEEP_PSEUDO, class2str[i].class, 0, 0, &q);
1254           for (j = 0; j < q.count; j++)
1255             {
1256               p = q.elements[j];
1257               op = 0;
1258               if (pool->installed && pool->solvables[p].repo == pool->installed)
1259                 op = transaction_obs_pkg(trans, p);
1260               s = pool_tmpjoin(pool, class2str[i].str, " ", testcase_solvid2str(pool, p));
1261               if (op)
1262                 s = pool_tmpjoin(pool, s, " ", testcase_solvid2str(pool, op));
1263               strqueue_push(&sq, s);
1264             }
1265         }
1266       queue_free(&q);
1267       transaction_free(trans);
1268     }
1269   if ((resultflags & TESTCASE_RESULT_PROBLEMS) != 0)
1270     {
1271       char *probprefix, *solprefix;
1272       int problem, solution, element;
1273       int pcnt, scnt;
1274
1275       pcnt = solver_problem_count(solv);
1276       for (problem = 1; problem <= pcnt; problem++)
1277         {
1278           Id rid, from, to, dep;
1279           SolverRuleinfo rinfo;
1280           rid = solver_findproblemrule(solv, problem);
1281           s = testcase_problemid(solv, problem);
1282           probprefix = solv_dupjoin("problem ", s, 0);
1283           rinfo = solver_ruleinfo(solv, rid, &from, &to, &dep);
1284           s = pool_tmpjoin(pool, probprefix, " info ", solver_problemruleinfo2str(solv, rinfo, from, to, dep));
1285           strqueue_push(&sq, s);
1286           scnt = solver_solution_count(solv, problem);
1287           for (solution = 1; solution <= scnt; solution++)
1288             {
1289               s = testcase_solutionid(solv, problem, solution);
1290               solprefix = solv_dupjoin(probprefix, " solution ", s);
1291               element = 0;
1292               while ((element = solver_next_solutionelement(solv, problem, solution, element, &p, &op)) != 0)
1293                 {
1294                   if (p == SOLVER_SOLUTION_JOB)
1295                     s = pool_tmpjoin(pool, solprefix, " deljob ", testcase_job2str(pool, solv->job.elements[op - 1], solv->job.elements[op]));
1296                   else if (p > 0 && op == 0)
1297                     s = pool_tmpjoin(pool, solprefix, " erase ", testcase_solvid2str(pool, p));
1298                   else if (p > 0 && op > 0)
1299                     {
1300                       s = pool_tmpjoin(pool, solprefix, " replace ", testcase_solvid2str(pool, p));
1301                       s = pool_tmpappend(pool, s, " ", testcase_solvid2str(pool, op));
1302                     }
1303                   else if (p < 0 && op > 0)
1304                     s = pool_tmpjoin(pool, solprefix, " allow ", testcase_solvid2str(pool, op));
1305                   else
1306                     s = pool_tmpjoin(pool, solprefix, " unknown", 0);
1307                   strqueue_push(&sq, s);
1308                 }
1309               solv_free(solprefix);
1310             }
1311           solv_free(probprefix);
1312         }
1313     }
1314
1315   if ((resultflags & TESTCASE_RESULT_ORPHANED) != 0)
1316     {
1317       Queue q;
1318
1319       queue_init(&q);
1320       solver_get_orphaned(solv, &q);
1321       for (i = 0; i < q.count; i++)
1322         {
1323           s = pool_tmpjoin(pool, "orphaned ", testcase_solvid2str(pool, q.elements[i]), 0);
1324           strqueue_push(&sq, s);
1325         }
1326       queue_free(&q);
1327     }
1328
1329   if ((resultflags & TESTCASE_RESULT_RECOMMENDED) != 0)
1330     {
1331       Queue qr, qs;
1332
1333       queue_init(&qr);
1334       queue_init(&qs);
1335       solver_get_recommendations(solv, &qr, &qs, 0);
1336       for (i = 0; i < qr.count; i++)
1337         {
1338           s = pool_tmpjoin(pool, "recommended ", testcase_solvid2str(pool, qr.elements[i]), 0);
1339           strqueue_push(&sq, s);
1340         }
1341       for (i = 0; i < qs.count; i++)
1342         {
1343           s = pool_tmpjoin(pool, "suggested ", testcase_solvid2str(pool, qs.elements[i]), 0);
1344           strqueue_push(&sq, s);
1345         }
1346       queue_free(&qr);
1347       queue_free(&qs);
1348     }
1349
1350   if ((resultflags & TESTCASE_RESULT_UNNEEDED) != 0)
1351     {
1352       Queue q, qf;
1353
1354       queue_init(&q);
1355       queue_init(&qf);
1356       solver_get_unneeded(solv, &q, 0);
1357       solver_get_unneeded(solv, &qf, 1);
1358       for (i = j = 0; i < q.count; i++)
1359         {
1360           /* we rely on qf containing a subset of q in the same order */
1361           if (j < qf.count && q.elements[i] == qf.elements[j])
1362             {
1363               s = pool_tmpjoin(pool, "unneeded_filtered ", testcase_solvid2str(pool, q.elements[i]), 0);
1364               j++;
1365             }
1366           else
1367             s = pool_tmpjoin(pool, "unneeded ", testcase_solvid2str(pool, q.elements[i]), 0);
1368           strqueue_push(&sq, s);
1369         }
1370       queue_free(&q);
1371       queue_free(&qf);
1372     }
1373   if ((resultflags & TESTCASE_RESULT_USERINSTALLED) != 0)
1374     {
1375       Queue q;
1376       solver_get_userinstalled(solv, &q, 0);
1377       for (i = 0; i < q.count; i++)
1378         {
1379           s = pool_tmpjoin(pool, "userinstalled pkg ", testcase_solvid2str(pool, q.elements[i]), 0);
1380           strqueue_push(&sq, s);
1381         }
1382       queue_empty(&q);
1383       solver_get_userinstalled(solv, &q, GET_USERINSTALLED_NAMES | GET_USERINSTALLED_INVERTED);
1384       for (i = 0; i < q.count; i++)
1385         {
1386           s = pool_tmpjoin(pool, "autoinst name ", pool_id2str(pool, q.elements[i]), 0);
1387           strqueue_push(&sq, s);
1388         }
1389       queue_free(&q);
1390     }
1391   if ((resultflags & TESTCASE_RESULT_ALTERNATIVES) != 0)
1392     {
1393       char *altprefix;
1394       Queue q, rq;
1395       int cnt;
1396       Id alternative;
1397       queue_init(&q);
1398       queue_init(&rq);
1399       cnt = solver_alternatives_count(solv);
1400       for (alternative = 1; alternative <= cnt; alternative++)
1401         {
1402           Id id, from, chosen;
1403           char num[20];
1404           int type = solver_get_alternative(solv, alternative, &id, &from, &chosen, &q, 0);
1405           altprefix = solv_dupjoin("alternative ", testcase_alternativeid(solv, type, id, from), " ");
1406           strcpy(num, " 0 ");
1407           if (type == SOLVER_ALTERNATIVE_TYPE_RECOMMENDS)
1408             {
1409               char *s = pool_tmpjoin(pool, altprefix, num, testcase_solvid2str(pool, from));
1410               s = pool_tmpappend(pool, s, " recommends ", testcase_dep2str(pool, id));
1411               strqueue_push(&sq, s);
1412             }
1413           else if (type == SOLVER_ALTERNATIVE_TYPE_RULE)
1414             {
1415               /* map choice rules back to pkg rules */
1416               if (solver_ruleclass(solv, id) == SOLVER_RULE_CHOICE)
1417                 id = solver_rule2pkgrule(solv, id);
1418               if (solver_ruleclass(solv, id) == SOLVER_RULE_RECOMMENDS)
1419                 id = solver_rule2pkgrule(solv, id);
1420               solver_allruleinfos(solv, id, &rq);
1421               for (i = 0; i < rq.count; i += 4)
1422                 {
1423                   int rtype = rq.elements[i];
1424                   if ((rtype & SOLVER_RULE_TYPEMASK) == SOLVER_RULE_JOB)
1425                     {
1426                       const char *js = testcase_job2str(pool, rq.elements[i + 2], rq.elements[i + 3]);
1427                       char *s = pool_tmpjoin(pool, altprefix, num, "job ");
1428                       s = pool_tmpappend(pool, s, js, 0);
1429                       strqueue_push(&sq, s);
1430                     }
1431                   else if (rtype == SOLVER_RULE_PKG_REQUIRES)
1432                     {
1433                       char *s = pool_tmpjoin(pool, altprefix, num, testcase_solvid2str(pool, rq.elements[i + 1]));
1434                       s = pool_tmpappend(pool, s, " requires ", testcase_dep2str(pool, rq.elements[i + 3]));
1435                       strqueue_push(&sq, s);
1436                     }
1437                   else if (rtype == SOLVER_RULE_UPDATE || rtype == SOLVER_RULE_FEATURE)
1438                     {
1439                       const char *js = testcase_solvid2str(pool, rq.elements[i + 1]);
1440                       char *s = pool_tmpjoin(pool, altprefix, num, "update ");
1441                       s = pool_tmpappend(pool, s, js, 0);
1442                       strqueue_push(&sq, s);
1443                     }
1444                 }
1445             }
1446           for (i = 0; i < q.count; i++)
1447             {
1448               Id p = q.elements[i];
1449               if (i >= 9)
1450                 num[0] = '0' + (i + 1) / 10;
1451               num[1] = '0' + (i + 1) % 10;
1452               if (-p == chosen)
1453                 s = pool_tmpjoin(pool, altprefix, num, "+ ");
1454               else if (p < 0)
1455                 s = pool_tmpjoin(pool, altprefix, num, "- ");
1456               else if (p >= 0)
1457                 s = pool_tmpjoin(pool, altprefix, num, "  ");
1458               s = pool_tmpappend(pool, s,  testcase_solvid2str(pool, p < 0 ? -p : p), 0);
1459               strqueue_push(&sq, s);
1460             }
1461           solv_free(altprefix);
1462         }
1463       queue_free(&q);
1464       queue_free(&rq);
1465     }
1466   if ((resultflags & TESTCASE_RESULT_RULES) != 0)
1467     {
1468       /* dump all rules */
1469       Id rid;
1470       SolverRuleinfo rclass;
1471       Queue q;
1472       int i;
1473
1474       queue_init(&q);
1475       for (rid = 1; (rclass = solver_ruleclass(solv, rid)) != SOLVER_RULE_UNKNOWN; rid++)
1476         {
1477           char *prefix = solv_dupjoin("rule ", testcase_rclass2str(rclass), " ");
1478           prefix = solv_dupappend(prefix, testcase_ruleid(solv, rid), 0);
1479           solver_ruleliterals(solv, rid, &q);
1480           if (rclass == SOLVER_RULE_FEATURE && q.count == 1 && q.elements[0] == -SYSTEMSOLVABLE)
1481             continue;
1482           for (i = 0; i < q.count; i++)
1483             {
1484               Id p = q.elements[i];
1485               const char *s;
1486               if (p < 0)
1487                 s = pool_tmpjoin(pool, prefix, " -", testcase_solvid2str(pool, -p));
1488               else
1489                 s = pool_tmpjoin(pool, prefix, "  ", testcase_solvid2str(pool, p));
1490               strqueue_push(&sq, s);
1491             }
1492           solv_free(prefix);
1493         }
1494       queue_free(&q);
1495     }
1496   if ((resultflags & TESTCASE_RESULT_GENID) != 0)
1497     {
1498       for (i = 0 ; i < solv->job.count; i += 2)
1499         {
1500           Id id, id2;
1501           if (solv->job.elements[i] != (SOLVER_NOOP | SOLVER_SOLVABLE_PROVIDES))
1502             continue;
1503           id = solv->job.elements[i + 1];
1504           s = testcase_dep2str(pool, id);
1505           strqueue_push(&sq, pool_tmpjoin(pool, "genid dep ", s, 0));
1506           if ((id2 = testcase_str2dep(pool, s)) != id)
1507             {
1508               s = pool_tmpjoin(pool, "genid roundtrip error: ", testcase_dep2str(pool, id2), 0);
1509               strqueue_push(&sq, s);
1510             }
1511           dump_genid(pool, &sq, id, 1);
1512         }
1513     }
1514   if ((resultflags & TESTCASE_RESULT_REASON) != 0)
1515     {
1516       Queue whyq;
1517       queue_init(&whyq);
1518       FOR_POOL_SOLVABLES(p)
1519         {
1520           Id info, p2, id;
1521           int reason = solver_describe_decision(solv, p, &info);
1522           if (reason == SOLVER_REASON_UNRELATED)
1523             continue;
1524           if (reason == SOLVER_REASON_WEAKDEP)
1525             {
1526               solver_describe_weakdep_decision(solv, p, &whyq);
1527               if (whyq.count)
1528                 {
1529                   for (i = 0; i < whyq.count; i += 3)
1530                     {
1531                       reason = whyq.elements[i];
1532                       p2 = whyq.elements[i + 1];
1533                       id = whyq.elements[i + 2];
1534                       s = pool_tmpjoin(pool, "reason ", testcase_solvid2str(pool, p), 0);
1535                       s = pool_tmpappend(pool, s, " ", testcase_reason2str(reason));
1536                       s = pool_tmpappend(pool, s, " ", testcase_dep2str(pool, id));
1537                       if (p2)
1538                         s = pool_tmpappend(pool, s, " ", testcase_solvid2str(pool, p2));
1539                       strqueue_push(&sq, s);
1540                     }
1541                   continue;
1542                 }
1543             }
1544           s = pool_tmpjoin(pool, "reason ", testcase_solvid2str(pool, p), 0);
1545           s = pool_tmpappend(pool, s, " ", testcase_reason2str(reason));
1546           if (info)
1547             s = pool_tmpappend(pool, s, " ", testcase_ruleid(solv, info));
1548           strqueue_push(&sq, s);
1549         }
1550       queue_free(&whyq);
1551     }
1552   if ((resultflags & TESTCASE_RESULT_CLEANDEPS) != 0)
1553     {
1554       Queue q;
1555
1556       queue_init(&q);
1557       solver_get_cleandeps(solv, &q);
1558       for (i = 0; i < q.count; i++)
1559         {
1560           s = pool_tmpjoin(pool, "cleandeps ", testcase_solvid2str(pool, q.elements[i]), 0);
1561           strqueue_push(&sq, s);
1562         }
1563       queue_free(&q);
1564     }
1565   if ((resultflags & TESTCASE_RESULT_JOBS) != 0)
1566     {
1567       for (i = 0; i < solv->job.count; i += 2)
1568         {
1569           s = (char *)testcase_job2str(pool, solv->job.elements[i], solv->job.elements[i + 1]);
1570           s = pool_tmpjoin(pool, "job ", s, 0);
1571           strqueue_push(&sq, s);
1572         }
1573     }
1574   strqueue_sort(&sq);
1575   result = strqueue_join(&sq);
1576   strqueue_free(&sq);
1577   return result;
1578 }
1579
1580
1581 static int
1582 testcase_write_mangled(Solver *solv, const char *dir, int resultflags, const char *testcasename, const char *resultname)
1583 {
1584   Pool *pool = solv->pool;
1585   Repo *repo;
1586   int i;
1587   Id arch, repoid;
1588   Id lowscore;
1589   FILE *fp;
1590   Strqueue sq;
1591   char *cmd, *out, *result;
1592   const char *s;
1593
1594   if (!testcasename)
1595     testcasename = "testcase.t";
1596   if (!resultname)
1597     resultname = "solver.result";
1598
1599 #ifdef _WIN32
1600   if (mkdir(dir) && errno != EEXIST)
1601 #else
1602   if (mkdir(dir, 0777) && errno != EEXIST)
1603 #endif
1604     return pool_error(solv->pool, 0, "testcase_write: could not create directory '%s'", dir);
1605   strqueue_init(&sq);
1606   FOR_REPOS(repoid, repo)
1607     {
1608       const char *name = testcase_repoid2str(pool, repoid);
1609       char priobuf[50];
1610       if (repo->subpriority)
1611         sprintf(priobuf, "%d.%d", repo->priority, repo->subpriority);
1612       else
1613         sprintf(priobuf, "%d", repo->priority);
1614 #if !defined(WITHOUT_COOKIEOPEN) && defined(ENABLE_ZLIB_COMPRESSION)
1615       out = pool_tmpjoin(pool, name, ".repo", ".gz");
1616 #else
1617       out = pool_tmpjoin(pool, name, ".repo", 0);
1618 #endif
1619       for (i = 0; out[i]; i++)
1620         if (out[i] == '/')
1621           out[i] = '_';
1622       cmd = pool_tmpjoin(pool, "repo ", name, " ");
1623       cmd = pool_tmpappend(pool, cmd, priobuf, " ");
1624       cmd = pool_tmpappend(pool, cmd, "testtags ", out);
1625       strqueue_push(&sq, cmd);
1626       out = pool_tmpjoin(pool, dir, "/", out);
1627       if (!(fp = solv_xfopen(out, "w")))
1628         {
1629           pool_error(solv->pool, 0, "testcase_write: could not open '%s' for writing", out);
1630           strqueue_free(&sq);
1631           return 0;
1632         }
1633       testcase_write_testtags(repo, fp);
1634       if (fclose(fp))
1635         {
1636           pool_error(solv->pool, 0, "testcase_write: write error");
1637           strqueue_free(&sq);
1638           return 0;
1639         }
1640     }
1641   /* hmm, this is not optimal... we currently search for the lowest score */
1642   lowscore = 0;
1643   arch = pool->solvables[SYSTEMSOLVABLE].arch;
1644   for (i = 0; i < pool->lastarch; i++)
1645     {
1646       if (pool->id2arch[i] == 1 && !lowscore)
1647         arch = i;
1648       if (pool->id2arch[i] > 0x10000 && (!lowscore || pool->id2arch[i] < lowscore))
1649         {
1650           arch = i;
1651           lowscore = pool->id2arch[i];
1652         }
1653     }
1654   cmd = pool_tmpjoin(pool, "system ", pool->lastarch ? pool_id2str(pool, arch) : "-", 0);
1655   for (i = 0; disttype2str[i].str != 0; i++)
1656     if (pool->disttype == disttype2str[i].type)
1657       break;
1658   pool_tmpappend(pool, cmd, " ", disttype2str[i].str ? disttype2str[i].str : "unknown");
1659   if (pool->installed)
1660     cmd = pool_tmpappend(pool, cmd, " ", testcase_repoid2str(pool, pool->installed->repoid));
1661   strqueue_push(&sq, cmd);
1662   s = testcase_getpoolflags(solv->pool);
1663   if (*s)
1664     {
1665       cmd = pool_tmpjoin(pool, "poolflags ", s, 0);
1666       strqueue_push(&sq, cmd);
1667     }
1668
1669   if (pool->vendorclasses)
1670     {
1671       cmd = 0;
1672       for (i = 0; pool->vendorclasses[i]; i++)
1673         {
1674           cmd = pool_tmpappend(pool, cmd ? cmd : "vendorclass", " ", pool->vendorclasses[i]);
1675           if (!pool->vendorclasses[i + 1])
1676             {
1677               strqueue_push(&sq, cmd);
1678               cmd = 0;
1679               i++;
1680             }
1681         }
1682     }
1683
1684   /* dump disabled packages (must come before the namespace/job lines) */
1685   if (pool->considered)
1686     {
1687       Id p;
1688       FOR_POOL_SOLVABLES(p)
1689         if (!MAPTST(pool->considered, p))
1690           {
1691             cmd = pool_tmpjoin(pool, "disable pkg ", testcase_solvid2str(pool, p), 0);
1692             strqueue_push(&sq, cmd);
1693           }
1694     }
1695
1696   s = testcase_getsolverflags(solv);
1697   if (*s)
1698     {
1699       cmd = pool_tmpjoin(pool, "solverflags ", s, 0);
1700       strqueue_push(&sq, cmd);
1701     }
1702
1703   /* now dump all the ns callback values we know */
1704   if (pool->nscallback)
1705     {
1706       Id rid;
1707       int d;
1708       for (rid = 1; rid < pool->nrels; rid++)
1709         {
1710           Reldep *rd = pool->rels + rid;
1711           if (rd->flags != REL_NAMESPACE || rd->name == NAMESPACE_OTHERPROVIDERS)
1712             continue;
1713           /* evaluate all namespace ids, skip empty results */
1714           d = pool_whatprovides(pool, MAKERELDEP(rid));
1715           if (!d || !pool->whatprovidesdata[d])
1716             continue;
1717           cmd = pool_tmpjoin(pool, "namespace ", pool_id2str(pool, rd->name), "(");
1718           cmd = pool_tmpappend(pool, cmd, pool_id2str(pool, rd->evr), ")");
1719           for (;  pool->whatprovidesdata[d]; d++)
1720             cmd = pool_tmpappend(pool, cmd, " ", testcase_solvid2str(pool, pool->whatprovidesdata[d]));
1721           strqueue_push(&sq, cmd);
1722         }
1723     }
1724
1725   for (i = 0; i < solv->job.count; i += 2)
1726     {
1727       cmd = (char *)testcase_job2str(pool, solv->job.elements[i], solv->job.elements[i + 1]);
1728       cmd = pool_tmpjoin(pool, "job ", cmd, 0);
1729       strqueue_push(&sq, cmd);
1730     }
1731
1732   if ((resultflags & ~TESTCASE_RESULT_REUSE_SOLVER) != 0)
1733     {
1734       cmd = 0;
1735       for (i = 0; resultflags2str[i].str; i++)
1736         if ((resultflags & resultflags2str[i].flag) != 0)
1737           cmd = pool_tmpappend(pool, cmd, cmd ? "," : 0, resultflags2str[i].str);
1738       cmd = pool_tmpjoin(pool, "result ", cmd ? cmd : "?", 0);
1739       cmd = pool_tmpappend(pool, cmd, " ", resultname);
1740       strqueue_push(&sq, cmd);
1741       result = testcase_solverresult(solv, resultflags);
1742       if (!strcmp(resultname, "<inline>"))
1743         {
1744           Strqueue rsq;
1745           strqueue_init(&rsq);
1746           strqueue_split(&rsq, result);
1747           for (i = 0; i < rsq.nstr; i++)
1748             {
1749               cmd = pool_tmpjoin(pool, "#>", rsq.str[i], 0);
1750               strqueue_push(&sq, cmd);
1751             }
1752           strqueue_free(&rsq);
1753         }
1754       else
1755         {
1756           out = pool_tmpjoin(pool, dir, "/", resultname);
1757           if (!(fp = fopen(out, "w")))
1758             {
1759               pool_error(solv->pool, 0, "testcase_write: could not open '%s' for writing", out);
1760               solv_free(result);
1761               strqueue_free(&sq);
1762               return 0;
1763             }
1764           if (result && *result && fwrite(result, strlen(result), 1, fp) != 1)
1765             {
1766               pool_error(solv->pool, 0, "testcase_write: write error");
1767               solv_free(result);
1768               strqueue_free(&sq);
1769               fclose(fp);
1770               return 0;
1771             }
1772           if (fclose(fp))
1773             {
1774               pool_error(solv->pool, 0, "testcase_write: write error");
1775               solv_free(result);
1776               strqueue_free(&sq);
1777               return 0;
1778             }
1779         }
1780       solv_free(result);
1781     }
1782
1783   result = strqueue_join(&sq);
1784   strqueue_free(&sq);
1785   out = pool_tmpjoin(pool, dir, "/", testcasename);
1786   if (!(fp = fopen(out, "w")))
1787     {
1788       pool_error(solv->pool, 0, "testcase_write: could not open '%s' for writing", out);
1789       solv_free(result);
1790       return 0;
1791     }
1792   if (*result && fwrite(result, strlen(result), 1, fp) != 1)
1793     {
1794       pool_error(solv->pool, 0, "testcase_write: write error");
1795       solv_free(result);
1796       fclose(fp);
1797       return 0;
1798     }
1799   if (fclose(fp))
1800     {
1801       pool_error(solv->pool, 0, "testcase_write: write error");
1802       solv_free(result);
1803       return 0;
1804     }
1805   solv_free(result);
1806   return 1;
1807 }
1808
1809 int
1810 testcase_write(Solver *solv, const char *dir, int resultflags, const char *testcasename, const char *resultname)
1811 {
1812   Pool *pool = solv->pool;
1813   int i, r, repoid;
1814   int mangle = 1;
1815   const char **orignames;
1816
1817   /* mangle repo names so that there are no conflicts */
1818   orignames = solv_calloc(pool->nrepos, sizeof(char *));
1819   for (repoid = 1; repoid < pool->nrepos; repoid++)
1820     {
1821       Repo *repo = pool_id2repo(pool, repoid);
1822       char *buf = solv_malloc((repo->name ? strlen(repo->name) : 0) + 40);
1823       char *mp;
1824       orignames[repoid] = repo->name;
1825       if (!repo->name || !repo->name[0])
1826         sprintf(buf, "#%d", repoid);
1827       else
1828         strcpy(buf, repo->name);
1829       for (i = 0; buf[i]; i++)
1830         if (buf[i] == ' ' || buf[i] == '\t' || buf[i] == '/')
1831           buf[i] = '_';
1832       mp = buf + strlen(buf);
1833       for (;;)
1834         {
1835           for (i = 1; i < repoid; i++)
1836             if (!strcmp(buf, pool_id2repo(pool, i)->name))
1837               break;
1838           if (i == repoid)
1839             break;
1840           sprintf(mp, "_%d", mangle++);
1841         }
1842       repo->name = buf;
1843     }
1844   r = testcase_write_mangled(solv, dir, resultflags, testcasename, resultname);
1845   for (repoid = 1; repoid < pool->nrepos; repoid++)
1846     {
1847       Repo *repo = pool_id2repo(pool, repoid);
1848       solv_free((void *)repo->name);
1849       repo->name = orignames[repoid];
1850     }
1851   solv_free(orignames);
1852   return r;
1853 }
1854
1855 static char *
1856 read_inline_file(FILE *fp, char **bufp, char **bufpp, int *buflp)
1857 {
1858   char *result = solv_malloc(1024);
1859   char *rp = result;
1860   int resultl = 1024;
1861
1862   for (;;)
1863     {
1864       size_t rl;
1865       if (rp - result + 256 >= resultl)
1866         {
1867           resultl = rp - result;
1868           result = solv_realloc(result, resultl + 1024);
1869           rp = result + resultl;
1870           resultl += 1024;
1871         }
1872       if (!fgets(rp, resultl - (rp - result), fp))
1873         *rp = 0;
1874       rl = strlen(rp);
1875       if (rl && (rp == result || rp[-1] == '\n'))
1876         {
1877           if (rl > 1 && rp[0] == '#' && rp[1] == '>')
1878             {
1879               memmove(rp, rp + 2, rl - 2);
1880               rl -= 2;
1881             }
1882           else
1883             {
1884               while (rl + 16 > *buflp)
1885                 {
1886                   *bufp = solv_realloc(*bufp, *buflp + 512);
1887                   *buflp += 512;
1888                 }
1889               memmove(*bufp, rp, rl);
1890               if ((*bufp)[rl - 1] == '\n')
1891                 {
1892                   ungetc('\n', fp);
1893                   rl--;
1894                 }
1895               (*bufp)[rl] = 0;
1896               (*bufpp) = *bufp + rl;
1897               rl = 0;
1898             }
1899         }
1900       if (rl <= 0)
1901         {
1902           *rp = 0;
1903           break;
1904         }
1905       rp += rl;
1906     }
1907   return result;
1908 }
1909
1910 static char *
1911 read_file(FILE *fp)
1912 {
1913   char *result = solv_malloc(1024);
1914   char *rp = result;
1915   int resultl = 1024;
1916
1917   for (;;)
1918     {
1919       size_t rl;
1920       if (rp - result + 256 >= resultl)
1921         {
1922           resultl = rp - result;
1923           result = solv_realloc(result, resultl + 1024);
1924           rp = result + resultl;
1925           resultl += 1024;
1926         }
1927       rl = fread(rp, 1, resultl - (rp - result), fp);
1928       if (rl <= 0)
1929         {
1930           *rp = 0;
1931           break;
1932         }
1933       rp += rl;
1934     }
1935   return result;
1936 }
1937
1938 static int
1939 str2resultflags(Pool *pool, char *s)    /* modifies the string! */
1940 {
1941   int i, resultflags = 0;
1942   while (s)
1943     {
1944       char *se = strchr(s, ',');
1945       if (se)
1946         *se++ = 0;
1947       for (i = 0; resultflags2str[i].str; i++)
1948         if (!strcmp(s, resultflags2str[i].str))
1949           {
1950             resultflags |= resultflags2str[i].flag;
1951             break;
1952           }
1953       if (!resultflags2str[i].str)
1954         pool_error(pool, 0, "result: unknown flag '%s'", s);
1955       s = se;
1956     }
1957   return resultflags;
1958 }
1959
1960 Solver *
1961 testcase_read(Pool *pool, FILE *fp, const char *testcase, Queue *job, char **resultp, int *resultflagsp)
1962 {
1963   Solver *solv;
1964   char *buf, *bufp;
1965   int bufl;
1966   char *testcasedir, *s;
1967   int l;
1968   char **pieces = 0;
1969   int npieces = 0;
1970   int prepared = 0;
1971   int closefp = !fp;
1972   int poolflagsreset = 0;
1973   int missing_features = 0;
1974   Id *genid = 0;
1975   int ngenid = 0;
1976   Queue autoinstq;
1977
1978   if (resultp)
1979     *resultp = 0;
1980   if (resultflagsp)
1981     *resultflagsp = 0;
1982   if (!fp && !(fp = fopen(testcase, "r")))
1983     {
1984       pool_error(pool, 0, "testcase_read: could not open '%s'", testcase);
1985       return 0;
1986     }
1987   testcasedir = solv_strdup(testcase);
1988   s = strrchr(testcasedir, '/');
1989 #ifdef _WIN32
1990   buf = strrchr(testcasedir, '\\');
1991   if (!s || (buf && buf > s))
1992     s = buf;
1993 #endif
1994   if (s)
1995     s[1] = 0;
1996   else
1997     *testcasedir = 0;
1998   bufl = 1024;
1999   buf = solv_malloc(bufl);
2000   bufp = buf;
2001   solv = 0;
2002   queue_init(&autoinstq);
2003   for (;;)
2004     {
2005       if (bufp - buf + 16 > bufl)
2006         {
2007           bufl = bufp - buf;
2008           buf = solv_realloc(buf, bufl + 512);
2009           bufp = buf + bufl;
2010           bufl += 512;
2011         }
2012       if (!fgets(bufp, bufl - (bufp - buf), fp))
2013         break;
2014       bufp = buf;
2015       l = strlen(buf);
2016       if (!l || buf[l - 1] != '\n')
2017         {
2018           bufp += l;
2019           continue;
2020         }
2021       buf[--l] = 0;
2022       s = buf;
2023       while (*s && (*s == ' ' || *s == '\t'))
2024         s++;
2025       if (!*s || *s == '#')
2026         continue;
2027       npieces = 0;
2028       /* split it in pieces */
2029       for (;;)
2030         {
2031           while (*s == ' ' || *s == '\t')
2032             s++;
2033           if (!*s)
2034             break;
2035           pieces = solv_extend(pieces, npieces, 1, sizeof(*pieces), 7);
2036           pieces[npieces++] = s;
2037           while (*s && *s != ' ' && *s != '\t')
2038             s++;
2039           if (*s)
2040             *s++ = 0;
2041         }
2042       pieces = solv_extend(pieces, npieces, 1, sizeof(*pieces), 7);
2043       pieces[npieces] = 0;
2044       if (!strcmp(pieces[0], "repo") && npieces >= 4)
2045         {
2046           Repo *repo = repo_create(pool, pieces[1]);
2047           FILE *rfp;
2048           int prio, subprio;
2049           const char *rdata;
2050
2051           prepared = 0;
2052           if (!poolflagsreset)
2053             {
2054               poolflagsreset = 1;
2055               testcase_resetpoolflags(pool);    /* hmm */
2056             }
2057           if (sscanf(pieces[2], "%d.%d", &prio, &subprio) != 2)
2058             {
2059               subprio = 0;
2060               prio = atoi(pieces[2]);
2061             }
2062           repo->priority = prio;
2063           repo->subpriority = subprio;
2064           if (strcmp(pieces[3], "empty") != 0)
2065             {
2066               const char *repotype = pool_tmpjoin(pool, pieces[3], 0, 0);       /* gets overwritten in <inline> case */
2067               if (!strcmp(pieces[4], "<inline>"))
2068                 {
2069                   char *idata = read_inline_file(fp, &buf, &bufp, &bufl);
2070                   rdata = "<inline>";
2071                   rfp = solv_xfopen_buf(rdata, &idata, 0, "rf");
2072                 }
2073               else
2074                 {
2075                   rdata = pool_tmpjoin(pool, testcasedir, pieces[4], 0);
2076                   rfp = solv_xfopen(rdata, "r");
2077                 }
2078               if (!rfp)
2079                 {
2080                   pool_error(pool, 0, "testcase_read: could not open '%s'", rdata);
2081                 }
2082               else if (!strcmp(repotype, "testtags"))
2083                 {
2084                   testcase_add_testtags(repo, rfp, 0);
2085                   fclose(rfp);
2086                 }
2087               else if (!strcmp(repotype, "solv"))
2088                 {
2089                   repo_add_solv(repo, rfp, 0);
2090                   fclose(rfp);
2091                 }
2092 #if ENABLE_TESTCASE_HELIXREPO
2093               else if (!strcmp(repotype, "helix"))
2094                 {
2095                   repo_add_helix(repo, rfp, 0);
2096                   fclose(rfp);
2097                 }
2098 #endif
2099               else
2100                 {
2101                   fclose(rfp);
2102                   pool_error(pool, 0, "testcase_read: unknown repo type for repo '%s'", repo->name);
2103                 }
2104             }
2105         }
2106       else if (!strcmp(pieces[0], "system") && npieces >= 3)
2107         {
2108           int i;
2109
2110           /* must set the disttype before the arch */
2111           prepared = 0;
2112           if (strcmp(pieces[2], "*") != 0)
2113             {
2114               char *dp = pieces[2];
2115               while (dp && *dp)
2116                 {
2117                   char *dpe = strchr(dp, ',');
2118                   if (dpe)
2119                     *dpe = 0;
2120                   for (i = 0; disttype2str[i].str != 0; i++)
2121                     if (!strcmp(disttype2str[i].str, dp))
2122                       break;
2123                   if (dpe)
2124                     *dpe++ = ',';
2125                   if (disttype2str[i].str)
2126                     {
2127 #ifdef MULTI_SEMANTICS
2128                       if (pool->disttype != disttype2str[i].type)
2129                         pool_setdisttype(pool, disttype2str[i].type);
2130 #endif
2131                       if (pool->disttype == disttype2str[i].type)
2132                         break;
2133                     }
2134                   dp = dpe;
2135                 }
2136               if (!(dp && *dp))
2137                 {
2138                   pool_error(pool, 0, "testcase_read: system: could not change disttype to '%s'", pieces[2]);
2139                   missing_features = 1;
2140                 }
2141             }
2142           if (strcmp(pieces[1], "unset") == 0 || strcmp(pieces[1], "-") == 0)
2143             pool_setarch(pool, 0);
2144           else if (pieces[1][0] == ':')
2145             pool_setarchpolicy(pool, pieces[1] + 1);
2146           else
2147             pool_setarch(pool, pieces[1]);
2148           if (npieces > 3)
2149             {
2150               Repo *repo = testcase_str2repo(pool, pieces[3]);
2151               if (!repo)
2152                 pool_error(pool, 0, "testcase_read: system: unknown repo '%s'", pieces[3]);
2153               else
2154                 pool_set_installed(pool, repo);
2155             }
2156         }
2157       else if (!strcmp(pieces[0], "job") && npieces > 1)
2158         {
2159           char *sp;
2160           Id how, what;
2161           if (prepared <= 0)
2162             {
2163               pool_addfileprovides(pool);
2164               pool_createwhatprovides(pool);
2165               prepared = 1;
2166             }
2167           if (npieces >= 3 && !strcmp(pieces[2], "selection"))
2168             {
2169               addselectionjob(pool, pieces + 1, npieces - 1, job, 0, 0);
2170               continue;
2171             }
2172           if (npieces >= 4 && !strcmp(pieces[2], "selection_matchdeps"))
2173             {
2174               pieces[2] = pieces[1];
2175               addselectionjob(pool, pieces + 2, npieces - 2, job, SELECTIONJOB_MATCHDEPS, pool_str2id(pool, pieces[3], 1));
2176               continue;
2177             }
2178           if (npieces >= 4 && !strcmp(pieces[2], "selection_matchdepid"))
2179             {
2180               pieces[2] = pieces[1];
2181               addselectionjob(pool, pieces + 2, npieces - 2, job, SELECTIONJOB_MATCHDEPID, pool_str2id(pool, pieces[3], 1));
2182               continue;
2183             }
2184           if (npieces >= 4 && !strcmp(pieces[2], "selection_matchsolvable"))
2185             {
2186               pieces[2] = pieces[1];
2187               addselectionjob(pool, pieces + 2, npieces - 2, job, SELECTIONJOB_MATCHSOLVABLE, pool_str2id(pool, pieces[3], 1));
2188               continue;
2189             }
2190           /* rejoin */
2191           for (sp = pieces[1]; sp < pieces[npieces - 1]; sp++)
2192             if (*sp == 0)
2193               *sp = ' ';
2194           how = testcase_str2job(pool, pieces[1], &what);
2195           if (how >= 0 && job)
2196             queue_push2(job, how, what);
2197         }
2198       else if (!strcmp(pieces[0], "vendorclass") && npieces > 1)
2199         {
2200           pool_addvendorclass(pool, (const char **)(pieces + 1));
2201         }
2202       else if (!strcmp(pieces[0], "namespace") && npieces > 1)
2203         {
2204           int i = strlen(pieces[1]);
2205           s = strchr(pieces[1], '(');
2206           if (!s || pieces[1][i - 1] != ')')
2207             {
2208               pool_error(pool, 0, "testcase_read: bad namespace '%s'", pieces[1]);
2209             }
2210           else
2211             {
2212               Id name, evr, id;
2213               Queue q;
2214               queue_init(&q);
2215               *s = 0;
2216               pieces[1][i - 1] = 0;
2217               name = pool_str2id(pool, pieces[1], 1);
2218               evr = pool_str2id(pool, s + 1, 1);
2219               *s = '(';
2220               pieces[1][i - 1] = ')';
2221               id = pool_rel2id(pool, name, evr, REL_NAMESPACE, 1);
2222               for (i = 2; i < npieces; i++)
2223                 queue_push(&q, testcase_str2solvid(pool, pieces[i]));
2224               /* now do the callback */
2225               if (prepared <= 0)
2226                 {
2227                   pool_addfileprovides(pool);
2228                   pool_createwhatprovides(pool);
2229                   prepared = 1;
2230                 }
2231               pool->whatprovides_rel[GETRELID(id)] = pool_queuetowhatprovides(pool, &q);
2232               queue_free(&q);
2233             }
2234         }
2235       else if (!strcmp(pieces[0], "poolflags"))
2236         {
2237           int i;
2238           if (!poolflagsreset)
2239             {
2240               poolflagsreset = 1;
2241               testcase_resetpoolflags(pool);    /* hmm */
2242             }
2243           for (i = 1; i < npieces; i++)
2244             testcase_setpoolflags(pool, pieces[i]);
2245         }
2246       else if (!strcmp(pieces[0], "solverflags") && npieces > 1)
2247         {
2248           int i;
2249           if (!solv)
2250             {
2251               solv = solver_create(pool);
2252               testcase_resetsolverflags(solv);
2253             }
2254           for (i = 1; i < npieces; i++)
2255             testcase_setsolverflags(solv, pieces[i]);
2256         }
2257       else if (!strcmp(pieces[0], "result") && npieces > 1)
2258         {
2259           char *result = 0;
2260           int resultflags = str2resultflags(pool, pieces[1]);
2261           const char *rdata;
2262           if (npieces > 2)
2263             {
2264               rdata = pool_tmpjoin(pool, testcasedir, pieces[2], 0);
2265               if (!strcmp(pieces[2], "<inline>"))
2266                 result = read_inline_file(fp, &buf, &bufp, &bufl);
2267               else
2268                 {
2269                   FILE *rfp = fopen(rdata, "r");
2270                   if (!rfp)
2271                     pool_error(pool, 0, "testcase_read: could not open '%s'", rdata);
2272                   else
2273                     {
2274                       result = read_file(rfp);
2275                       fclose(rfp);
2276                     }
2277                 }
2278             }
2279           if (resultp)
2280             *resultp = result;
2281           else
2282             solv_free(result);
2283           if (resultflagsp)
2284             *resultflagsp = resultflags;
2285         }
2286       else if (!strcmp(pieces[0], "nextjob"))
2287         {
2288           if (npieces == 2 && resultflagsp && !strcmp(pieces[1], "reusesolver"))
2289             *resultflagsp |= TESTCASE_RESULT_REUSE_SOLVER;
2290           break;
2291         }
2292       else if (!strcmp(pieces[0], "disable") && npieces == 3)
2293         {
2294           Id p, pp, jobsel, what = 0;
2295           if (!prepared)
2296             pool_createwhatprovides(pool);
2297           prepared = -1;
2298           if (!pool->considered)
2299             {
2300               pool->considered = solv_calloc(1, sizeof(Map));
2301               map_init(pool->considered, pool->nsolvables);
2302               map_setall(pool->considered);
2303             }
2304           jobsel = testcase_str2jobsel(pool, "disable", pieces + 1, npieces - 1, &what);
2305           if (jobsel < 0)
2306             continue;
2307           if (jobsel == SOLVER_SOLVABLE_ALL)
2308             map_empty(pool->considered);
2309           else if (jobsel == SOLVER_SOLVABLE_REPO)
2310             {
2311               Repo *repo = pool_id2repo(pool, what);
2312               Solvable *s;
2313               FOR_REPO_SOLVABLES(repo, p, s)
2314                 MAPCLR(pool->considered, p);
2315             }
2316           FOR_JOB_SELECT(p, pp, jobsel, what)
2317             MAPCLR(pool->considered, p);
2318         }
2319       else if (!strcmp(pieces[0], "feature"))
2320         {
2321           int i, j;
2322           for (i = 1; i < npieces; i++)
2323             {
2324               for (j = 0; features[j]; j++)
2325                 if (!strcmp(pieces[i], features[j]))
2326                   break;
2327               if (!features[j])
2328                 {
2329                   pool_error(pool, 0, "testcase_read: missing feature '%s'", pieces[i]);
2330                   missing_features++;
2331                 }
2332             }
2333           if (missing_features)
2334             break;
2335         }
2336       else if (!strcmp(pieces[0], "genid") && npieces > 1)
2337         {
2338           Id id;
2339           /* rejoin */
2340           if (npieces > 2)
2341             {
2342               char *sp;
2343               for (sp = pieces[2]; sp < pieces[npieces - 1]; sp++)
2344                 if (*sp == 0)
2345                   *sp = ' ';
2346             }
2347           genid = solv_extend(genid, ngenid, 1, sizeof(*genid), 7);
2348           if (!strcmp(pieces[1], "op") && npieces > 2)
2349             {
2350               struct oplist *op;
2351               for (op = oplist; op->flags; op++)
2352                 if (!strncmp(pieces[2], op->opname, strlen(op->opname)))
2353                   break;
2354               if (!op->flags)
2355                 {
2356                   pool_error(pool, 0, "testcase_read: genid: unknown op '%s'", pieces[2]);
2357                   break;
2358                 }
2359               if (ngenid < 2)
2360                 {
2361                   pool_error(pool, 0, "testcase_read: genid: out of stack");
2362                   break;
2363                 }
2364               ngenid -= 2;
2365               id = pool_rel2id(pool, genid[ngenid] , genid[ngenid + 1], op->flags, 1);
2366             }
2367           else if (!strcmp(pieces[1], "lit"))
2368             id = pool_str2id(pool, npieces > 2 ? pieces[2] : "", 1);
2369           else if (!strcmp(pieces[1], "null"))
2370             id = 0;
2371           else if (!strcmp(pieces[1], "dep"))
2372             id = testcase_str2dep(pool, pieces[2]);
2373           else
2374             {
2375               pool_error(pool, 0, "testcase_read: genid: unknown command '%s'", pieces[1]);
2376               break;
2377             }
2378           genid[ngenid++] = id;
2379         }
2380       else if (!strcmp(pieces[0], "autoinst") && npieces > 2)
2381         {
2382           if (strcmp(pieces[1], "name"))
2383             {
2384               pool_error(pool, 0, "testcase_read: autoinst: illegal mode");
2385               break;
2386             }
2387           queue_push(&autoinstq, pool_str2id(pool, pieces[2], 1));
2388         }
2389       else if (!strcmp(pieces[0], "evrcmp") && npieces == 3)
2390         {
2391           Id evr1 = pool_str2id(pool, pieces[1], 1);
2392           Id evr2 = pool_str2id(pool, pieces[2], 1);
2393           int r = pool_evrcmp(pool, evr1, evr2, EVRCMP_COMPARE);
2394           r = r < 0 ? REL_LT : r > 0 ? REL_GT : REL_EQ;
2395           queue_push2(job, SOLVER_NOOP | SOLVER_SOLVABLE_PROVIDES, pool_rel2id(pool, evr1, evr2, r, 1));
2396         }
2397       else
2398         {
2399           pool_error(pool, 0, "testcase_read: cannot parse command '%s'", pieces[0]);
2400         }
2401     }
2402   while (job && ngenid > 0)
2403     queue_push2(job, SOLVER_NOOP | SOLVER_SOLVABLE_PROVIDES, genid[--ngenid]);
2404   if (autoinstq.count)
2405     pool_add_userinstalled_jobs(pool, &autoinstq, job, GET_USERINSTALLED_NAMES | GET_USERINSTALLED_INVERTED);
2406   queue_free(&autoinstq);
2407   genid = solv_free(genid);
2408   buf = solv_free(buf);
2409   pieces = solv_free(pieces);
2410   solv_free(testcasedir);
2411   if (!prepared)
2412     {
2413       pool_addfileprovides(pool);
2414       pool_createwhatprovides(pool);
2415     }
2416   if (!solv)
2417     {
2418       solv = solver_create(pool);
2419       testcase_resetsolverflags(solv);
2420     }
2421   if (closefp)
2422     fclose(fp);
2423   if (missing_features)
2424     {
2425       solver_free(solv);
2426       solv = 0;
2427       if (resultflagsp)
2428         *resultflagsp = 77;     /* hack for testsolv */
2429     }
2430   return solv;
2431 }
2432
2433 char *
2434 testcase_resultdiff(const char *result1, const char *result2)
2435 {
2436   Strqueue sq1, sq2, osq;
2437   char *r;
2438   strqueue_init(&sq1);
2439   strqueue_init(&sq2);
2440   strqueue_init(&osq);
2441   strqueue_split(&sq1, result1);
2442   strqueue_split(&sq2, result2);
2443   strqueue_sort(&sq1);
2444   strqueue_sort(&sq2);
2445   strqueue_diff(&sq1, &sq2, &osq);
2446   r = osq.nstr ? strqueue_join(&osq) : 0;
2447   strqueue_free(&sq1);
2448   strqueue_free(&sq2);
2449   strqueue_free(&osq);
2450   return r;
2451 }
2452