initial load of upstream version 1.06.32
[xmlrpc-c] / src / test / test.c
diff --git a/src/test/test.c b/src/test/test.c
new file mode 100644 (file)
index 0000000..80511f7
--- /dev/null
@@ -0,0 +1,741 @@
+/* Copyright information is at the end of the file. */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <limits.h>
+
+#include "casprintf.h"
+
+#include "xmlrpc_config.h"
+
+#include "xmlrpc-c/base.h"
+#include "xmlrpc-c/server.h"
+
+#include "test.h"
+#include "value.h"
+#include "serialize.h"
+#include "parse_xml.h"
+#include "cgi.h"
+#include "xml_data.h"
+#include "client.h"
+#include "server_abyss.h"
+#include "method_registry.h"
+
+/*=========================================================================
+**  Test Harness
+**=========================================================================
+**  This is a super light-weight test harness. It's vaguely inspired by
+**  Kent Beck's book on eXtreme Programming (XP)--the output is succinct,
+**  new tests can be coded quickly, and the whole thing runs in a few
+**  second's time.
+**
+**  To run the tests, type './rpctest'.
+**  To check for memory leaks, install RedHat's 'memprof' utility, and
+**  type 'memprof rpctest'.
+**
+**  If you add new tests to this file, please deallocate any data
+**  structures you use in the appropriate fashion. This allows us to test
+**  various destructor code for memory leaks.
+*/
+
+int total_tests = 0;
+int total_failures = 0;
+
+
+/*=========================================================================
+**  Test Data
+**=========================================================================
+**  Some common test data which need to be allocated at a fixed address,
+**  or which are inconvenient to allocate inline.
+*/
+
+static char* test_string_1 = "foo";
+static char* test_string_2 = "bar";
+static int test_int_array_1[5] = {1, 2, 3, 4, 5};
+static int test_int_array_2[3] = {6, 7, 8};
+static int test_int_array_3[8] = {1, 2, 3, 4, 5, 6, 7, 8};
+
+/*=========================================================================
+**  Test Suites
+**=========================================================================
+*/
+
+static void test_env(void)
+{
+    xmlrpc_env env, env2;
+    char *s;
+
+    /* Test xmlrpc_env_init. */
+    xmlrpc_env_init(&env);
+    TEST(!env.fault_occurred);
+    TEST(env.fault_code == 0);
+    TEST(env.fault_string == NULL);
+
+    /* Test xmlrpc_set_fault. */
+    xmlrpc_env_set_fault(&env, 1, test_string_1);
+    TEST(env.fault_occurred);
+    TEST(env.fault_code == 1);
+    TEST(env.fault_string != test_string_1);
+    TEST(strcmp(env.fault_string, test_string_1) == 0);
+
+    /* Change an existing fault. */
+    xmlrpc_env_set_fault(&env, 2, test_string_2);
+    TEST(env.fault_occurred);
+    TEST(env.fault_code == 2);
+    TEST(strcmp(env.fault_string, test_string_2) == 0);    
+
+    /* Set a fault with a format string. */
+    xmlrpc_env_set_fault_formatted(&env, 3, "a%s%d", "bar", 9);
+    TEST(env.fault_occurred);
+    TEST(env.fault_code == 3);
+    TEST(strcmp(env.fault_string, "abar9") == 0);
+
+    /* Set a fault with an oversized string. */
+    s = "12345678901234567890123456789012345678901234567890";
+    xmlrpc_env_set_fault_formatted(&env, 4, "%s%s%s%s%s%s", s, s, s, s, s, s);
+    TEST(env.fault_occurred);
+    TEST(env.fault_code == 4);
+    TEST(strlen(env.fault_string) == 255);
+
+    /* Test cleanup code (with help from memprof). */
+    xmlrpc_env_clean(&env);
+
+    /* Test cleanup code on in absence of xmlrpc_env_set_fault. */
+    xmlrpc_env_init(&env2);
+    xmlrpc_env_clean(&env2);
+}
+
+static void test_mem_block (void)
+{
+    xmlrpc_env env;
+    xmlrpc_mem_block* block;
+
+    xmlrpc_mem_block* typed_heap_block;
+    xmlrpc_mem_block typed_auto_block;
+    void** typed_contents;
+
+    xmlrpc_env_init(&env);
+
+    /* Allocate a zero-size block. */
+    block = xmlrpc_mem_block_new(&env, 0);
+    TEST_NO_FAULT(&env);
+    TEST(block != NULL);
+    TEST(xmlrpc_mem_block_size(block) == 0);
+
+    /* Grow the block a little bit. */
+    xmlrpc_mem_block_resize(&env, block, strlen(test_string_1) + 1);
+    TEST_NO_FAULT(&env);
+    TEST(xmlrpc_mem_block_size(block) == strlen(test_string_1) + 1);
+    
+    /* Insert a string into the block, and resize it by large amount.
+    ** We want to cause a reallocation and copy of the block contents. */
+    strcpy(xmlrpc_mem_block_contents(block), test_string_1);
+    xmlrpc_mem_block_resize(&env, block, 10000);
+    TEST_NO_FAULT(&env);
+    TEST(xmlrpc_mem_block_size(block) == 10000);
+    TEST(strcmp(xmlrpc_mem_block_contents(block), test_string_1) == 0);
+
+    /* Test cleanup code (with help from memprof). */
+    xmlrpc_mem_block_free(block);
+    
+    /* Allocate a bigger block. */
+    block = xmlrpc_mem_block_new(&env, 128);
+    TEST_NO_FAULT(&env);
+    TEST(block != NULL);
+    TEST(xmlrpc_mem_block_size(block) == 128);
+
+    /* Test cleanup code (with help from memprof). */
+    xmlrpc_mem_block_free(block);
+
+    /* Allocate a "typed" memory block. */
+    typed_heap_block = XMLRPC_TYPED_MEM_BLOCK_NEW(void*, &env, 20);
+    TEST_NO_FAULT(&env);
+    TEST(typed_heap_block != NULL);
+    TEST(XMLRPC_TYPED_MEM_BLOCK_SIZE(void*, typed_heap_block) == 20);
+    typed_contents = XMLRPC_TYPED_MEM_BLOCK_CONTENTS(void*, typed_heap_block);
+    TEST(typed_contents != NULL);
+
+    /* Resize a typed memory block. */
+    XMLRPC_TYPED_MEM_BLOCK_RESIZE(void*, &env, typed_heap_block, 100);
+    TEST_NO_FAULT(&env);
+    TEST(XMLRPC_TYPED_MEM_BLOCK_SIZE(void*, typed_heap_block) == 100);
+
+    /* Test cleanup code (with help from memprof). */
+    XMLRPC_TYPED_MEM_BLOCK_FREE(void*, typed_heap_block);
+
+    /* Test _INIT and _CLEAN for stack-based memory blocks. */
+    XMLRPC_TYPED_MEM_BLOCK_INIT(void*, &env, &typed_auto_block, 30);
+    TEST(XMLRPC_TYPED_MEM_BLOCK_SIZE(void*, &typed_auto_block) == 30);
+    XMLRPC_TYPED_MEM_BLOCK_CLEAN(void*, &typed_auto_block);
+
+    /* Test xmlrpc_mem_block_append. */
+    block = XMLRPC_TYPED_MEM_BLOCK_NEW(int, &env, 5);
+    TEST_NO_FAULT(&env);
+    memcpy(XMLRPC_TYPED_MEM_BLOCK_CONTENTS(int, block),
+           test_int_array_1, sizeof(test_int_array_1));
+    XMLRPC_TYPED_MEM_BLOCK_APPEND(int, &env, block, test_int_array_2, 3);
+    TEST(XMLRPC_TYPED_MEM_BLOCK_SIZE(int, block) == 8);
+    TEST(memcmp(XMLRPC_TYPED_MEM_BLOCK_CONTENTS(int, block),
+                test_int_array_3, sizeof(test_int_array_3)) == 0);
+    XMLRPC_TYPED_MEM_BLOCK_FREE(int, block);
+
+    xmlrpc_env_clean(&env);
+}
+
+static char *(base64_triplets[]) = {
+    "", "", "\r\n",
+    "a", "YQ==", "YQ==\r\n",
+    "aa", "YWE=", "YWE=\r\n",
+    "aaa", "YWFh", "YWFh\r\n",
+    "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
+    "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ",
+    "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXpBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWmFiY"
+    "2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6QUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVo=",
+    "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXpBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWmFiY"
+    "2Rl\r\n"
+    "ZmdoaWprbG1ub3BxcnN0dXZ3eHl6QUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVo=\r\n",
+    NULL};
+
+static void
+test_base64_conversion(void) {
+    xmlrpc_env env;
+    char ** triplet;
+
+    xmlrpc_env_init(&env);
+
+    for (triplet = base64_triplets; *triplet != NULL; triplet += 3) {
+        char * bin_data;
+        char * nocrlf_ascii_data;
+        char * ascii_data;
+        xmlrpc_mem_block * output;
+
+        bin_data = *triplet;
+        nocrlf_ascii_data = *(triplet + 1);
+        ascii_data = *(triplet + 2);
+
+        /* Test our encoding routine. */
+        output = xmlrpc_base64_encode(&env,
+                                      (unsigned char*) bin_data,
+                                      strlen(bin_data));
+        TEST_NO_FAULT(&env);
+        TEST(output != NULL);
+        TEST(xmlrpc_mem_block_size(output) == strlen(ascii_data));
+        TEST(memcmp(xmlrpc_mem_block_contents(output), ascii_data,
+                    strlen(ascii_data)) == 0);
+        xmlrpc_mem_block_free(output);
+
+        /* Test our newline-free encoding routine. */
+        output =
+            xmlrpc_base64_encode_without_newlines(&env,
+                                                  (unsigned char*) bin_data,
+                                                  strlen(bin_data));
+        TEST_NO_FAULT(&env);
+        TEST(output != NULL);
+        TEST(xmlrpc_mem_block_size(output) == strlen(nocrlf_ascii_data));
+        TEST(memcmp(xmlrpc_mem_block_contents(output), nocrlf_ascii_data,
+                    strlen(nocrlf_ascii_data)) == 0);
+        xmlrpc_mem_block_free(output);
+
+        /* Test our decoding routine. */
+        output = xmlrpc_base64_decode(&env, ascii_data, strlen(ascii_data));
+        TEST_NO_FAULT(&env);
+        TEST(output != NULL);
+        TEST(xmlrpc_mem_block_size(output) == strlen(bin_data));
+        TEST(memcmp(xmlrpc_mem_block_contents(output), bin_data,
+                    strlen(bin_data)) == 0);
+        xmlrpc_mem_block_free(output);
+    }
+
+    /* Now for something broken... */
+    {
+        xmlrpc_env env2;
+        xmlrpc_mem_block * output;
+
+        xmlrpc_env_init(&env2);
+        output = xmlrpc_base64_decode(&env2, "====", 4);
+        TEST(output == NULL);
+        TEST_FAULT(&env2, XMLRPC_PARSE_ERROR);
+        xmlrpc_env_clean(&env2);
+    }
+    /* Now for something broken in a really sneaky way... */
+    {
+        xmlrpc_env env2;
+        xmlrpc_mem_block * output;
+        xmlrpc_env_init(&env2);
+        output = xmlrpc_base64_decode(&env2, "a==", 4);
+        TEST(output == NULL);
+        TEST_FAULT(&env2, XMLRPC_PARSE_ERROR);
+        xmlrpc_env_clean(&env2);
+    }
+    xmlrpc_env_clean(&env);
+}
+
+
+
+static void test_bounds_checks (void)
+{
+    xmlrpc_env env;
+    xmlrpc_value *array;
+    int i1, i2, i3, i4;
+
+    /* Get an array to work with. */
+    xmlrpc_env_init(&env);
+    array = xmlrpc_build_value(&env, "(iii)", 100, 200, 300);
+    TEST_NO_FAULT(&env);
+    xmlrpc_env_clean(&env);
+    
+    /* Test xmlrpc_decompose_value with too few values. */
+    xmlrpc_env_init(&env);
+    xmlrpc_decompose_value(&env, array, "(iiii)", &i1, &i2, &i3, &i4);
+    TEST_FAULT(&env, XMLRPC_INDEX_ERROR);
+    xmlrpc_env_clean(&env);
+
+    /* Test xmlrpc_decompose_value with too many values. */
+    xmlrpc_env_init(&env);
+    xmlrpc_decompose_value(&env, array, "(ii)", &i1, &i2, &i3, &i4);
+    TEST_FAULT(&env, XMLRPC_INDEX_ERROR);
+    xmlrpc_env_clean(&env);
+
+    /* Dispose of our array. */
+    xmlrpc_DECREF(array);
+}
+
+
+
+static void test_nesting_limit (void)
+{
+    xmlrpc_env env;
+    xmlrpc_value *val;
+
+    xmlrpc_env_init(&env);
+    
+    /* Test with an adequate limit for a result value which is an
+       array which contains an element which is a struct, whose values
+       are simple: 3.
+    */
+    xmlrpc_limit_set(XMLRPC_NESTING_LIMIT_ID, 3);
+    val = xmlrpc_parse_response(&env,
+                                good_response_xml, strlen(good_response_xml));
+    TEST_NO_FAULT(&env);
+    TEST(val != NULL);
+    xmlrpc_DECREF(val);
+
+    /* Test with an inadequate limit. */
+    xmlrpc_limit_set(XMLRPC_NESTING_LIMIT_ID, 2);
+    val = xmlrpc_parse_response(&env,
+                                good_response_xml, strlen(good_response_xml));
+    TEST_FAULT(&env, XMLRPC_PARSE_ERROR); /* BREAKME - Will change. */
+    TEST(val == NULL);
+
+    /* Reset the default limit. */
+    xmlrpc_limit_set(XMLRPC_NESTING_LIMIT_ID, XMLRPC_NESTING_LIMIT_DEFAULT);
+    TEST(xmlrpc_limit_get(XMLRPC_NESTING_LIMIT_ID)
+         == XMLRPC_NESTING_LIMIT_DEFAULT);
+
+    xmlrpc_env_clean(&env);
+}
+
+
+
+static void
+test_xml_size_limit(void) {
+
+    xmlrpc_env env;
+    const char * methodName;
+    xmlrpc_value * paramsP;
+    
+    /* NOTE - This test suite only verifies the last-ditch size-checking
+       code.  There should also be matching code in all server (and
+       preferably all client) modules as well.
+    */
+
+    /* Set our XML size limit to something ridiculous. */
+    xmlrpc_limit_set(XMLRPC_XML_SIZE_LIMIT_ID, 6);
+    
+    /* Attempt to parse a call. */
+    xmlrpc_env_init(&env);
+    xmlrpc_parse_call(&env, serialized_call, strlen(serialized_call),
+                      &methodName, &paramsP);
+    TEST_FAULT(&env, XMLRPC_LIMIT_EXCEEDED_ERROR);
+    xmlrpc_env_clean(&env);
+
+    {
+        xmlrpc_value * resultP;
+        int faultCode;
+        const char * faultString;
+
+        /* Attempt to parse a response. */
+        xmlrpc_env_init(&env);
+        xmlrpc_parse_response2(&env,
+                               good_response_xml, strlen(good_response_xml),
+                               &resultP, &faultCode, &faultString);
+        TEST_FAULT(&env, XMLRPC_LIMIT_EXCEEDED_ERROR);
+        xmlrpc_env_clean(&env);
+    }
+    /* Reset the default limit. */
+    xmlrpc_limit_set(XMLRPC_XML_SIZE_LIMIT_ID, XMLRPC_XML_SIZE_LIMIT_DEFAULT);
+}
+
+
+
+/*=========================================================================
+**  test_sample_files
+**=========================================================================
+**  Read in a bunch of sample test files and make sure we get plausible
+**  results.
+**
+**  We use these files to test strange-but-legal encodings, illegal-but-
+**  supported encodings, etc.
+*/
+
+#define TESTDATA_DIR "data"
+
+static char *good_requests[] = {
+    TESTDATA_DIR DIRECTORY_SEPARATOR "req_out_of_order.xml",
+    TESTDATA_DIR DIRECTORY_SEPARATOR "req_no_params.xml",
+    TESTDATA_DIR DIRECTORY_SEPARATOR "req_value_name.xml",
+    NULL
+};
+
+#define MAX_SAMPLE_FILE_LEN (16 * 1024)
+
+static char file_buff [MAX_SAMPLE_FILE_LEN];
+
+static void
+read_file (char *path, char **out_data, size_t *out_size)
+{
+    FILE *f;
+    size_t bytes_read;
+
+    /* Open the file. */
+    f = fopen(path, "r");
+    if (f == NULL) {
+        /* Since this error is fairly likely to happen, give an
+        ** informative error message... */
+        fflush(stdout);
+        fprintf(stderr, "Could not open file '%s'.  errno=%d (%s)\n", 
+                path, errno, strerror(errno));
+        abort();
+    }
+    
+    /* Read in one buffer full of data, and make sure that everything
+    ** fit.  (We perform a lazy error/no-eof/zero-length-file test using
+    ** bytes_read.) */
+    bytes_read = fread(file_buff, sizeof(char), MAX_SAMPLE_FILE_LEN, f);
+    TEST(0 < bytes_read && bytes_read < MAX_SAMPLE_FILE_LEN);
+
+    /* Close the file and return our data. */
+    fclose(f);
+    *out_data = file_buff;
+    *out_size = bytes_read;
+}
+
+static void test_sample_files (void)
+{
+    xmlrpc_env env;
+    char **paths, *path;
+    char *data;
+    size_t data_len;
+    const char *method_name;
+    xmlrpc_value *params;
+
+    xmlrpc_env_init(&env);
+
+    /* Test our good requests. */
+    for (paths = good_requests; *paths != NULL; paths++) {
+        path = *paths;
+        read_file(path, &data, &data_len);
+        xmlrpc_parse_call(&env, data, data_len, &method_name, &params);
+        TEST_NO_FAULT(&env);
+        strfree(method_name);
+        xmlrpc_DECREF(params);
+    }
+
+    xmlrpc_env_clean(&env);
+}
+
+
+/*=========================================================================
+**  test_utf8_coding
+**=========================================================================
+**  We need to test our UTF-8 decoder thoroughly.  Most of these test
+**  cases are taken from the UTF-8-test.txt file by Markus Kuhn
+**  <mkuhn@acm.org>:
+**      http://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt
+*/
+
+#if HAVE_UNICODE_WCHAR
+
+typedef struct {
+    char *utf8;
+    wchar_t wcs[16];
+} utf8_and_wcs;
+
+static utf8_and_wcs good_utf8[] = {
+
+    /* Greek 'kosme'. */
+    {"\316\272\341\275\271\317\203\316\274\316\265",
+     {0x03BA, 0x1F79, 0x03C3, 0x03BC, 0x03B5, 0}},
+
+    /* First sequences of a given length. */
+    /* '\000' is not a legal C string. */
+    {"\302\200", {0x0080, 0}},
+    {"\340\240\200", {0x0800, 0}},
+
+    /* Last sequences of a given length. */
+    {"\177", {0x007F, 0}},
+    {"\337\277", {0x07FF, 0}},
+    /* 0xFFFF is not a legal Unicode character. */
+
+    /* Other boundry conditions. */
+    {"\001", {0x0001, 0}},
+    {"\355\237\277", {0xD7FF, 0}},
+    {"\356\200\200", {0xE000, 0}},
+    {"\357\277\275", {0xFFFD, 0}},
+
+    /* Other random test cases. */
+    {"", {0}},
+    {"abc", {0x0061, 0x0062, 0x0063, 0}},
+    {"[\302\251]", {0x005B, 0x00A9, 0x005D, 0}},
+    
+    {NULL, {0}}
+};
+
+static char *(bad_utf8[]) = {
+
+    /* Continuation bytes. */
+    "\200", "\277",
+
+    /* Lonely start characters. */
+    "\300", "\300x", "\300xx",
+    "\340", "\340x", "\340xx", "\340xxx",
+
+    /* Last byte missing. */
+    "\340\200", "\340\200x", "\340\200xx",
+    "\357\277", "\357\277x", "\357\277xx",
+
+    /* Illegal bytes. */
+    "\376", "\377",
+
+    /* Overlong '/'. */
+    "\300\257", "\340\200\257",
+
+    /* Overlong ASCII NUL. */
+    "\300\200", "\340\200\200",
+
+    /* Maximum overlong sequences. */
+    "\301\277", "\340\237\277",
+
+    /* Illegal code positions. */
+    "\357\277\276", /* U+FFFE */
+    "\357\277\277", /* U+FFFF */
+
+    /* UTF-16 surrogates (unpaired and paired). */
+    "\355\240\200",
+    "\355\277\277",
+    "\355\240\200\355\260\200",
+    "\355\257\277\355\277\277",
+
+    /* Valid UCS-4 characters (we don't handle these yet).
+    ** On systems with UCS-4 or UTF-16 wchar_t values, we
+    ** may eventually handle these in some fashion. */
+    "\360\220\200\200",
+    "\370\210\200\200\200",
+    "\374\204\200\200\200\200",
+
+    NULL
+};
+#endif  /* HAVE_UNICODE_WCHAR */
+
+/* This routine is missing on certain platforms.  This implementation
+** *appears* to be correct. */
+#if 0
+#ifndef HAVE_WCSNCMP
+int wcsncmp(wchar_t *wcs1, wchar_t* wcs2, size_t len)
+{
+    size_t i;
+    /* XXX - 'unsigned long' should be 'uwchar_t'. */
+    unsigned long c1, c2;
+    for (i=0; i < len; i++) {
+        c1 = wcs1[i];
+        c2 = wcs2[i];
+        /* This clever comparison borrowed from the GNU C Library. */
+        if (c1 == 0 || c1 != c2)
+            return c1 - c2;
+    }
+    return 0;
+}
+#endif /* HAVE_WCSNCMP */
+#endif
+
+static void
+test_utf8_coding(void) {
+
+#if HAVE_UNICODE_WCHAR
+    xmlrpc_env env, env2;
+    utf8_and_wcs *good_data;
+    char **bad_data;
+    char *utf8;
+    wchar_t *wcs;
+    xmlrpc_mem_block *output;
+
+    xmlrpc_env_init(&env);
+
+    /* Test each of our valid UTF-8 sequences. */
+    for (good_data = good_utf8; good_data->utf8 != NULL; good_data++) {
+        utf8 = good_data->utf8;
+        wcs = good_data->wcs;
+
+        /* Attempt to validate the UTF-8 string. */
+        xmlrpc_validate_utf8(&env, utf8, strlen(utf8));
+        TEST_NO_FAULT(&env);
+
+        /* Attempt to decode the UTF-8 string. */
+        output = xmlrpc_utf8_to_wcs(&env, utf8, strlen(utf8));
+        TEST_NO_FAULT(&env);
+        TEST(output != NULL);
+        TEST(wcslen(wcs) == XMLRPC_TYPED_MEM_BLOCK_SIZE(wchar_t, output));
+        TEST(0 ==
+             wcsncmp(wcs, XMLRPC_TYPED_MEM_BLOCK_CONTENTS(wchar_t, output),
+                     wcslen(wcs)));
+        xmlrpc_mem_block_free(output);
+
+        /* Test the UTF-8 encoder, too. */
+        output = xmlrpc_wcs_to_utf8(&env, wcs, wcslen(wcs));
+        TEST_NO_FAULT(&env);
+        TEST(output != NULL);
+        TEST(strlen(utf8) == XMLRPC_TYPED_MEM_BLOCK_SIZE(char, output));
+        TEST(0 ==
+             strncmp(utf8, XMLRPC_TYPED_MEM_BLOCK_CONTENTS(char, output),
+                     strlen(utf8)));
+        xmlrpc_mem_block_free(output);
+    }
+
+    /* Test each of our illegal UTF-8 sequences. */
+    for (bad_data = bad_utf8; *bad_data != NULL; bad_data++) {
+        utf8 = *bad_data;
+    
+        /* Attempt to validate the UTF-8 string. */
+        xmlrpc_env_init(&env2);
+        xmlrpc_validate_utf8(&env2, utf8, strlen(utf8));
+        TEST_FAULT(&env2, XMLRPC_INVALID_UTF8_ERROR);
+        /* printf("Fault: %s\n", env2.fault_string); --Hand-checked */
+        xmlrpc_env_clean(&env2);
+
+        /* Attempt to decode the UTF-8 string. */
+        xmlrpc_env_init(&env2);
+        output = xmlrpc_utf8_to_wcs(&env2, utf8, strlen(utf8));
+        TEST_FAULT(&env2, XMLRPC_INVALID_UTF8_ERROR);
+        TEST(output == NULL);
+        xmlrpc_env_clean(&env2);
+    }
+    xmlrpc_env_clean(&env);
+#endif  /* HAVE_UNICODE_WCHAR */
+}
+
+
+
+static void
+test_server_cgi_maybe(void) {
+
+#ifndef WIN32
+
+    test_server_cgi();
+
+#endif 
+}
+
+
+
+static void
+test_client_maybe(void) {
+
+#ifndef WIN32 /* Must get Windows Curl transport working for this to work */
+
+    test_client();
+
+#endif 
+}
+
+
+
+int 
+main(int     argc, 
+     char ** argv ATTR_UNUSED) {
+
+    int retval;
+
+    if (argc-1 > 0) {
+        fprintf(stderr, "There are no arguments.");
+        retval = 1;
+    } else {
+        /* Add your test suites here. */
+        test_env();
+        test_mem_block();
+        test_base64_conversion();
+        printf("\n");
+        test_value();
+        test_bounds_checks();
+        printf("\n");
+        test_serialize();
+        test_parse_xml();
+        test_method_registry();
+        test_nesting_limit();
+        test_xml_size_limit();
+        test_sample_files();
+        printf("\n");
+        test_server_cgi_maybe();
+        test_server_abyss();
+
+        test_utf8_coding();
+
+        printf("\n");
+
+        test_client_maybe();
+
+        /* Summarize our test run. */
+        printf("\nRan %d tests, %d failed, %.1f%% passed\n",
+               total_tests, total_failures,
+               100.0 - (100.0 * total_failures) / total_tests);
+
+        /* Print the final result. */
+        if (total_failures == 0) {
+            printf("OK\n");
+            retval = 0;
+        } else {
+            retval = 1;
+            printf("FAILED\n");
+        }
+    }
+    return retval;
+}
+
+
+
+
+/* Copyright (C) 2001 by First Peer, Inc. All rights reserved.
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions
+** are met:
+** 1. Redistributions of source code must retain the above copyright
+**    notice, this list of conditions and the following disclaimer.
+** 2. Redistributions in binary form must reproduce the above copyright
+**    notice, this list of conditions and the following disclaimer in the
+**    documentation and/or other materials provided with the distribution.
+** 3. The name of the author may not be used to endorse or promote products
+**    derived from this software without specific prior written permission. 
+**  
+** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+** ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+** SUCH DAMAGE. */