diff mbox

[2/2,FYI] coverage test for Linux installs

Message ID 1312831898-18702-2-git-send-email-aliguori@us.ibm.com
State New
Headers show

Commit Message

Anthony Liguori Aug. 8, 2011, 7:31 p.m. UTC
This is a simple tool that I've been using for the past couple months to help
with day-to-day testing of changes.  It may seem like it's similar to
kvm-autotest but it's actually quite different.  Most noticably:

 - It's a coverage test instead of an acceptance test.  Each time it runs it
   uses a slightly a semi-random configuration for the guest.  This means that
   the more you run it, the more coverage you get.  This is a good fit for
   maintainer testing because you get wide testing without having to wait for
   48-hour test cycles.

 - It runs in the foreground as an unprivileged user.  This means I can kick it
   off in a terminal while I go off and do something else, but still keep an
   eye on what's going on.

 - It works with TCG guests too and includes a TCG test case for the pseries
   Power machine.

That said, it's *not* a replacement for KVM autotest.  It is not a good tool
for doing acceptance testing, like you would do to cut a new QEMU release.  But
perhaps there's some behavior that could be leveraged by KVM autotest in the
future here.

I'm not proposing this for tree inclusion right now.  Just sharing a tool that
I've found to be useful.  I really just want the previous patch to go in so that
I can stop carrying that patch privately.

Right now, you need to setup an ISO directory to use the test tool.  After
copy-on-read lands in the tree, I plan on making it create CoR files backing to
http so that no explicit setup is required.
---
 test-linux.c |  549 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 549 insertions(+), 0 deletions(-)
 create mode 100644 test-linux.c

Comments

Anthony Liguori Aug. 8, 2011, 7:35 p.m. UTC | #1
On 08/08/2011 02:31 PM, Anthony Liguori wrote:
> +static const char preseed[] =
> +    "d-i	debian-installer/locale	string en_US.UTF-8\n"
> +    "d-i	debian-installer/splash boolean false\n"
> +    "d-i	console-setup/ask_detect	boolean false\n"
> +    "d-i	console-setup/layoutcode	string us\n"
> +    "d-i	console-setup/variantcode	string \n"
> +    "d-i	netcfg/get_nameservers	string \n"
> +    "d-i	netcfg/get_ipaddress	string \n"
> +    "d-i	netcfg/get_netmask	string 255.255.255.0\n"
> +    "d-i	netcfg/get_gateway	string \n"
> +    "d-i	netcfg/confirm_static	boolean true\n"
> +    "d-i	clock-setup/utc	boolean true\n"
> +    "d-i 	partman-auto/method string regular\n"
> +    "d-i 	partman-lvm/device_remove_lvm boolean true\n"
> +    "d-i 	partman-lvm/confirm boolean true\n"
> +    "d-i 	partman/confirm_write_new_label boolean true\n"
> +    "d-i 	partman/choose_partition        select Finish partitioning and write changes to disk\n"
> +    "d-i 	partman/confirm boolean true\n"
> +    "d-i 	partman/confirm_nooverwrite boolean true\n"
> +    "d-i 	partman/default_filesystem string ext3\n"
> +    "d-i 	clock-setup/utc boolean true\n"
> +    "d-i	clock-setup/ntp	boolean true\n"
> +    "d-i	clock-setup/ntp-server	string ntp.ubuntu.com\n"
> +    "d-i	base-installer/kernel/image	string linux-server\n"
> +    "d-i	passwd/root-login	boolean false\n"
> +    "d-i	passwd/make-user	boolean true\n"
> +    "d-i	passwd/user-fullname	string ubuntu\n"
> +    "d-i	passwd/username	string ubuntu\n"
> +    "d-i	passwd/user-password-crypted	password $6$.1eHH0iY$ArGzKX2YeQ3G6U.mlOO3A.NaL22Ewgz8Fi4qqz.Ns7EMKjEJRIW2Pm/TikDptZpuu7I92frytmk5YeL.9fRY4.\n"
> +    "d-i	passwd/user-uid	string \n"
> +    "d-i	user-setup/allow-password-weak	boolean false\n"
> +    "d-i	user-setup/encrypt-home	boolean false\n"
> +    "d-i	passwd/user-default-groups	string adm cdrom dialout lpadmin plugdev sambashare\n"
> +    "d-i	apt-setup/services-select	multiselect security\n"
> +    "d-i	apt-setup/security_host	string security.ubuntu.com\n"
> +    "d-i	apt-setup/security_path	string /ubuntu\n"
> +    "d-i	debian-installer/allow_unauthenticated	string false\n"
> +    "d-i	pkgsel/upgrade	select safe-upgrade\n"
> +    "d-i	pkgsel/language-packs	multiselect \n"
> +    "d-i	pkgsel/update-policy	select none\n"
> +    "d-i	pkgsel/updatedb	boolean true\n"
> +    "d-i	grub-installer/skip	boolean false\n"
> +    "d-i	lilo-installer/skip	boolean false\n"
> +    "d-i	grub-installer/only_debian	boolean true\n"
> +    "d-i	grub-installer/with_other_os	boolean true\n"
> +    "d-i	finish-install/keep-consoles	boolean false\n"
> +    "d-i	finish-install/reboot_in_progress	note \n"
> +    "d-i	cdrom-detect/eject	boolean true\n"
> +    "d-i	debian-installer/exit/halt	boolean false\n"
> +    "d-i	debian-installer/exit/poweroff	boolean false\n"
> +    "d-i	preseed/late_command		string echo -ne '\x1' | dd bs=1 count=1 seek=1281 of=/dev/port\n"

If anyone is curious, this little bit of loveliness will write 0x01 to 
port 0x501 when installation succeeds.  This is how the test tool 
determines that an installation completed successfully.

Regards,

Anthony Liguori
diff mbox

Patch

diff --git a/test-linux.c b/test-linux.c
new file mode 100644
index 0000000..55bcaa0
--- /dev/null
+++ b/test-linux.c
@@ -0,0 +1,549 @@ 
+/*
+ */
+
+#include "qemu-common.h"
+#include "qemu_socket.h"
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <glib.h>
+#include <linux/iso_fs.h>
+
+static uint8_t iso_u8(void *ptr)
+{
+    uint8_t *buf = (uint8_t *)ptr;
+    return buf[0];
+}
+
+static inline uint16_t iso_u16(void *ptr)
+{
+    uint8_t *buf = (uint8_t *)ptr;
+    return buf[0] | (buf[1] << 8);
+}
+
+static inline uint32_t iso_u32(void *ptr)
+{
+    uint8_t *buf = (uint8_t *)ptr;
+    return (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
+}
+
+typedef struct IsoFile
+{
+    int directory;
+    off_t offset;
+    off_t size;
+} IsoFile;
+
+static IsoFile *iso_find_path(int fd, IsoFile *dir, const char *pathname)
+{
+    struct iso_directory_record record;
+    ssize_t len;
+    char name[257];
+    uint8_t name_len;
+    off_t offset = dir->offset;
+
+    if (!dir->directory) {
+        return NULL;
+    }
+
+    len = pread(fd, &record, sizeof(record), offset);
+    assert(len == sizeof(record));
+
+    while ((offset - dir->offset) < dir->size) {
+        size_t size;
+        int directory;
+
+        name_len = iso_u8(record.name_len);
+        directory = !!(iso_u8(record.flags) & 0x02);
+
+        size = len;
+        len = pread(fd, name, name_len, offset + len);
+        assert(len == name_len);
+        name[name_len] = 0;
+
+        size += len;
+
+        if (size < iso_u8(record.length)) {
+            size_t record_length = iso_u8(record.length) - size;
+            char buffer[256];
+            int i = 0;
+
+            len = pread(fd, buffer, record_length, offset + size);
+            assert(len == record_length);
+            if (buffer[i] == 0) {
+                i++;
+            }
+
+            if (record_length > 5 && buffer[i] == 'R' && buffer[i + 1] == 'R') {
+                i += 5;
+                while (i < record_length) {
+                    if (buffer[i] == 'N' && buffer[i + 1] == 'M') {
+                        name_len = buffer[i + 2] - 5;
+                        memcpy(name, &buffer[i + 5], name_len);
+                        name[name_len] = 0;
+                        i += buffer[i + 2];
+                    } else {
+                        break;
+                    }
+                }
+            }
+        }
+
+        if (strcmp(name, pathname) == 0) {
+            IsoFile *ret = malloc(sizeof(*ret));
+            ret->directory = directory;
+            ret->offset = iso_u32(record.extent) * 2048;
+            ret->size = iso_u32(record.size);
+            return ret;
+        }
+
+        offset += iso_u8(record.length);
+
+        do {
+            len = pread(fd, &record, sizeof(record), offset);
+            assert(len == sizeof(record));
+
+            if (iso_u8(record.length) == 0) {
+                offset += 2047;
+                offset &= ~2047ULL;
+            }
+        } while (iso_u8(record.length) == 0 && (offset - dir->offset) < dir->size);
+    }
+    
+    return NULL;
+}
+
+static IsoFile *iso_find_root(int fd)
+{
+    off_t offset = 0;
+    ssize_t len;
+    struct iso_volume_descriptor vd;
+
+    offset = 32768;
+    do {
+        len = pread(fd, &vd, sizeof(vd), offset);
+        assert(len == sizeof(vd));
+
+        if (iso_u8(vd.type) == 1) {
+            struct iso_primary_descriptor *pd;
+            struct iso_directory_record *root;
+            char name[256];
+            IsoFile *ret;
+
+            pd = (struct iso_primary_descriptor *)&vd;
+            root = (struct iso_directory_record *)pd->root_directory_record;
+
+            memcpy(name, root->name, iso_u8(root->name_len));
+            name[iso_u8(root->name_len)] = 0;
+
+            ret = malloc(sizeof(*ret));
+            ret->directory = 1;
+            ret->offset = iso_u32(root->extent) * 2048;
+            ret->size = iso_u32(root->size);
+            return ret;
+        }
+
+        offset += len;
+    } while (iso_u8(vd.type) != 255);
+
+    return NULL;
+}
+
+static IsoFile *iso_find_file(int fd, const char *filename)
+{
+    const char *end;
+    IsoFile *cur = iso_find_root(fd);
+
+    while (cur && filename) {
+        IsoFile *dir;
+        char pathname[257];
+
+        end = strchr(filename, '/');
+        if (end) {
+            memcpy(pathname, filename, end - filename);
+            pathname[end - filename] = 0;
+            filename = end + 1;
+        } else {
+            snprintf(pathname, sizeof(pathname), "%s", filename);
+            filename = end;
+        }
+
+        dir = iso_find_path(fd, cur, pathname);
+        free(cur);
+        cur = dir;
+    }
+
+    return cur;
+}
+
+static const char preseed[] = 
+    "d-i	debian-installer/locale	string en_US.UTF-8\n"
+    "d-i	debian-installer/splash boolean false\n"
+    "d-i	console-setup/ask_detect	boolean false\n"
+    "d-i	console-setup/layoutcode	string us\n"
+    "d-i	console-setup/variantcode	string \n"
+    "d-i	netcfg/get_nameservers	string \n"
+    "d-i	netcfg/get_ipaddress	string \n"
+    "d-i	netcfg/get_netmask	string 255.255.255.0\n"
+    "d-i	netcfg/get_gateway	string \n"
+    "d-i	netcfg/confirm_static	boolean true\n"
+    "d-i	clock-setup/utc	boolean true\n"
+    "d-i 	partman-auto/method string regular\n"
+    "d-i 	partman-lvm/device_remove_lvm boolean true\n"
+    "d-i 	partman-lvm/confirm boolean true\n"
+    "d-i 	partman/confirm_write_new_label boolean true\n"
+    "d-i 	partman/choose_partition        select Finish partitioning and write changes to disk\n"
+    "d-i 	partman/confirm boolean true\n"
+    "d-i 	partman/confirm_nooverwrite boolean true\n"
+    "d-i 	partman/default_filesystem string ext3\n"
+    "d-i 	clock-setup/utc boolean true\n"
+    "d-i	clock-setup/ntp	boolean true\n"
+    "d-i	clock-setup/ntp-server	string ntp.ubuntu.com\n"
+    "d-i	base-installer/kernel/image	string linux-server\n"
+    "d-i	passwd/root-login	boolean false\n"
+    "d-i	passwd/make-user	boolean true\n"
+    "d-i	passwd/user-fullname	string ubuntu\n"
+    "d-i	passwd/username	string ubuntu\n"
+    "d-i	passwd/user-password-crypted	password $6$.1eHH0iY$ArGzKX2YeQ3G6U.mlOO3A.NaL22Ewgz8Fi4qqz.Ns7EMKjEJRIW2Pm/TikDptZpuu7I92frytmk5YeL.9fRY4.\n"
+    "d-i	passwd/user-uid	string \n"
+    "d-i	user-setup/allow-password-weak	boolean false\n"
+    "d-i	user-setup/encrypt-home	boolean false\n"
+    "d-i	passwd/user-default-groups	string adm cdrom dialout lpadmin plugdev sambashare\n"
+    "d-i	apt-setup/services-select	multiselect security\n"
+    "d-i	apt-setup/security_host	string security.ubuntu.com\n"
+    "d-i	apt-setup/security_path	string /ubuntu\n"
+    "d-i	debian-installer/allow_unauthenticated	string false\n"
+    "d-i	pkgsel/upgrade	select safe-upgrade\n"
+    "d-i	pkgsel/language-packs	multiselect \n"
+    "d-i	pkgsel/update-policy	select none\n"
+    "d-i	pkgsel/updatedb	boolean true\n"
+    "d-i	grub-installer/skip	boolean false\n"
+    "d-i	lilo-installer/skip	boolean false\n"
+    "d-i	grub-installer/only_debian	boolean true\n"
+    "d-i	grub-installer/with_other_os	boolean true\n"
+    "d-i	finish-install/keep-consoles	boolean false\n"
+    "d-i	finish-install/reboot_in_progress	note \n"
+    "d-i	cdrom-detect/eject	boolean true\n"
+    "d-i	debian-installer/exit/halt	boolean false\n"
+    "d-i	debian-installer/exit/poweroff	boolean false\n"
+    "d-i	preseed/late_command		string echo -ne '\x1' | dd bs=1 count=1 seek=1281 of=/dev/port\n"
+    ;
+
+static const char ks[] =
+    "install\n"
+    "text\n"
+    "reboot\n"
+    "lang en_US.UTF-8\n"
+    "keyboard us\n"
+    "network --bootproto dhcp\n"
+    "rootpw 123456\n"
+    "firewall --enabled --ssh\n"
+    "selinux --enforcing\n"
+    "timezone --utc America/New_York\n"
+    "firstboot --disable\n"
+    "bootloader --location=mbr --append=\"console=tty0 console=ttyS0,115200\"\n"
+    "zerombr\n"
+    "clearpart --all --initlabel\n"
+    "autopart\n"
+    "poweroff\n"
+    "\n"
+    "%packages\n"
+    "@base\n"
+    "@core\n"
+    "%end\n"
+    "%post\n"
+    "echo -ne '\x1' | dd bs=1 count=1 seek=1281 of=/dev/port\n"
+    "%end\n"
+    ;
+
+static int systemf(const char *fmt, ...)
+    __attribute__((format(printf, 1, 2)));
+
+static int systemf(const char *fmt, ...)
+{
+    char buffer[1024];
+    va_list ap;
+
+    va_start(ap, fmt);
+    vsnprintf(buffer, sizeof(buffer), fmt, ap);
+    va_end(ap);
+
+    g_test_message("Running command %s\n", buffer);
+
+    return system(buffer);
+}
+
+typedef struct WeightedChoice
+{
+    const char *string;
+    int percentage;
+} WeightedChoice;
+
+static const char *choose(WeightedChoice *choices)
+{
+    int i, value;
+    int cur_percentage = 0;
+
+    value = g_test_rand_int_range(0, 100);
+    for (i = 0; choices[i].string; i++) {
+        cur_percentage += choices[i].percentage;
+        if (value < cur_percentage) {
+            return choices[i].string;
+        }
+    }
+
+    g_assert_not_reached();
+    return NULL;
+}
+
+static void copy_file_from_iso(const char *iso, const char *src, const char *dst)
+{
+    int iso_fd, dst_fd;
+    IsoFile *filp;
+    off_t offset;
+
+    iso_fd = open(iso, O_RDONLY);
+    dst_fd = open(dst, O_RDWR | O_CREAT | O_TRUNC, 0644);
+
+    g_assert(iso_fd != -1);
+    g_assert(dst_fd != -1);
+
+    filp = iso_find_file(iso_fd, src);
+
+    for (offset = 0; offset < filp->size; offset += 2048) {
+        char buffer[2048];
+        size_t size = MIN(filp->size - offset, 2048);
+        ssize_t len;
+
+        len = pread(iso_fd, buffer, size, filp->offset + offset);
+        g_assert(len == size);
+
+        len = pwrite(dst_fd, buffer, size, offset);
+        g_assert(len == size);
+    }
+
+    close(dst_fd);
+    close(iso_fd);
+}
+
+static void test_image(const char *command,
+                       const char *image, const char *iso,
+                       const char *kernel, const char *initrd,
+                       const char *cmdline, const char *config_file,
+                       bool pseries)
+{
+    char buffer[1024];
+    int fd;
+    pid_t pid;
+    int status, ret;
+    long max_cpus, max_mem;
+    int num_cpus, mem_mb;
+    struct sockaddr_un addr;
+    const char *tmp_kernel = "/tmp/test-vmlinuz";
+    const char *tmp_initrd = "/tmp/test-initrd";
+    const char *tmp_sock = "/tmp/test-linux.sock";
+    const char *nic_type, *blk_type, *cache_type, *disk_format, *aio_method;
+    const char *vga_type;
+    int connect_count;
+    WeightedChoice nic_types[] = {
+        { "e1000", 25 },
+        { "virtio", 50 },
+        { "rtl8139", 25 },
+        { }
+    };
+    WeightedChoice blk_types[] = {
+        { "virtio", 50 },
+        { "ide", 50 },
+        { }
+    };
+    WeightedChoice cache_types[] = {
+        { "none", 50 },
+        { "writethrough", 50 },
+        { }
+    };
+    WeightedChoice disk_formats[] = {
+        { "raw", 50 },
+        { "qcow2", 25 },
+        { "qed", 25 },
+        { }
+    };
+    WeightedChoice aio_methods[] = {
+        { "threads", 75 },
+        { "native", 25 },
+        { }
+    };
+    WeightedChoice vga_types[] = {
+        { "cirrus", 80 },
+        { "std", 20 },
+        { }
+    };
+
+    max_cpus = sysconf(_SC_NPROCESSORS_ONLN);
+    num_cpus = MIN(g_test_rand_int_range(1, max_cpus + 1), 8);
+
+    /* Don't use more than 1/2 of physical memory */
+    max_mem = (sysconf(_SC_PHYS_PAGES) * sysconf(_SC_PAGE_SIZE)) >> 20;
+    max_mem /= 2;
+
+    mem_mb = (g_test_rand_int_range(128, max_mem) + 7) & ~0x03;
+
+    nic_type = choose(nic_types);
+    blk_type = choose(blk_types);
+    cache_type = choose(cache_types);
+    disk_format = choose(disk_formats);
+    aio_method = choose(aio_methods);
+    vga_type = choose(vga_types);
+
+    if (pseries) {
+        nic_type = "ibmveth";
+        blk_type = "scsi";
+        vga_type = "none";
+    }
+
+    if (strcmp(cache_type, "none") != 0) {
+        aio_method = "threads";
+    }
+
+    g_test_message("Using %d VCPUS", num_cpus);
+    g_test_message("Using %d MB of RAM", mem_mb);
+    g_test_message("Using `%s' network card", nic_type);
+    g_test_message("Using `%s' block device, cache=`%s', format=`%s', aio=`%s'",
+                   blk_type, cache_type, disk_format, aio_method);
+    g_test_message("Using `%s' graphics card", vga_type);
+
+    copy_file_from_iso(iso, kernel, tmp_kernel);
+    copy_file_from_iso(iso, initrd, tmp_initrd);
+
+    pid = fork();
+    if (pid == 0) {
+        int status;
+
+        status = systemf("./qemu-img create -f %s %s 10G", disk_format, image);
+        if (status != 0) {
+            exit(WEXITSTATUS(status));
+        }
+
+        status = systemf("%s "
+                         "-drive file=%s,if=%s,cache=%s,aio=%s -cdrom %s "
+                         "-chardev socket,server=on,wait=on,id=httpd,path=%s "
+                         "-net user,guestfwd=tcp:10.0.2.1:80-chardev:httpd "
+                         "-net nic,model=%s "
+                         "-kernel %s -initrd %s "
+                         "-append '%s' -vga %s "
+                         "-serial stdio -vnc none -smp %d -m %d ",
+                         command, image, blk_type, cache_type, aio_method,
+                         iso, tmp_sock, nic_type, tmp_kernel, tmp_initrd,
+                         cmdline, vga_type, num_cpus, mem_mb);
+        unlink(image);
+
+        if (!WIFEXITED(status)) {
+            exit(1);
+        }
+
+        exit(WEXITSTATUS(status));
+    }
+
+    fd = socket(PF_UNIX, SOCK_STREAM, 0);
+    g_assert(fd != -1);
+
+    addr.sun_family = AF_UNIX;
+    snprintf(addr.sun_path, sizeof(addr.sun_path), "%s", tmp_sock);
+
+    connect_count = 0;
+    do {
+        ret = connect(fd, (struct sockaddr *)&addr, sizeof(addr));
+        if (ret == -1) {
+            usleep(100000);
+        }
+        connect_count++;
+    } while (ret == -1 && connect_count < 100);
+
+    g_assert(connect_count < 100);
+
+    ret = read(fd, buffer, 1);
+    g_assert(ret == 1);
+    snprintf(buffer, sizeof(buffer),
+             "HTTP/1.0 200 OK\r\n"
+             "Server: BaseHTTP/0.3 Python/2.6.5\r\n"
+             "Date: Wed, 30 Mar 2011 19:46:35 GMT\r\n"
+             "Content-type: text/plain\r\n"
+             "Content-length: %ld\r\n"
+             "\r\n", strlen(config_file));
+    ret = write(fd, buffer, strlen(buffer));
+    g_assert_cmpint(ret, ==, strlen(buffer));
+
+    ret = write(fd, config_file, strlen(config_file));
+    g_assert_cmpint(ret, ==, strlen(config_file));
+
+    ret = waitpid(pid, &status, 0);
+    g_assert(ret == pid);
+    g_assert(WIFEXITED(status));
+    g_assert_cmpint(WEXITSTATUS(status), ==, 3);
+
+    close(fd);
+}
+
+static void test_debian_ppc(gconstpointer data)
+{
+    const char *distro = data;
+    char image[1024];
+    char iso[1024];
+
+    snprintf(image, sizeof(image), "/tmp/debian-%s.img", distro);
+    snprintf(iso, sizeof(iso), "%s/isos/debian-%s-DVD-1.iso",
+             getenv("HOME"), distro);
+
+    test_image("ppc64-softmmu/qemu-system-ppc64 -M pseries ",
+               image, iso, "/install/powerpc64/vmlinux",
+               "/install/powerpc64/initrd.gz",
+               "priority=critical locale=en_US "
+               "url=http://10.0.2.1/server.cfg console=hvc0",
+               preseed, true);
+}
+
+static void test_ubuntu(gconstpointer data)
+{
+    const char *distro = data;
+    char image[1024];
+    char iso[1024];
+
+    snprintf(image, sizeof(image), "/tmp/ubuntu-%s.img", distro);
+    snprintf(iso, sizeof(iso), "%s/isos/ubuntu-%s.iso", getenv("HOME"), distro);
+
+    test_image("x86_64-softmmu/qemu-system-x86_64 -enable-kvm",
+               image, iso, "/install/vmlinuz", "/install/initrd.gz",
+               "priority=critical locale=en_US "
+               "url=http://10.0.2.1/server.cfg console=ttyS0",
+               preseed, false);
+}
+
+static void test_fedora(gconstpointer data)
+{
+    const char *distro = data;
+    char image[1024];
+    char iso[1024];
+
+    snprintf(image, sizeof(image), "/tmp/fedora-%s.img", distro);
+    snprintf(iso, sizeof(iso), "%s/isos/Fedora-%s-DVD.iso", getenv("HOME"), distro);
+
+    test_image("x86_64-softmmu/qemu-system-x86_64 -enable-kvm",
+               image, iso, "/isolinux/vmlinuz", "/isolinux/initrd.img",
+               "stage2=hd:LABEL=\"Fedora\" "
+               "ks=http://10.0.2.1/server.ks console=ttyS0",
+               ks, false);
+}
+
+int main(int argc, char **argv)
+{
+    g_test_init(&argc, &argv, NULL);
+
+    g_test_add_data_func("/fedora/14/x86_64", "14-x86_64", test_fedora);
+    g_test_add_data_func("/ubuntu/9.10/server/amd64", "9.10-server-amd64", test_ubuntu);
+    g_test_add_data_func("/ubuntu/10.04.2/server/amd64", "10.04.2-server-amd64", test_ubuntu);
+    g_test_add_data_func("/ubuntu/10.10/server/amd64", "10.10-server-amd64", test_ubuntu);
+    g_test_add_data_func("/debian/6.0.1a/powerpc", "6.0.1a-powerpc", test_debian_ppc);
+
+    g_test_run();
+
+    return 0;
+}