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