/* $Progeny$ */

/*
 * Copyright 2002 Progeny Linux Systems, Inc.
 * Copyright 2002 Hewlett-Packard Company
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * THE COPYRIGHT HOLDER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 */

#include <stdlib.h>
#include <string.h>
#include <check.h>

#include <discover/device.h>
#include <discover/discover.h>

struct test_data_structure {
    char *device_class;
    char *vendor_id;
    char *model_id;

    char *model_name;
    char *vendor_name;

    char *data_class;
    char *data_version;
    char *data_text;
};

struct test_data_structure test_data = {
    "display",
    "102b",
    "0010",
    "Millennium 4MB MGA-I [Impression?]",
    "Matrox Graphics, Inc.",
    "xfree86/server/device/driver",
    "4.1.1",
    "mga"
};

struct test_data_structure test_data2 = {
    "display",
    "102b",
    "1010",
    "Faux Millennium 8MB MGA-II",
    "Matrox Graphics, Inc.",
    "xfree86/server/device/driver",
    NULL,
    "mga"
};

struct test_data_structure test_data3 = {
    "display",
    "102b",
    "2020",
    "Faux^2 Millennium 8MB MGA-II",
    "Matrox Graphics, Inc.",
    "linux/module/name",
    "0.1",
    "Extra data",
};

struct test_data_structure test_data4 = {
    "display",
    "102b",
    "3030",
    "Faux Millennium^3",
    "Matrox Graphics, Inc.",
    NULL,
    NULL,
    NULL,
};

static discover_device_t *device;
static discover_error_t *status;

static bool
device_matches(discover_device_t *found, struct test_data_structure *target)
{
    return (strcmp(discover_device_get_vendor_id(found),
                   target->vendor_id) == 0
            && strcmp(discover_device_get_model_id(found),
                      target->model_id) == 0);
}

START_TEST(device_find)
{
    device = discover_device_find(test_data.device_class, status);
    fail_unless(status->code == 0, status->message);
}
END_TEST

START_TEST(device_get_vendor_id)
{
    device = discover_device_find(test_data.device_class, status);
    fail_unless(status->code == 0, status->message);

    fail_unless(strcmp(discover_device_get_vendor_id(device),
                       test_data.vendor_id) == 0, NULL);
}
END_TEST

START_TEST(device_get_model_id)
{
    device = discover_device_find(test_data.device_class, status);
    fail_unless(status->code == 0, status->message);

    fail_unless(strcmp(discover_device_get_model_id(device),
                       test_data.model_id) == 0, NULL);
}
END_TEST

START_TEST(device_get_vendor_name)
{
    device = discover_device_find(test_data.device_class, status);
    fail_unless(status->code == 0, status->message);

    fail_unless(strcmp(discover_device_get_vendor_name(device),
                       test_data.vendor_name) == 0, NULL);
}
END_TEST

START_TEST(device_get_model_name)
{
    device = discover_device_find(test_data.device_class, status);
    fail_unless(status->code == 0, status->message);

    fail_unless(strcmp(discover_device_get_model_name(device),
                       test_data.model_name) == 0, NULL);

}
END_TEST

START_TEST(device_get_data)
{
    char *data_string;

    device = discover_device_find(test_data.device_class, status);
    fail_unless(status->code == 0, status->message);

    while (device && !device_matches(device, &test_data)) {
        device = discover_device_get_next(device);
    }
    fail_unless(device != NULL, NULL);

    data_string = discover_device_get_data(device,
                                           test_data.data_class,
                                           test_data.data_version,
                                           status);
    fail_unless(status->code == 0, status->message);
    fail_unless(data_string != NULL, NULL);
    fail_unless(strcmp(data_string, test_data.data_text) == 0, NULL);
}
END_TEST

START_TEST(device_get_data_unknown_tag)
{
    char *data_string;

    device = discover_device_find(test_data2.device_class, status);
    fail_unless(status->code == 0, status->message);

    while (device && !device_matches(device, &test_data2)) {
        device = discover_device_get_next(device);
    }
    fail_unless(device != NULL, NULL);
    data_string = discover_device_get_data(device,
                                           test_data2.data_class,
                                           test_data2.data_version,
                                           status);
    fail_unless(status->code == 0, status->message);

    /* This time we want data_string to be NULL, because the data is
     * hidden in an unknown tag.
     */
    fail_unless(data_string == NULL, data_string);
}
END_TEST

START_TEST(device_test_version)
{
    char *data_string;

    device = discover_device_find(test_data3.device_class, status);
    fail_unless(status->code == 0, status->message);

    while (device && !device_matches(device, &test_data3)) {
        device = discover_device_get_next(device);
    }
    fail_unless(device != NULL, NULL);

    /*
     * We should have extra information, specifically an extra device
     * structure.
     */
    fail_unless(device->extra != NULL, NULL);

    data_string = discover_device_get_data(device,
                                           test_data3.data_class,
                                           test_data3.data_version,
                                           status);
    fail_unless(status->code == 0, status->message);
    fail_unless(data_string != NULL, NULL);
    fail_unless(strcmp(data_string, test_data3.data_text) == 0, NULL);
}
END_TEST

static void
recurse_node(discover_data_t *node, char **buffer)
{
    if (node) {
        **buffer = node->discover_class[0];
        (*buffer)++;
        recurse_node(node->child, buffer);
        recurse_node(node->next, buffer);
    }
}

START_TEST(device_test_internals)
{
    char *buffer = calloc(500, 1);
    char *bufptr = buffer;

    device = discover_device_find(test_data4.device_class, status);
    fail_unless(status->code == 0, status->message);

    while (device && !device_matches(device, &test_data4)) {
        device = discover_device_get_next(device);
    }
    fail_unless(device != NULL, NULL);

    recurse_node(device->data, &buffer);
    fail_unless(strcmp(bufptr, "This is a depth-first traversal") == 0, bufptr);
}
END_TEST

Suite *
make_device_suite(void)
{
    Suite *s;
    TCase *tc_core;

    status = discover_error_new();

    tc_core = tcase_create("core");
    tcase_add_test(tc_core, device_find);
    tcase_add_test(tc_core, device_get_vendor_id);
    tcase_add_test(tc_core, device_get_model_id);
    tcase_add_test(tc_core, device_get_vendor_name);
    tcase_add_test(tc_core, device_get_model_id);
    tcase_add_test(tc_core, device_get_model_name);
    tcase_add_test(tc_core, device_get_data);
    tcase_add_test(tc_core, device_get_data_unknown_tag);
    tcase_add_test(tc_core, device_test_version);
    tcase_add_test(tc_core, device_test_internals);

    s = suite_create("device");
    suite_add_tcase(s, tc_core);

    return s;
}
