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