tests/plover-gtk/test-transactionhelper.c
author J. Ali Harlow <ali@juiblex.co.uk>
Sat Jul 16 11:07:18 2016 +0100 (2016-07-16)
changeset 61 31fb35727621
parent 42 419a02fa70db
permissions -rw-r--r--
Support parallel installations. The idea is that for CAD screener, we want
to be able to install this on the same machine as a standard AVOT setup
(most notably for John's laptop). To allow for the possibility of a second
application that might have the same requirements, we add the concept of
vendor-specific distributions. Thus we can have one distribution for CAD
screener and one for The Next Big Thing. It doesn't seem trivial to have
both CAD screener and AVOT under the same vendor tag so we'll have to have
AVOT under "City Occupational" and CAD screener under "City Occupational Ltd"
or some such kludge.

Most of this is done although we are very short of test cases (in particular
we don't test that it's actually possible to install CAD screener in parallel
with AVOT or to update either of them once installed, which is fundamental).

We also have a lot of baggage left over, including an intercept of razor_set.
The problem that this was introduced to debug has been fixed but it looks
like there are a number of memory leaks which it might be useful to help
track down so it has been left in place for now.

There is still a lot of confusion in plover between path-based and URI-based
API. We should review the API, decide what we want and have a general clear up.

There is also confusion as to the purpose of RAZOR_ROOT (and meaning; path or
URI). This is not used at all in librazor (although it is used in razor.exe).
Ideally we shouldn't use it in plover or plover-gtk either although again, we
might want to support it or an equivalent in (some of) the various executables.

Work that would still to nice to do for CAD screener:

- uninstall (ideally as an installed program that hooks into Add/Remove programs
but even re-running the installer would be acceptable).
- xz support (smaller packages).
- repomd.xml and xml:base (would be needed for an Internet installer).
- graphical installer.
     1 /*
     2  * Copyright (C) 2016  J. Ali Harlow <ali@juiblex.co.uk>
     3  *
     4  * This program is free software; you can redistribute it and/or modify
     5  * it under the terms of the GNU General Public License as published by
     6  * the Free Software Foundation; either version 2 of the License, or
     7  * (at your option) any later version.
     8  *
     9  * This program is distributed in the hope that it will be useful,
    10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    12  * GNU General Public License for more details.
    13  *
    14  * You should have received a copy of the GNU General Public License along
    15  * with this program; if not, write to the Free Software Foundation, Inc.,
    16  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
    17  */
    18 
    19 #include <stdlib.h>
    20 #include <string.h>
    21 #include <locale.h>
    22 #include <sys/stat.h>
    23 #include <glib.h>
    24 #include <gdk/gdk.h>
    25 #include <gtk/gtk.h>
    26 #include <plover/plover.h>
    27 #include <plover-gtk/transactionhelper.h>
    28 
    29 GtkBuilder *ui;
    30 gboolean manual_mode=FALSE;
    31 
    32 #if 0
    33 static void complete_changed(GtkWidget *page,GParamSpec *child_property,
    34   PloverTransactionHelper *helper)
    35 {
    36     g_message("complete child property for %s now %s",
    37       gtk_buildable_get_name(GTK_BUILDABLE(page)),
    38       gtk_assistant_get_page_complete(helper->assistant,page)?"TRUE":"FALSE");
    39 }
    40 #endif
    41 
    42 PloverTransactionHelper *get_transaction_helper(void)
    43 {
    44     const char *dir;
    45     gchar *s;
    46     GError *err=NULL;
    47     PloverTransactionHelper *helper;
    48     dir=g_getenv("gtk_srcdir");
    49     s=g_build_filename(dir,"software-installation.ui",NULL);
    50     ui=gtk_builder_new();
    51     if (!gtk_builder_add_from_file(ui,s,&err))
    52 	g_error("%s: %s",s,err->message);
    53     g_free(s);
    54     helper=plover_transaction_helper_new(ui);
    55     g_object_unref(ui);
    56 #if 0
    57     if (helper->assistant)
    58     {
    59 	GtkWidget *page;
    60 	int i;
    61 	for(i=gtk_assistant_get_n_pages(helper->assistant)-1;i>=0;i--)
    62 	{
    63 	    page=gtk_assistant_get_nth_page(helper->assistant,i);
    64 	    g_signal_connect(page,"child-notify::complete",
    65 	      G_CALLBACK(complete_changed),helper);
    66 	    g_message("complete child property for %s initially %s",
    67 	      gtk_buildable_get_name(GTK_BUILDABLE(page)),
    68 	      gtk_assistant_get_page_complete(helper->assistant,page)?
    69 	      "TRUE":"FALSE");
    70 	}
    71     }
    72 #endif
    73     return helper;
    74 }
    75 
    76 static void test_init(void)
    77 {
    78     PloverTransactionHelper *helper;
    79     helper=get_transaction_helper();
    80     g_object_unref(helper);
    81 }
    82 
    83 static void test_basic_properties(void)
    84 {
    85     const char *prefix;
    86     char *uri;
    87     gchar *default_prefix;
    88     GError *err=NULL;
    89     struct comps *comps;
    90     PloverTransactionHelper *helper;
    91     PloverPackageSet *installed;
    92     PloverRepository *upstream;
    93     upstream=plover_repository_new_from_yum("../yum-repo-test-dir",&err);
    94     if (!upstream)
    95 	g_error("../yum-repo-test-dir: %s",err->message);
    96     uri=razor_path_to_uri("../razor-test-dir");
    97     installed=plover_package_set_new_from_installed(uri,&err);
    98     free(uri);
    99     if (!installed)
   100 	g_error("../razor-test-dir: %s",err->message);
   101     helper=get_transaction_helper();
   102     g_assert(!plover_transaction_helper_get_visible(helper));
   103     plover_transaction_helper_set_installed(helper,installed);
   104     g_assert(plover_transaction_helper_get_installed(helper)==installed);
   105     plover_transaction_helper_set_upstream(helper,upstream);
   106     g_assert(plover_transaction_helper_get_upstream(helper,&err)==upstream);
   107     g_assert(!err);
   108     plover_transaction_helper_set_base(helper,"../yum-repo-test-dir");
   109     g_assert_cmpstr(plover_transaction_helper_get_base(helper),==,"../yum-repo-test-dir");
   110     comps=plover_transaction_helper_get_comps(helper,&err);
   111     g_assert(!err);
   112     g_assert(plover_comps_lookup_group(comps,"base"));
   113     prefix=plover_transaction_helper_get_prefix(helper,&err);
   114     g_assert(!err);
   115     comps=plover_comps_new();
   116     plover_comps_set_vendor(comps,"Acme Corporation");
   117     default_prefix=plover_comps_get_default_prefix(comps);
   118     plover_comps_free(comps);
   119     g_assert_cmpstr(prefix,==,default_prefix);
   120     g_free(default_prefix);
   121     g_assert(!plover_transaction_helper_get_visible(helper));
   122     g_assert(!plover_transaction_helper_get_error(helper,NULL));
   123     g_object_unref(upstream);
   124     g_object_unref(installed);
   125     g_object_unref(helper);
   126 }
   127 
   128 static void test_install_group(void)
   129 {
   130     gchar *root;
   131     GError *err=NULL;
   132     char *uri;
   133     PloverPackageSet *installed;
   134     PloverTransactionHelper *helper;
   135     root=g_strdup("razor-test-dir-XXXXXX");
   136     g_assert(mkdtemp(root));
   137     uri=razor_path_to_uri(root);
   138     g_free(root);
   139     g_setenv("RAZOR_ROOT",uri,TRUE);
   140     free(uri);
   141     helper=get_transaction_helper();
   142     uri=razor_path_to_uri("../razor-test-dir");
   143     installed=plover_package_set_new_from_installed(uri,&err);
   144     free(uri);
   145     if (!installed)
   146 	g_error("../razor-test-dir: %s",err->message);
   147     plover_transaction_helper_set_installed(helper,installed);
   148     g_object_unref(installed);
   149     plover_transaction_helper_set_base(helper,"../yum-repo-test-dir");
   150     if (!plover_transaction_helper_install_group(helper,"base",&err))
   151 	g_error("base: %s",err->message);
   152     g_assert(!err);
   153     g_object_unref(helper);
   154     g_unsetenv("RAZOR_ROOT");
   155 }
   156 
   157 static void test_remove_group(void)
   158 {
   159     gchar *root;
   160     char *uri;
   161     GError *err=NULL;
   162     PloverPackageSet *installed;
   163     PloverTransactionHelper *helper;
   164     struct plover_vector *packages;
   165     char *pkgs[]={"zip",NULL};
   166     root=g_strdup("razor-test-dir-XXXXXX");
   167     g_assert(mkdtemp(root));
   168     uri=razor_path_to_uri(root);
   169     g_setenv("RAZOR_ROOT",uri,TRUE);
   170     helper=get_transaction_helper();
   171     plover_transaction_helper_set_base(helper,"../yum-repo-test-dir");
   172     packages=plover_transaction_helper_group_get_default_packages(helper,
   173       "zappy",&err);
   174     if (!packages)
   175 	g_error("zappy: %s",err->message);
   176     if (!plover_install("../yum-repo-test-dir",NULL,packages->strings,&err))
   177 	g_error("plover_install: %s",err->message);
   178     plover_vector_free(packages);
   179     installed=plover_package_set_new_from_installed(uri,&err);
   180     if (!installed)
   181 	g_error("%s: %s",root,err->message);
   182     plover_transaction_helper_set_installed(helper,installed);
   183     g_object_unref(installed);
   184     if (!plover_transaction_helper_remove_group(helper,"zappy",&err))
   185 	g_error("zappy: %s",err->message);
   186     g_assert(!err);
   187     g_object_unref(helper);
   188     g_unsetenv("RAZOR_ROOT");
   189     free(uri);
   190     g_free(root);
   191 }
   192 
   193 static void test_update(void)
   194 {
   195     gchar *root;
   196     char *uri;
   197     GError *err=NULL;
   198     PloverPackageSet *installed;
   199     PloverTransactionHelper *helper;
   200     root=g_strdup("razor-test-dir-XXXXXX");
   201     g_assert(mkdtemp(root));
   202     uri=razor_path_to_uri(root);
   203     g_setenv("RAZOR_ROOT",uri,TRUE);
   204     g_free(uri);
   205     helper=get_transaction_helper();
   206     uri=razor_path_to_uri("../razor-test-dir");
   207     installed=plover_package_set_new_from_installed(uri,&err);
   208     free(uri);
   209     if (!installed)
   210 	g_error("../razor-test-dir: %s",err->message);
   211     plover_transaction_helper_set_installed(helper,installed);
   212     g_object_unref(installed);
   213     plover_transaction_helper_set_base(helper,"../yum-repo-test-dir");
   214     if (plover_transaction_helper_update(helper,&err))
   215 	g_error("plover_transaction_helper_update reports work to be done");
   216     g_assert_error(err,PLOVER_GENERAL_ERROR,PLOVER_GENERAL_ERROR_NO_WORK);
   217     g_object_unref(helper);
   218     g_unsetenv("RAZOR_ROOT");
   219 }
   220 
   221 struct run_install_baton {
   222     enum {
   223 	RI_STATE_INIT = 0,
   224 	RI_STATE_SUMMARY,
   225 	RI_STATE_PROGRESS,
   226 	RI_STATE_PROGRESS_DELAY,
   227 	RI_STATE_DONE,
   228 	RI_STATE_FINISH
   229     } state;
   230     guint eid;		/* event ID (or 0) */
   231     PloverTransactionHelper *helper;
   232 };
   233 
   234 gboolean run_install_tick(gpointer data)
   235 {
   236     gboolean retval=TRUE;
   237     struct run_install_baton *baton=data;
   238     GtkWidget *page;
   239     GtkAssistant *assistant=baton->helper->assistant;
   240 #if 0
   241     page=gtk_assistant_get_nth_page(assistant,
   242       gtk_assistant_get_current_page(assistant));
   243     g_message("run_install_tick: state is %s, on page %s",
   244       baton->state==RI_STATE_INIT?"INIT":
   245       baton->state==RI_STATE_SUMMARY?"SUMMARY":
   246       baton->state==RI_STATE_PROGRESS?"PROGRESS":
   247       baton->state==RI_STATE_PROGRESS_DELAY?"PROGRESS_DELAY":
   248       baton->state==RI_STATE_DONE?"DONE":
   249       baton->state==RI_STATE_FINISH?"FINISH":
   250       "Unknown",
   251       gtk_buildable_get_name(GTK_BUILDABLE(page)));
   252 #endif
   253     switch(baton->state)
   254     {
   255 	case RI_STATE_INIT:
   256 	    if (!assistant || !gtk_widget_get_visible(GTK_WIDGET(assistant)))
   257 		return TRUE;
   258 	    if (!manual_mode)
   259 		gtk_button_clicked(GTK_BUTTON(assistant->forward));
   260 	    break;
   261 	case RI_STATE_SUMMARY:
   262 	    if (gtk_assistant_get_current_page(assistant)<1)
   263 		return TRUE;
   264 	    g_assert(gtk_widget_is_sensitive(assistant->apply));
   265 	    if (!manual_mode)
   266 		gtk_button_clicked(GTK_BUTTON(assistant->apply));
   267 	    break;
   268 	case RI_STATE_PROGRESS:
   269 	    if (gtk_assistant_get_current_page(assistant)<2)
   270 		return TRUE;
   271 	    page=gtk_assistant_get_nth_page(assistant,2);
   272 	    baton->eid=g_timeout_add_seconds(1,run_install_tick,baton);
   273 	    if (!gtk_assistant_get_page_complete(assistant,page))
   274 		return FALSE;
   275 	    else
   276 		retval=FALSE;
   277 	    break;
   278 	case RI_STATE_PROGRESS_DELAY:
   279 	    retval=FALSE;
   280 	    baton->eid=g_idle_add_full(G_PRIORITY_LOW,run_install_tick,baton,
   281 	      NULL);
   282 	    if (!manual_mode)
   283 		gtk_button_clicked(GTK_BUTTON(assistant->forward));
   284 	    break;
   285 	case RI_STATE_DONE:
   286 	    if (gtk_assistant_get_current_page(assistant)<3)
   287 		return TRUE;
   288 	    if (!manual_mode)
   289 		gtk_button_clicked(GTK_BUTTON(assistant->close));
   290 	    break;
   291 	case RI_STATE_FINISH:
   292 	    if (assistant && gtk_widget_get_visible(GTK_WIDGET(assistant)))
   293 		return TRUE;
   294 	    gtk_main_quit();
   295 	    baton->eid=0;
   296 	    return FALSE;
   297     }
   298     baton->state++;
   299     return retval;
   300 }
   301 
   302 static void test_run_install(void)
   303 {
   304     gchar *root;
   305     char *uri;
   306     GError *err=NULL;
   307     struct plover_vector *packages;
   308     PloverPackageSet *installed;
   309     PloverTransactionHelper *helper;
   310     struct run_install_baton baton={0,};
   311     root=g_strdup("razor-test-dir-XXXXXX");
   312     g_assert(mkdtemp(root));
   313     uri=razor_path_to_uri(root);
   314     g_setenv("RAZOR_ROOT",uri,TRUE);
   315     helper=get_transaction_helper();
   316     installed=plover_package_set_new();
   317     if (!plover_package_set_open(installed,uri,TRUE,&err))
   318 	g_error("%s: %s",root,err->message);
   319     plover_transaction_helper_set_installed(helper,installed);
   320     g_object_unref(installed);
   321     plover_transaction_helper_set_base(helper,"../yum-repo-test-dir");
   322     packages=plover_vector_new();
   323     plover_vector_append(packages,"zappy-tools");
   324     if (!plover_transaction_helper_install_packages(helper,packages,&err))
   325 	g_error("zappy-tools: %s",err->message);
   326     g_assert(!err);
   327     plover_vector_free(packages);
   328     plover_transaction_helper_present(helper);
   329     baton.helper=helper;
   330     baton.eid=g_idle_add_full(G_PRIORITY_LOW,run_install_tick,&baton,NULL);
   331     gtk_main();
   332     g_object_unref(helper);
   333     g_unsetenv("RAZOR_ROOT");
   334     free(uri);
   335     g_free(root);
   336 }
   337 
   338 struct run_remove_baton {
   339     enum {
   340 	RR_STATE_INIT = 0,
   341 	RR_STATE_SUMMARY,
   342 	RR_STATE_PROGRESS,
   343 	RR_STATE_PROGRESS_DELAY,
   344 	RR_STATE_DONE,
   345 	RR_STATE_FINISH
   346     } state;
   347     guint eid;		/* event ID (or 0) */
   348     PloverTransactionHelper *helper;
   349 };
   350 
   351 gboolean run_remove_tick(gpointer data)
   352 {
   353     gboolean retval=TRUE;
   354     struct run_remove_baton *baton=data;
   355     GtkWidget *page;
   356     GtkAssistant *assistant=baton->helper->assistant;
   357     switch(baton->state)
   358     {
   359 	case RR_STATE_INIT:
   360 	    if (!assistant || !gtk_widget_get_visible(GTK_WIDGET(assistant)))
   361 		return TRUE;
   362 	    if (!manual_mode)
   363 		gtk_button_clicked(GTK_BUTTON(assistant->forward));
   364 	    break;
   365 	case RR_STATE_SUMMARY:
   366 	    if (gtk_assistant_get_current_page(assistant)<1)
   367 		return TRUE;
   368 	    g_assert(gtk_widget_is_sensitive(assistant->apply));
   369 	    if (!manual_mode)
   370 		gtk_button_clicked(GTK_BUTTON(assistant->apply));
   371 	    break;
   372 	case RR_STATE_PROGRESS:
   373 	    if (gtk_assistant_get_current_page(assistant)<2)
   374 		return TRUE;
   375 	    page=gtk_assistant_get_nth_page(assistant,2);
   376 	    baton->eid=g_timeout_add_seconds(1,run_remove_tick,baton);
   377 	    if (!gtk_assistant_get_page_complete(assistant,page))
   378 		return FALSE;
   379 	    else
   380 		retval=FALSE;
   381 	    break;
   382 	case RR_STATE_PROGRESS_DELAY:
   383 	    retval=FALSE;
   384 	    baton->eid=g_idle_add_full(G_PRIORITY_LOW,run_remove_tick,baton,
   385 	      NULL);
   386 	    if (!manual_mode)
   387 		gtk_button_clicked(GTK_BUTTON(assistant->forward));
   388 	    break;
   389 	case RR_STATE_DONE:
   390 	    if (gtk_assistant_get_current_page(assistant)<3)
   391 		return TRUE;
   392 	    if (!manual_mode)
   393 		gtk_button_clicked(GTK_BUTTON(assistant->close));
   394 	    break;
   395 	case RR_STATE_FINISH:
   396 	    if (assistant && gtk_widget_get_visible(GTK_WIDGET(assistant)))
   397 		return TRUE;
   398 	    gtk_main_quit();
   399 	    baton->eid=0;
   400 	    return FALSE;
   401     }
   402     baton->state++;
   403     return retval;
   404 }
   405 
   406 static void test_run_remove(void)
   407 {
   408     gchar *root;
   409     char *uri;
   410     GError *err=NULL;
   411     struct plover_vector *packages;
   412     PloverPackageSet *installed;
   413     PloverTransactionHelper *helper;
   414     struct run_remove_baton baton={0,};
   415     root=g_strdup("razor-test-dir-XXXXXX");
   416     g_assert(mkdtemp(root));
   417     uri=razor_path_to_uri(root);
   418     g_setenv("RAZOR_ROOT",uri,TRUE);
   419     helper=get_transaction_helper();
   420     plover_transaction_helper_set_base(helper,"../yum-repo-test-dir");
   421     packages=
   422       plover_transaction_helper_group_get_default_packages(helper,"zappy",&err);
   423     if (!packages)
   424 	g_error("zappy: %s",err->message);
   425     if (!plover_install("../yum-repo-test-dir",NULL,packages->strings,&err))
   426 	g_error("plover_install: %s",err->message);
   427     plover_vector_free(packages);
   428     installed=plover_package_set_new();
   429     if (!plover_package_set_open(installed,uri,TRUE,&err))
   430 	g_error("%s: %s",root,err->message);
   431     plover_transaction_helper_set_installed(helper,installed);
   432     g_object_unref(installed);
   433     if (!plover_transaction_helper_remove_group(helper,"zappy",&err))
   434 	g_error("zappy: %s",err->message);
   435     g_assert(!err);
   436     plover_transaction_helper_present(helper);
   437     baton.helper=helper;
   438     baton.eid=g_idle_add_full(G_PRIORITY_LOW,run_remove_tick,&baton,NULL);
   439     gtk_main();
   440     g_object_unref(helper);
   441     g_unsetenv("RAZOR_ROOT");
   442     free(uri);
   443     g_free(root);
   444 }
   445 
   446 struct run_update_baton {
   447     enum {
   448 	RU_STATE_INIT = 0,
   449 	RU_STATE_SUMMARY,
   450 	RU_STATE_PROGRESS,
   451 	RU_STATE_PROGRESS_DELAY,
   452 	RU_STATE_DONE,
   453 	RU_STATE_FINISH
   454     } state;
   455     guint eid;		/* event ID (or 0) */
   456     PloverTransactionHelper *helper;
   457 };
   458 
   459 gboolean run_update_tick(gpointer data)
   460 {
   461     gboolean retval=TRUE;
   462     struct run_update_baton *baton=data;
   463     GtkWidget *page;
   464     GtkAssistant *assistant=baton->helper->assistant;
   465     switch(baton->state)
   466     {
   467 	case RU_STATE_INIT:
   468 	    if (!assistant || !gtk_widget_get_visible(GTK_WIDGET(assistant)))
   469 		return TRUE;
   470 	    if (!manual_mode)
   471 		gtk_button_clicked(GTK_BUTTON(assistant->forward));
   472 	    break;
   473 	case RU_STATE_SUMMARY:
   474 	    if (gtk_assistant_get_current_page(assistant)<1)
   475 		return TRUE;
   476 	    g_assert(gtk_widget_is_sensitive(assistant->apply));
   477 	    if (!manual_mode)
   478 		gtk_button_clicked(GTK_BUTTON(assistant->apply));
   479 	    break;
   480 	case RU_STATE_PROGRESS:
   481 	    if (gtk_assistant_get_current_page(assistant)<2)
   482 		return TRUE;
   483 	    page=gtk_assistant_get_nth_page(assistant,2);
   484 	    baton->eid=g_timeout_add_seconds(1,run_update_tick,baton);
   485 	    if (!gtk_assistant_get_page_complete(assistant,page))
   486 		return FALSE;
   487 	    else
   488 		retval=FALSE;
   489 	    break;
   490 	case RU_STATE_PROGRESS_DELAY:
   491 	    retval=FALSE;
   492 	    baton->eid=g_idle_add_full(G_PRIORITY_LOW,run_update_tick,baton,
   493 	      NULL);
   494 	    if (!manual_mode)
   495 		gtk_button_clicked(GTK_BUTTON(assistant->forward));
   496 	    break;
   497 	case RU_STATE_DONE:
   498 	    if (gtk_assistant_get_current_page(assistant)<3)
   499 		return TRUE;
   500 	    if (!manual_mode)
   501 		gtk_button_clicked(GTK_BUTTON(assistant->close));
   502 	    break;
   503 	case RU_STATE_FINISH:
   504 	    if (assistant && gtk_widget_get_visible(GTK_WIDGET(assistant)))
   505 		return TRUE;
   506 	    gtk_main_quit();
   507 	    baton->eid=0;
   508 	    return FALSE;
   509     }
   510     baton->state++;
   511     return retval;
   512 }
   513 
   514 static void test_run_update(void)
   515 {
   516     gchar *root;
   517     char *uri;
   518     GError *err=NULL;
   519     struct razor_importer *importer;
   520     struct razor_set *downgraded;
   521     struct razor_atomic *atomic;
   522     struct plover_vector *packages;
   523     PloverPackageSet *installed;
   524     PloverTransactionHelper *helper;
   525     struct run_update_baton baton={0,};
   526     root=g_strdup("razor-test-dir-XXXXXX");
   527     g_assert(mkdtemp(root));
   528     uri=razor_path_to_uri(root);
   529     g_setenv("RAZOR_ROOT",uri,TRUE);
   530     helper=get_transaction_helper();
   531     plover_transaction_helper_set_base(helper,"../yum-repo-test-dir");
   532     installed=plover_package_set_new();
   533     if (!plover_package_set_open(installed,uri,TRUE,&err))
   534 	g_error("%s: %s",root,err->message);
   535     importer=razor_importer_create();
   536     razor_importer_begin_package(importer,"zappy","0-1","noarch");
   537     razor_importer_add_details(importer,"","","","");
   538     razor_importer_add_property(importer,"zappy",RAZOR_PROPERTY_PROVIDES,"0-1");
   539     razor_importer_finish_package(importer);
   540     downgraded=razor_importer_finish(importer);
   541     atomic=razor_atomic_open("Add downgraded packages");
   542     if (!plover_package_set_update(installed,downgraded,atomic) ||
   543       razor_atomic_commit(atomic))
   544 	g_error("%s: %s",root,razor_atomic_get_error_msg(atomic));
   545     razor_atomic_destroy(atomic);
   546     razor_set_unref(downgraded);
   547     plover_transaction_helper_set_installed(helper,installed);
   548     g_object_unref(installed);
   549     if (!plover_transaction_helper_update(helper,&err))
   550 	g_error("update: %s",err->message);
   551     g_assert(!err);
   552     plover_transaction_helper_present(helper);
   553     baton.helper=helper;
   554     baton.eid=g_idle_add_full(G_PRIORITY_LOW,run_update_tick,&baton,NULL);
   555     gtk_main();
   556     g_object_unref(helper);
   557     g_unsetenv("RAZOR_ROOT");
   558     free(uri);
   559     g_free(root);
   560 }
   561 
   562 struct check_vendor_baton {
   563     enum {
   564 	CV_STATE_INIT = 0,
   565 	CV_STATE_SUMMARY,
   566 	CV_STATE_PROGRESS,
   567 	CV_STATE_PROGRESS_DELAY,
   568 	CV_STATE_DONE,
   569 	CV_STATE_FINISH
   570     } state;
   571     guint eid;		/* event ID (or 0) */
   572     PloverTransactionHelper *helper;
   573 };
   574 
   575 gboolean check_vendor_tick(gpointer data)
   576 {
   577     gboolean retval=TRUE;
   578     struct check_vendor_baton *baton=data;
   579     GtkWidget *page,*w;
   580     GtkAssistant *assistant=baton->helper->assistant;
   581     switch(baton->state)
   582     {
   583 	case CV_STATE_INIT:
   584 	    if (!assistant || !gtk_widget_get_visible(GTK_WIDGET(assistant)))
   585 		return TRUE;
   586 	    if (!manual_mode)
   587 		gtk_button_clicked(GTK_BUTTON(assistant->forward));
   588 	    break;
   589 	case CV_STATE_SUMMARY:
   590 	    if (gtk_assistant_get_current_page(assistant)<1)
   591 		return TRUE;
   592 	    g_assert(!gtk_widget_is_sensitive(assistant->apply));
   593 	    w=GTK_WIDGET(gtk_builder_get_object(ui,"SIRemoveExisting"));
   594 	    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w),TRUE);
   595 	    g_assert(gtk_widget_is_sensitive(assistant->apply));
   596 	    if (!manual_mode)
   597 		gtk_button_clicked(GTK_BUTTON(assistant->apply));
   598 	    break;
   599 	case CV_STATE_PROGRESS:
   600 	    if (gtk_assistant_get_current_page(assistant)<2)
   601 		return TRUE;
   602 	    page=gtk_assistant_get_nth_page(assistant,2);
   603 	    baton->eid=g_timeout_add_seconds(1,check_vendor_tick,baton);
   604 	    if (!gtk_assistant_get_page_complete(assistant,page))
   605 		return FALSE;
   606 	    else
   607 		retval=FALSE;
   608 	    break;
   609 	case CV_STATE_PROGRESS_DELAY:
   610 	    retval=FALSE;
   611 	    baton->eid=g_idle_add_full(G_PRIORITY_LOW,check_vendor_tick,baton,
   612 	      NULL);
   613 	    if (!manual_mode)
   614 		gtk_button_clicked(GTK_BUTTON(assistant->forward));
   615 	    break;
   616 	case CV_STATE_DONE:
   617 	    if (gtk_assistant_get_current_page(assistant)<3)
   618 		return TRUE;
   619 	    if (!manual_mode)
   620 		gtk_button_clicked(GTK_BUTTON(assistant->close));
   621 	    break;
   622 	case CV_STATE_FINISH:
   623 	    if (assistant && gtk_widget_get_visible(GTK_WIDGET(assistant)))
   624 		return TRUE;
   625 	    gtk_main_quit();
   626 	    baton->eid=0;
   627 	    return FALSE;
   628     }
   629     baton->state++;
   630     return retval;
   631 }
   632 
   633 static void test_check_vendor(void)
   634 {
   635     int fh;
   636     gchar *root,*s;
   637     char *uri;
   638     GError *err=NULL;
   639     struct razor_importer *importer;
   640     struct razor_set *downgraded;
   641     struct razor_atomic *atomic;
   642     struct plover_vector *packages;
   643     PloverPackageSet *installed;
   644     PloverTransactionHelper *helper;
   645     struct check_vendor_baton baton={0,};
   646     g_setenv("PLOVER_VENDOR_PREFIX","/srv",TRUE);
   647     root=g_strdup("razor-test-dir-XXXXXX");
   648     g_assert(mkdtemp(root));
   649     uri=razor_path_to_uri(root);
   650     g_setenv("RAZOR_ROOT",uri,TRUE);
   651     helper=get_transaction_helper();
   652     plover_transaction_helper_set_check_vendor(helper,TRUE);
   653     plover_transaction_helper_set_base(helper,"../yum-repo-test-dir");
   654     installed=plover_package_set_new();
   655     if (!plover_package_set_open(installed,uri,TRUE,&err))
   656 	g_error("%s: %s",root,err->message);
   657     importer=razor_importer_create();
   658     razor_importer_begin_package(importer,"zappy","0-1","noarch");
   659     razor_importer_add_details(importer,"","","","");
   660     razor_importer_add_property(importer,"zappy",RAZOR_PROPERTY_PROVIDES,"0-1");
   661     razor_importer_add_install_prefix(importer,"/test");
   662     razor_importer_add_file(importer,"/test/bin/zappy");
   663     razor_importer_finish_package(importer);
   664     downgraded=razor_importer_finish(importer);
   665     atomic=razor_atomic_open("Add downgraded packages");
   666     razor_atomic_make_dirs(atomic,uri,"/test/bin/zappy");
   667     s=g_build_filename(uri,"test/bin/zappy",NULL);
   668     fh=razor_atomic_create_file(atomic,s,S_IRWXU|S_IRWXG|S_IRWXO);
   669     g_free(s);
   670     razor_atomic_close(atomic,fh);
   671     if (!plover_package_set_update(installed,downgraded,atomic) ||
   672       razor_atomic_commit(atomic))
   673 	g_error("%s: %s",root,razor_atomic_get_error_msg(atomic));
   674     razor_atomic_destroy(atomic);
   675     razor_set_unref(downgraded);
   676     plover_transaction_helper_set_installed(helper,installed);
   677     g_object_unref(installed);
   678     if (!plover_transaction_helper_update(helper,&err))
   679 	g_error("update: %s",err->message);
   680     g_assert(!err);
   681     plover_transaction_helper_present(helper);
   682     baton.helper=helper;
   683     baton.eid=g_idle_add_full(G_PRIORITY_LOW,check_vendor_tick,&baton,NULL);
   684     gtk_main();
   685     g_object_unref(helper);
   686     g_unsetenv("RAZOR_ROOT");
   687     g_free(root);
   688     free(uri);
   689     g_unsetenv("PLOVER_VENDOR_PREFIX");
   690 }
   691 
   692 static gchar *create_new_root_and_install(gchar **prefix_p,char **pkgs)
   693 {
   694     gchar *root,*prefix;
   695     char *uri;
   696     GError *err=NULL;
   697     struct comps *comps;
   698     PloverTransaction *transaction;
   699     root=g_strdup("razor-test-dir-XXXXXX");
   700     g_assert(mkdtemp(root));
   701     uri=razor_path_to_uri(root);
   702     g_setenv("RAZOR_ROOT",uri,TRUE);
   703     free(uri);
   704     uri=razor_path_to_uri("../yum-repo-test-dir/repodata/comps.xml");
   705     comps=plover_comps_new_from_uri(uri,&err);
   706     free(uri);
   707     if (!comps)
   708 	g_error("../yum-repo-test-dir/repodata/comps.xml: %s",err->message);
   709     prefix=plover_comps_get_default_prefix(comps);
   710     plover_comps_free(comps);
   711     transaction=plover_transaction_new_install("../yum-repo-test-dir",prefix,
   712       pkgs,&err);
   713     if (!transaction)
   714 	g_error("../yum-repo-test-dir: %s",err->message);
   715     g_assert(plover_transaction_commit(transaction,NULL,NULL));
   716     g_object_unref(transaction);
   717     fflush(stdout);
   718     if (prefix_p)
   719 	*prefix_p=prefix;
   720     else
   721 	g_free(prefix);
   722     return root;
   723 }
   724 
   725 static check_file(const char *root,const char *path)
   726 {
   727     gchar *s;
   728     s=g_build_filename(root,path,NULL);
   729     g_assert(g_file_test(s,G_FILE_TEST_EXISTS));
   730     g_free(s);
   731 }
   732 
   733 static check_no_file(const char *root,const char *path)
   734 {
   735     gchar *s;
   736     s=g_build_filename(root,path,NULL);
   737     g_assert(!g_file_test(s,G_FILE_TEST_EXISTS));
   738     g_free(s);
   739 }
   740 
   741 /*
   742  * Test that installing from a repository which is marked as distribution-local
   743  * on top of an existing global installation works if prefix is NULL
   744  * (which is the case on UNIX if PLOVER_VENDOR_PREFIX is not set).
   745  */
   746 static void test_unprefixed_distribution_local(void)
   747 {
   748     gchar *root;
   749     GError *err=NULL;
   750     char *pkgs[]={"zap",NULL};
   751     struct plover_vector *packages;
   752     PloverPackageSet *installed;
   753     PloverTransactionHelper *helper;
   754     struct run_install_baton baton={0,};
   755     g_unsetenv("PLOVER_VENDOR_PREFIX");
   756     root=create_new_root_and_install(NULL,pkgs);
   757     check_file(root,"/usr/bin/zap");
   758     helper=get_transaction_helper();
   759     plover_transaction_helper_set_check_vendor(helper,TRUE);
   760     plover_transaction_helper_set_base(helper,"../distribution-local-test-dir");
   761     packages=plover_vector_new();
   762     plover_vector_append(packages,"zappy");
   763     if (!plover_transaction_helper_install_packages(helper,packages,&err))
   764 	g_error("zappy: %s",err->message);
   765     g_assert(!err);
   766     plover_vector_free(packages);
   767     plover_transaction_helper_present(helper);
   768     baton.helper=helper;
   769     baton.eid=g_idle_add_full(G_PRIORITY_LOW,run_install_tick,&baton,NULL);
   770     gtk_main();
   771     g_object_unref(helper);
   772     check_file(root,"/usr/bin/zappy");
   773     g_free(root);
   774     g_unsetenv("RAZOR_ROOT");
   775 }
   776 
   777 /*
   778  * Test that installing from a repository which is marked as distribution-local
   779  * on top of an existing global installation requires removing the existing
   780  * installation if prefix is non-NULL (which is the case if PLOVER_VENDOR_PREFIX
   781  * is set).
   782  */
   783 static void test_prefixed_distribution_local(void)
   784 {
   785     gchar *root,*s;
   786     gchar *prefix;
   787     GError *err=NULL;
   788     char *pkgs[]={"zap",NULL};
   789     struct plover_vector *packages;
   790     PloverPackageSet *installed;
   791     PloverTransactionHelper *helper;
   792     struct check_vendor_baton baton={0,};
   793     g_setenv("PLOVER_VENDOR_PREFIX","/srv",TRUE);
   794     helper=get_transaction_helper();
   795     plover_transaction_helper_set_check_vendor(helper,TRUE);
   796     plover_transaction_helper_set_base(helper,"../distribution-local-test-dir");
   797     root=create_new_root_and_install(&prefix,pkgs);
   798     s=g_build_filename(prefix,"bin/zap",NULL);
   799     check_file(root,s);
   800     g_free(s);
   801     packages=plover_vector_new();
   802     plover_vector_append(packages,"filesystem");
   803     if (!plover_transaction_helper_install_packages(helper,packages,&err))
   804 	g_error("filesystem: %s",err->message);
   805     g_assert(!err);
   806     plover_vector_free(packages);
   807     plover_transaction_helper_present(helper);
   808     baton.helper=helper;
   809     baton.eid=g_idle_add_full(G_PRIORITY_LOW,check_vendor_tick,&baton,NULL);
   810     gtk_main();
   811     g_object_unref(helper);
   812     s=g_build_filename(prefix,"bin/zap",NULL);
   813     check_no_file(root,s);
   814     g_free(s);
   815     check_no_file(root,"/media");
   816     g_free(root);
   817     g_free(prefix);
   818     g_unsetenv("RAZOR_ROOT");
   819 }
   820 
   821 struct set_error_baton {
   822     enum {
   823 	SE_STATE_INIT = 0,
   824 	SE_STATE_FINISH
   825     } state;
   826     PloverTransactionHelper *helper;
   827 };
   828 
   829 /*
   830  * This handler may be called as either an event (ie., idle or timeout)
   831  * or as a (swapped) signal. In the latter case, the return is ignored.
   832  */
   833 gboolean set_error_tick(gpointer data)
   834 {
   835     struct set_error_baton *baton=data;
   836     GtkDialog *dlg;
   837     GtkWidget *button;
   838     dlg=GTK_DIALOG(baton->helper->error_dialog);
   839     switch(baton->state)
   840     {
   841 	case SE_STATE_INIT:
   842 	    if (!dlg || !gtk_widget_get_visible(GTK_WIDGET(dlg)))
   843 		return TRUE;
   844 	    button=gtk_dialog_get_widget_for_response(dlg,GTK_RESPONSE_CLOSE);
   845 	    if (!manual_mode)
   846 		gtk_button_clicked(GTK_BUTTON(button));
   847 	    break;
   848 	case SE_STATE_FINISH:
   849 	    if (dlg && gtk_widget_get_visible(GTK_WIDGET(dlg)))
   850 		return TRUE;
   851 	    gtk_main_quit();
   852 	    return FALSE;
   853     }
   854     baton->state++;
   855     return TRUE;
   856 }
   857 
   858 static void test_set_error(void)
   859 {
   860     gchar *root;
   861     char *uri;
   862     const char *errmsg;
   863     GError *err=NULL;
   864     const GError *err2=NULL;
   865     PloverPackageSet *installed;
   866     PloverTransactionHelper *helper;
   867     struct set_error_baton baton={0,};
   868     root=g_strdup("razor-test-dir-XXXXXX");
   869     g_assert(mkdtemp(root));
   870     uri=razor_path_to_uri(root);
   871     g_free(root);
   872     g_setenv("RAZOR_ROOT",uri,TRUE);
   873     free(uri);
   874     helper=get_transaction_helper();
   875     uri=razor_path_to_uri("../razor-test-dir");
   876     installed=plover_package_set_new_from_installed(uri,&err);
   877     free(uri);
   878     if (!installed)
   879 	g_error("../razor-test-dir: %s",err->message);
   880     plover_transaction_helper_set_installed(helper,installed);
   881     g_object_unref(installed);
   882     plover_transaction_helper_set_base(helper,"../yum-repo-test-dir");
   883     if (!plover_transaction_helper_update(helper,&err))
   884 	plover_transaction_helper_set_error(helper,err,"Expected error");
   885     g_assert(plover_transaction_helper_get_visible(helper));
   886     errmsg=plover_transaction_helper_get_error(helper,&err2);
   887     g_assert_cmpstr(errmsg,==,"Expected error");
   888     g_assert_error(err2,err->domain,err->code);
   889     plover_transaction_helper_present(helper);
   890     baton.helper=helper;
   891     g_idle_add_full(G_PRIORITY_LOW,set_error_tick,&baton,NULL);
   892     g_error_free(err);
   893     gtk_main();
   894     g_object_unref(helper);
   895     g_unsetenv("RAZOR_ROOT");
   896 }
   897 
   898 int main(int argc,char **argv)
   899 {
   900     int retval;
   901     GError *err=NULL;
   902     /*
   903      * Note that because g_test_init() handles --help,
   904      * these options will not appear in the output.
   905      */
   906     GOptionEntry options[]={
   907 	{"manual",0,0,G_OPTION_ARG_NONE,&manual_mode,
   908 	  "Disable automatic mode",NULL},
   909 	{NULL}
   910     };
   911     g_test_init(&argc,&argv,NULL);
   912     g_setenv("GTK_MODULES","",TRUE);
   913     g_setenv("GTK2_RC_FILES","/dev/null",TRUE);
   914     gtk_disable_setlocale();
   915     setlocale(LC_ALL,"C");
   916     g_test_bug_base("mailto:ali@juiblex.co.uk");
   917     if (!gtk_init_with_args(&argc,&argv,NULL,options,NULL,&err))
   918     {
   919 	g_printf("%s\n",err->message);
   920 	exit(0);
   921     }
   922     g_test_add_func("/transactionhelper/init",test_init);
   923     g_test_add_func("/transactionhelper/basic-properties",
   924       test_basic_properties);
   925     g_test_add_func("/transactionhelper/install-group",test_install_group);
   926     g_test_add_func("/transactionhelper/remove-group",test_remove_group);
   927     g_test_add_func("/transactionhelper/update",test_update);
   928     g_test_add_func("/transactionhelper/run-install",test_run_install);
   929     g_test_add_func("/transactionhelper/run-remove",test_run_remove);
   930     g_test_add_func("/transactionhelper/run-update",test_run_update);
   931     g_test_add_func("/transactionhelper/check-vendor",test_check_vendor);
   932     g_test_add_func("/transactionhelper/unprefixed-distribution-local",
   933       test_unprefixed_distribution_local);
   934     g_test_add_func("/transactionhelper/prefixed-distribution-local",
   935       test_prefixed_distribution_local);
   936     g_test_add_func("/transactionhelper/set-error",test_set_error);
   937     retval=g_test_run();
   938     return retval;
   939 }