/*
* %CopyrightBegin%
*
* Copyright Ericsson AB 2005-2009. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
* compliance with the License. You should have received a copy of the
* Erlang Public License along with this software. If not, it can be
* retrieved online at http://www.erlang.org/.
*
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
* the License for the specific language governing rights and limitations
* under the License.
*
* %CopyrightEnd%
*/
/*
* Description: Test suite for the ethread thread library.
* Author: Rickard Green
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <limits.h>
#include <float.h>
#ifndef __WIN32__
#include <unistd.h>
#endif
#include "erl_printf.h"
#ifdef THREAD_SAFE
#include "ethread.h"
#endif
#ifdef __WIN32__
#undef HAVE_VSNPRINTF
#define HAVE_VSNPRINTF 1
#define vsnprintf _vsnprintf
#endif
#ifdef __WIN32__
#define signed_long_long __int64
#define unsigned_long_long unsigned __int64
#else
#define signed_long_long signed long long
#define unsigned_long_long unsigned long long
#endif
/*
* Auxiliary functions
*/
#define PRINT_VA_LIST(FRMT) \
do { \
if (FRMT && FRMT != '\0') { \
va_list args; \
va_start(args, FRMT); \
vfprintf(stderr, FRMT, args); \
va_end(args); \
} \
} while (0)
#define ASSERT(B) \
do { \
if (!(B)) \
fail("%s:%d: Assertion \"%s\" failed!",__FILE__,__LINE__,#B); \
} while (0)
static void
print_eol(void)
{
fprintf(stderr, "\n");
}
static void print_line(char *frmt,...)
{
PRINT_VA_LIST(frmt);
print_eol();
}
static void print(char *frmt,...)
{
PRINT_VA_LIST(frmt);
}
static void fail(char *frmt,...)
{
char *abrt_env;
print_eol();
fprintf(stderr, "EP-TEST-FAILURE");
PRINT_VA_LIST(frmt);
print_eol();
abrt_env = getenv("ERL_ABORT_ON_FAILURE");
if (abrt_env && strcmp("true", abrt_env) == 0)
abort();
else
exit(1);
}
static void skip(char *frmt,...)
{
print_eol();
fprintf(stderr, "EP-TEST-SKIP");
PRINT_VA_LIST(frmt);
print_eol();
exit(0);
}
static void succeed(char *frmt,...)
{
print_eol();
fprintf(stderr, "EP-TEST-SUCCESS");
PRINT_VA_LIST(frmt);
print_eol();
exit(0);
}
#if 0 /* Not used */
static void
do_sleep(unsigned secs)
{
#ifdef __WIN32__
Sleep((DWORD) secs*1000);
#else
sleep(secs);
#endif
}
#endif
static void
send_my_pid(void)
{
#ifndef __WIN32__
int pid = (int) getpid();
fprintf(stderr, "\nEP-TEST-PID%d\n", pid);
#endif
}
#define BUF_SIZE (1024*1024)
FILE *outfile = NULL;
char **expected_result;
#define FENCE_SIZE 512
static void
print_cmp_test(int n, char *frmt, ...)
{
int res = -1;
static char clib_buf[BUF_SIZE];
static unsigned char the_erts_buf[BUF_SIZE];
char *erts_buf = (char *) &the_erts_buf[FENCE_SIZE];
va_list args;
if (outfile) {
char *fp, *tp;
va_start(args, frmt);
if (n < 0)
res = vsprintf(erts_buf, frmt, args);
else {
#ifdef HAVE_VSNPRINTF
res = vsnprintf(erts_buf, (size_t) n, frmt, args);
#else
fail("No vsnprintf()");
#endif
}
va_end(args);
ASSERT(res >= 0);
fp = erts_buf;
tp = clib_buf;
while (*fp) {
switch (*fp) {
case '\a': *(tp++) = '\\'; *(tp++) = 'a'; break;
case '\b': *(tp++) = '\\'; *(tp++) = 'b'; break;
case '\f': *(tp++) = '\\'; *(tp++) = 'f'; break;
case '\n': *(tp++) = '\\'; *(tp++) = 'n'; break;
case '\r': *(tp++) = '\\'; *(tp++) = 'r'; break;
case '\t': *(tp++) = '\\'; *(tp++) = 't'; break;
case '\v': *(tp++) = '\\'; *(tp++) = 'v'; break;
case '\"': *(tp++) = '\\'; *(tp++) = '\"'; break;
case '\\': *(tp++) = '\\'; *(tp++) = '\\'; break;
default: *(tp++) = *fp; break;
}
fp++;
}
*tp = '\0';
res = fprintf(outfile, "\t\"%s\",\n", clib_buf);
ASSERT(res >= 0);
}
else {
char *xres;
va_start(args, frmt);
if (n < 0)
res = erts_vsprintf(erts_buf, frmt, args);
else {
int i;
int chk_sz = 2*FENCE_SIZE + n;
for (i = 0; i < chk_sz; i++)
the_erts_buf[i] = 0xeb;
res = erts_vsnprintf(erts_buf, (size_t) n, frmt, args);
for (i = 0; i < chk_sz; i++)
if ((((char *) &the_erts_buf[i]) < erts_buf
|| erts_buf + n <= ((char *) &the_erts_buf[i]))
&& the_erts_buf[i] != 0xeb) {
int j;
for (j = 0; j < chk_sz; j++)
print(j ? ",%x(%d)" : "%x(%d)",
(unsigned) the_erts_buf[j], j - FENCE_SIZE);
print_eol();
fail("Garbage written out of bounds (%d,%d)",
i - FENCE_SIZE, n);
}
}
va_end(args);
ASSERT(res >= 0);
if (expected_result) {
ASSERT(*expected_result);
xres = *expected_result;
expected_result++;
}
else {
va_start(args, frmt);
if (n < 0)
res = vsprintf(clib_buf, frmt, args);
else {
#ifdef HAVE_VSNPRINTF
res = vsnprintf(clib_buf, (size_t) n, frmt, args);
#else
fail("No vsnprintf()");
#endif
}
va_end(args);
ASSERT(res >= 0);
xres = clib_buf;
}
if (strcmp(xres, erts_buf) != 0) {
print_line("expected result : \"%s\"", xres);
print_line("erts_buf : \"%s\"", erts_buf);
fail("\"%s\" != \"%s\" (format=\"%s\")", xres, erts_buf, frmt);
}
print_line("Checked format \"%s\" with result: \"%s\"", frmt, erts_buf);
}
}
/*
* The test-cases
*/
#include "integer_64_test.h"
#include "integer_test.h"
#define INT_SUB_BATCH_TEST(FRMT, TYPE) \
print_cmp_test(-1, FRMT, ((TYPE) 4711)); \
print_cmp_test(-1, FRMT, ~((TYPE) 4711)); \
print_cmp_test(-1, FRMT, (~((TYPE) 0))/2 + (~((TYPE) 0))/4);\
print_cmp_test(-1, FRMT, ((TYPE) - 1)); \
print_cmp_test(-1, FRMT, ((TYPE) 1)); \
print_cmp_test(-1, FRMT, ((TYPE) ((long) 0xabcdef01))); \
#define INT_BATCH_TEST(P, X, S) \
print_line("%s:%d",__FILE__,__LINE__); \
INT_SUB_BATCH_TEST("%" P "h" X, S char); \
INT_SUB_BATCH_TEST("%" P "h" X, S short); \
INT_SUB_BATCH_TEST("%" P X, S int); \
INT_SUB_BATCH_TEST("%" P "l" X, S long); \
INT_SUB_BATCH_TEST("%" P "ll" X, S ## _long_long); \
static void
integer_test(void)
{
/* This testcase should be rewritten. It assumes the following
sizes of types... */
if (sizeof(char) != 1
|| sizeof(short) != 2
|| sizeof(int) != 4
|| sizeof(long) != (sizeof(void *) == 8 ? 8 : 4)
|| sizeof(signed_long_long) != 8)
skip("Unexpected size of primitive datatype:"
" sizeof(char) == %d (expected 1);"
" sizeof(short) == %d (expected 2);"
" sizeof(int) == %d (expected 4);"
" sizeof(long) == %d (expected %d);"
" sizeof(signed_long_long) == %d (expected 8)",
sizeof(char),
sizeof(short),
sizeof(int),
sizeof(long), sizeof(void *) == 8 ? 8 : 4,
sizeof(signed_long_long));
expected_result = (sizeof(void *) == 8
? integer_64_expected_result
: integer_expected_result);
INT_BATCH_TEST("", "i", signed);
INT_BATCH_TEST("", "d", signed);
INT_BATCH_TEST("", "u", unsigned);
INT_BATCH_TEST("", "o", unsigned);
INT_BATCH_TEST("", "x", unsigned);
INT_BATCH_TEST("", "X", unsigned);
INT_BATCH_TEST("010.5", "i", signed);
INT_BATCH_TEST("010.5", "d", signed);
INT_BATCH_TEST("010.5", "u", unsigned);
INT_BATCH_TEST("010.5", "o", unsigned);
INT_BATCH_TEST("010.5", "x", unsigned);
INT_BATCH_TEST("010.5", "X", unsigned);
INT_BATCH_TEST("-+29", "i", signed);
INT_BATCH_TEST("-+29", "d", signed);
INT_BATCH_TEST("-29", "u", unsigned);
INT_BATCH_TEST("-29", "o", unsigned);
INT_BATCH_TEST("-29", "x", unsigned);
INT_BATCH_TEST("-29", "X", unsigned);
INT_BATCH_TEST("22.8", "i", signed);
INT_BATCH_TEST("22.8", "d", signed);
INT_BATCH_TEST("22.8", "u", unsigned);
INT_BATCH_TEST("22.8", "o", unsigned);
INT_BATCH_TEST("22.8", "x", unsigned);
INT_BATCH_TEST("22.8", "X", unsigned);
INT_BATCH_TEST("-22.8", "i", signed);
INT_BATCH_TEST("-22.8", "d", signed);
INT_BATCH_TEST("-22.8", "u", unsigned);
INT_BATCH_TEST("-22.8", "o", unsigned);
INT_BATCH_TEST("-22.8", "x", unsigned);
INT_BATCH_TEST("-22.8", "X", unsigned);
INT_BATCH_TEST("-823.193", "i", signed);
INT_BATCH_TEST("-823.193", "d", signed);
INT_BATCH_TEST("-823.193", "u", unsigned);
INT_BATCH_TEST("-823.193", "o", unsigned);
INT_BATCH_TEST("-823.193", "x", unsigned);
INT_BATCH_TEST("-823.193", "X", unsigned);
}
static void
float_test(void)
{
expected_result = NULL;
print_cmp_test(-1, "%70.10f", DBL_MAX);
print_cmp_test(-1, "%500.10f", DBL_MAX);
print_cmp_test(-1, "%-500.10f", DBL_MAX);
print_cmp_test(-1, "%500.10e", DBL_MAX);
print_cmp_test(-1, "%-500.10e", DBL_MAX);
print_cmp_test(-1, "%500.10E", DBL_MAX);
print_cmp_test(-1, "%-500.10E", DBL_MAX);
print_cmp_test(-1, "%500.10g", DBL_MAX);
print_cmp_test(-1, "%-500.10g", DBL_MAX);
print_cmp_test(-1, "%500.10G", DBL_MAX);
print_cmp_test(-1, "%-500.10G", DBL_MAX);
}
char some_characters[] =
"abcdefghijklmnopqrstuvwxyz���"
"ABCDEFGHIJKLMNOPQRSTUVXYZ���"
"1234567890"
"()[]{}+-;,:.@�$!\"#�%&/\\=?'`�^~��|<>�*_"
"\a\b\f\n\r\t\v";
#include "string_test.h"
static void
string_test(void)
{
expected_result = string_expected_result;
print_cmp_test(-1, "%s", "hej");
print_cmp_test(-1, "%-10.5s", "hopp");
print_cmp_test(-1, "%10.5s", "hopp");
print_cmp_test(-1, "%-500.500s", "hopp");
print_cmp_test(-1, "%500.500s", "hopp");
print_cmp_test(-1, "\t%10.4s", some_characters);
print_cmp_test(-1, "\t%500.500s", some_characters);
}
#include "character_test.h"
static void
character_test(void)
{
char *cp;
expected_result = character_expected_result;
for (cp = some_characters; *cp; cp++) {
print_cmp_test(-1, "%c", *cp);
print_cmp_test(-1, "%-10.5c", *cp);
print_cmp_test(-1, "%10.5c", *cp);
print_cmp_test(-1, "%-500.500c", *cp);
print_cmp_test(-1, "%500.500c", *cp);
}
}
#include "snprintf_test.h"
static void
snprintf_test(void)
{
expected_result = snprintf_expected_result;
print_cmp_test(6, "hej hopp");
print_cmp_test(7, "hej hopp");
print_cmp_test(8, "hej hopp");
print_cmp_test(9, "hej hopp");
print_cmp_test(10, "hej hopp");
print_cmp_test(6, "hej %d", 4711);
print_cmp_test(7, "hej %d", 4711);
print_cmp_test(8, "hej %d", 4711);
print_cmp_test(9, "hej %d", 4711);
print_cmp_test(10, "hej %d", 4711);
print_cmp_test(sizeof(some_characters)-2, "%s", some_characters);
print_cmp_test(sizeof(some_characters)-1, "%s", some_characters);
print_cmp_test(sizeof(some_characters), "%s", some_characters);
print_cmp_test(sizeof(some_characters)+1, "%s", some_characters);
print_cmp_test(sizeof(some_characters)+2, "%s", some_characters);
print_cmp_test(sizeof(some_characters)/2, "%s%s",
some_characters, some_characters);
print_cmp_test(sizeof(some_characters)*3, "%s%s",
some_characters, some_characters);
}
static void
quote_test(void)
{
expected_result = NULL;
print_cmp_test(-1, "\n");
print_cmp_test(-1, "\\n");
print_cmp_test(-1, "\r");
print_cmp_test(-1, "\\r");
print_cmp_test(-1, "\t");
print_cmp_test(-1, "\\t");
print_cmp_test(-1, "\v");
print_cmp_test(-1, "\\v");
print_cmp_test(-1, "\b");
print_cmp_test(-1, "\\b");
print_cmp_test(-1, "\f");
print_cmp_test(-1, "\\f");
print_cmp_test(-1, "\x80");
print_cmp_test(-1, "\\x80");
print_cmp_test(-1, "\x14");
print_cmp_test(-1, "\\x14");
print_cmp_test(-1, "\xff");
print_cmp_test(-1, "\\xff");
print_cmp_test(-1, "\043");
print_cmp_test(-1, "\\043");
print_cmp_test(-1, "\053");
print_cmp_test(-1, "\\053");
print_cmp_test(-1, "\0143");
print_cmp_test(-1, "\\0143");
print_cmp_test(-1, "\\lf");
print_cmp_test(-1, "\\msss");
print_cmp_test(-1, "\\ss");
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* The dispatcher *
\* */
int
main(int argc, char *argv[])
{
if (argc < 2)
fail("To few arguments for test case");
{
char *testcase;
int save_xres = 0;
int i;
send_my_pid();
testcase = argv[1];
#ifdef THREAD_SAFE
{
int res = ethr_init(NULL);
if (res != 0)
fail("Failed to initialize the ethread library");
}
#endif
for (i = 2; i < argc; i++) {
if (strcmp(argv[i], "save_expected_result") == 0) {
save_xres = 1;
break;
}
}
if (save_xres) {
char filename[100];
sprintf(filename,
"%s%s_test.h",
testcase,
sizeof(void *) == 8 ? "_64" : "");
printf("Saving expected result to %s\n", filename);
outfile = fopen(filename, "w");
ASSERT(outfile);
fprintf(outfile,
"/*\n"
" * %%CopyrightBegin%%\n"
" * Copyright Ericsson AB 1996-2009. All Rights Reserved.\n"
" * \n"
" * The contents of this file are subject to the Erlang Public License,\n"
" * Version 1.1, (the \"License\"); you may not use this file except in\n"
" * compliance with the License. You should have received a copy of the\n"
" * Erlang Public License along with this software. If not, it can be\n"
" * retrieved online at http://www.erlang.org/.\n"
" * \n"
" * Software distributed under the License is distributed on an \"AS IS\"\n"
" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See\n"
" * the License for the specific language governing rights and limitations\n"
" * under the License.\n"
" * %%CopyrightEnd%%\n"
" */\n"
"\n");
fprintf(outfile,
"/* \n"
" * This file has been automatically generated. Do NOT edit it; instead,\n"
" * run '%s %s save_expected_result'%s.\n"
" */\n"
"\n",
argv[0],
testcase,
sizeof(void *) == 8 ? " on a 64-bit machine" : "");
fprintf(outfile,
"char *%s%s_expected_result[] = {\n",
testcase,
sizeof(void *) == 8 ? "_64" : "");
}
if (strcmp("integer", testcase) == 0)
integer_test();
else if (strcmp("float", testcase) == 0)
float_test();
else if (strcmp("string", testcase) == 0)
string_test();
else if (strcmp("character", testcase) == 0)
character_test();
else if (strcmp("snprintf", testcase) == 0)
snprintf_test();
else if (strcmp("quote", testcase) == 0)
quote_test();
else if (!save_xres)
skip("Test case \"%s\" not implemented yet", testcase);
if (save_xres) {
fprintf(outfile, "\tNULL};\n");
fclose(outfile);
}
succeed(NULL);
}
return 0;
}