X-Git-Url: https://git.openssl.org/?p=openssl.git;a=blobdiff_plain;f=test%2Ftestutil.c;h=3d2824c1f9237299232d1f4ace71b63fdfd31608;hp=89e814193c70ac8aec9c327836beb8d59a36514c;hb=3304d57848479441ffa0facc6d9693a466559756;hpb=b2e50bcd0e07af1afa31caff049ae636f45be69b diff --git a/test/testutil.c b/test/testutil.c index 89e814193c..3d2824c1f9 100644 --- a/test/testutil.c +++ b/test/testutil.c @@ -1,109 +1,437 @@ -/* test/testutil.c */ /* - * Utilities for writing OpenSSL unit tests. + * Copyright 2014-2016 The OpenSSL Project Authors. All Rights Reserved. * - * More information: - * http://wiki.openssl.org/index.php/How_To_Write_Unit_Tests_For_OpenSSL - * - * Author: Mike Bland (mbland@acm.org) - * Date: 2014-07-15 - * ==================================================================== - * Copyright (c) 2014 The OpenSSL Project. All rights reserved. + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include "testutil.h" + +#include +#include +#include +#include +#include "e_os.h" + +#include +#include +#include + +/* The size of memory buffers to display on failure */ +#define MEM_BUFFER_SIZE (21) + +/* + * Declares the structures needed to register each test case function. + */ +typedef struct test_info { + const char *test_case_name; + int (*test_fn) (); + int (*param_test_fn)(int idx); + int num; +} TEST_INFO; + +static TEST_INFO all_tests[1024]; +static int num_tests = 0; +/* + * A parameterised tests runs a loop of test cases. + * |num_test_cases| counts the total number of test cases + * across all tests. + */ +static int num_test_cases = 0; + +void add_test(const char *test_case_name, int (*test_fn) ()) +{ + assert(num_tests != OSSL_NELEM(all_tests)); + all_tests[num_tests].test_case_name = test_case_name; + all_tests[num_tests].test_fn = test_fn; + all_tests[num_tests].num = -1; + ++num_test_cases; + ++num_tests; +} + +void add_all_tests(const char *test_case_name, int(*test_fn)(int idx), + int num) +{ + assert(num_tests != OSSL_NELEM(all_tests)); + all_tests[num_tests].test_case_name = test_case_name; + all_tests[num_tests].param_test_fn = test_fn; + all_tests[num_tests].num = num; + ++num_tests; + num_test_cases += num; +} + +#ifndef OPENSSL_NO_CRYPTO_MDEBUG +static int should_report_leaks() +{ + /* + * When compiled with enable-crypto-mdebug, OPENSSL_DEBUG_MEMORY=0 + * can be used to disable leak checking at runtime. + * Note this only works when running the test binary manually; + * the test harness always enables OPENSSL_DEBUG_MEMORY. + */ + char *mem_debug_env = getenv("OPENSSL_DEBUG_MEMORY"); + + return mem_debug_env == NULL + || (strcmp(mem_debug_env, "0") && strcmp(mem_debug_env, "")); +} +#endif + + +void setup_test() +{ +#ifndef OPENSSL_NO_CRYPTO_MDEBUG + if (should_report_leaks()) { + CRYPTO_set_mem_debug(1); + CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_ON); + } +#endif +} + +int finish_test(int ret) +{ +#ifndef OPENSSL_NO_CRYPTO_MDEBUG + if (should_report_leaks() && CRYPTO_mem_leaks_fp(stderr) <= 0) + return EXIT_FAILURE; +#endif + return ret; +} + +static void finalize(int success) +{ + if (success) + ERR_clear_error(); + else + ERR_print_errors_fp(stderr); +} + +int run_tests(const char *test_prog_name) +{ + int num_failed = 0; + + int i, j; + + printf("%s: %d test case%s\n", test_prog_name, num_test_cases, + num_test_cases == 1 ? "" : "s"); + fflush(stdout); + + for (i = 0; i != num_tests; ++i) { + if (all_tests[i].num == -1) { + int ret = all_tests[i].test_fn(); + + if (!ret) { + printf("** %s failed **\n--------\n", + all_tests[i].test_case_name); + fflush(stdout); + ++num_failed; + } + finalize(ret); + } else { + for (j = 0; j < all_tests[i].num; j++) { + int ret = all_tests[i].param_test_fn(j); + + if (!ret) { + printf("** %s failed test %d\n--------\n", + all_tests[i].test_case_name, j); + fflush(stdout); + ++num_failed; + } + finalize(ret); + } + } + } + + if (num_failed != 0) { + printf("%s: %d test%s failed (out of %d)\n", test_prog_name, + num_failed, num_failed != 1 ? "s" : "", num_test_cases); + fflush(stdout); + return EXIT_FAILURE; + } + printf(" All tests passed.\n"); + fflush(stdout); + return EXIT_SUCCESS; +} + +/* + * A common routine to output test failure messages. Generally this should not + * be called directly, rather it should be called by the following functions. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: + * |desc| is a printf formatted description with arguments |args| that is + * supplied by the user and |desc| can be NULL. |type| is the data type + * that was tested (int, char, ptr, ...). |fmt| is a system provided + * printf format with following arguments that spell out the failure + * details i.e. the actual values compared and the operator used. * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. + * The typical use for this is from an utility test function: * - * 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. + * int test6(const char *file, int line, int n) { + * if (n != 6) { + * test_fail_message(1, file, line, "int", "value %d is not %d", n, 6); + * return 0; + * } + * return 1; + * } * - * 3. All advertising materials mentioning features or use of this - * software must display the following acknowledgment: - * "This product includes software developed by the OpenSSL Project - * for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" + * calling test6(3, "oops") will return 0 and produce out along the lines of: + * FAIL oops: (int) value 3 is not 6\n * - * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to - * endorse or promote products derived from this software without - * prior written permission. For written permission, please contact - * licensing@OpenSSL.org. + * It general, test_fail_message should not be called directly. + */ +static void test_fail_message(const char *prefix, const char *file, int line, + const char *type, const char *fmt, ...) + PRINTF_FORMAT(5, 6); + +static void test_fail_message_va(const char *prefix, const char *file, int line, + const char *type, const char *fmt, va_list ap) +{ + fputs(prefix != NULL ? prefix : "ERROR", stderr); + fputs(":", stderr); + if (type) + fprintf(stderr, " (%s)", type); + if (fmt != NULL) { + fputc(' ', stderr); + vfprintf(stderr, fmt, ap); + } + if (file != NULL) { + fprintf(stderr, " @ %s:%d", file, line); + } + fputc('\n', stderr); +} + +static void test_fail_message(const char *prefix, const char *file, int line, + const char *type, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + test_fail_message_va(prefix, file, line, type, fmt, ap); + va_end(ap); +} + +void test_info_c90(const char *desc, ...) +{ + va_list ap; + + va_start(ap, desc); + test_fail_message_va("INFO", NULL, -1, NULL, desc, ap); + va_end(ap); +} + +void test_info(const char *file, int line, const char *desc, ...) +{ + va_list ap; + + va_start(ap, desc); + test_fail_message_va("INFO", file, line, NULL, desc, ap); + va_end(ap); +} + +void test_error_c90(const char *desc, ...) +{ + va_list ap; + + va_start(ap, desc); + test_fail_message(NULL, NULL, -1, NULL, desc, ap); + va_end(ap); +} + +void test_error(const char *file, int line, const char *desc, ...) +{ + va_list ap; + + va_start(ap, desc); + test_fail_message_va(NULL, file, line, NULL, desc, ap); + va_end(ap); +} + +/* + * Define some comparisons between pairs of various types. + * These functions return 1 if the test is true. + * Otherwise, they return 0 and pretty-print diagnostics. * - * 5. Products derived from this software may not be called "OpenSSL" - * nor may "OpenSSL" appear in their names without prior written - * permission of the OpenSSL Project. + * In each case the functions produced are: + * int test_name_eq(const type t1, const type t2, const char *desc, ...); + * int test_name_ne(const type t1, const type t2, const char *desc, ...); + * int test_name_lt(const type t1, const type t2, const char *desc, ...); + * int test_name_le(const type t1, const type t2, const char *desc, ...); + * int test_name_gt(const type t1, const type t2, const char *desc, ...); + * int test_name_ge(const type t1, const type t2, const char *desc, ...); * - * 6. Redistributions of any form whatsoever must retain the following - * acknowledgment: - * "This product includes software developed by the OpenSSL Project - * for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" + * The t1 and t2 arguments are to be compared for equality, inequality, + * less than, less than or equal to, greater than and greater than or + * equal to respectively. If the specified condition holds, the functions + * return 1. If the condition does not hold, the functions print a diagnostic + * message and return 0. * - * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY - * EXPRESSED 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 OpenSSL PROJECT OR - * ITS 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. - * ==================================================================== + * The desc argument is a printf format string followed by its arguments and + * this is included in the output if the condition being tested for is false. */ +#define DEFINE_COMPARISON(type, name, opname, op, fmt) \ + int test_ ## name ## _ ## opname(const char *file, int line, \ + const char *s1, const char *s2, \ + const type t1, const type t2) \ + { \ + if (t1 op t2) \ + return 1; \ + test_fail_message(NULL, file, line, #type, \ + "%s [" fmt "] " #op " %s [" fmt "]", \ + s1, t1, s2, t2); \ + return 0; \ + } -#include "testutil.h" +#define DEFINE_COMPARISONS(type, name, fmt) \ + DEFINE_COMPARISON(type, name, eq, ==, fmt) \ + DEFINE_COMPARISON(type, name, ne, !=, fmt) \ + DEFINE_COMPARISON(type, name, lt, <, fmt) \ + DEFINE_COMPARISON(type, name, le, <=, fmt) \ + DEFINE_COMPARISON(type, name, gt, >, fmt) \ + DEFINE_COMPARISON(type, name, ge, >=, fmt) -#include -#include -#include +DEFINE_COMPARISONS(int, int, "%d") +DEFINE_COMPARISONS(unsigned int, uint, "%u") +DEFINE_COMPARISONS(char, char, "%c") +DEFINE_COMPARISONS(unsigned char, uchar, "%u") +DEFINE_COMPARISONS(long, long, "%ld") +DEFINE_COMPARISONS(unsigned long, ulong, "%lu") +DEFINE_COMPARISONS(size_t, size_t, "%" OSSLzu) + +DEFINE_COMPARISON(void *, ptr, eq, ==, "%p") +DEFINE_COMPARISON(void *, ptr, ne, !=, "%p") + +int test_ptr_null(const char *file, int line, const char *s, const void *p) +{ + if (p == NULL) + return 1; + test_fail_message(NULL, file, line, "ptr", "%s [%p] == NULL", s, p); + return 0; +} + +int test_ptr(const char *file, int line, const char *s, const void *p) +{ + if (p != NULL) + return 1; + test_fail_message(NULL, file, line, "ptr", "%s [%p] != NULL", s, p); + return 0; +} + +int test_true(const char *file, int line, const char *s, int b) +{ + if (b) + return 1; + test_fail_message(NULL, file, line, "bool", "%s [false] == true", s); + return 0; +} + +int test_false(const char *file, int line, const char *s, int b) +{ + if (!b) + return 1; + test_fail_message(NULL, file, line, "bool", "%s [true] == false", s); + return 0; +} + +static const char *print_string_maybe_null(const char *s) +{ + return s == NULL ? "(NULL)" : s; +} -/* Declares the structures needed to register each test case function. +int test_str_eq(const char *file, int line, const char *st1, const char *st2, + const char *s1, const char *s2) +{ + if (s1 == NULL && s2 == NULL) + return 1; + if (s1 == NULL || s2 == NULL || strcmp(s1, s2) != 0) { + test_fail_message(NULL, file, line, "string", "%s [%s] == %s [%s]", + st1, print_string_maybe_null(s1), + st2, print_string_maybe_null(s2)); + return 0; + } + return 1; +} + +int test_str_ne(const char *file, int line, const char *st1, const char *st2, + const char *s1, const char *s2) +{ + if ((s1 == NULL) ^ (s2 == NULL)) + return 1; + if (s1 == NULL || strcmp(s1, s2) == 0) { + test_fail_message(NULL, file, line, "string", "%s [%s] != %s [%s]", + st1, print_string_maybe_null(s1), + st2, print_string_maybe_null(s2)); + return 0; + } + return 1; +} + +/* + * We could use OPENSSL_buf2hexstr() to do this but trying to allocate memory + * in a failure state isn't generally a great idea. */ -typedef struct test_info - { - const char* test_case_name; - int (*test_fn)(); - } TEST_INFO; +static const char *print_mem_maybe_null(const void *s, size_t n, + char out[MEM_BUFFER_SIZE]) +{ + size_t i; + const unsigned char *p = (const unsigned char *)s; + int pad = 2*n >= MEM_BUFFER_SIZE; -static TEST_INFO all_tests[1024]; -static int num_tests = 0; + if (s == NULL) + return "(NULL)"; + if (pad) + n = MEM_BUFFER_SIZE-4; + + for (i=0; i<2*n; i++) { + unsigned char c = (i & 1) != 0 ? p[i / 2] & 15 : p[i / 2] >> 4; + out[i] = "0123456789abcdef"[c]; + } + if (pad) { + out[i++] = '.'; + out[i++] = '.'; + out[i++] = '.'; + } + out[i] = '\0'; + + return out; +} + +int test_mem_eq(const char *file, int line, const char *st1, const char *st2, + const void *s1, size_t n1, const void *s2, size_t n2) +{ + char b1[MEM_BUFFER_SIZE], b2[MEM_BUFFER_SIZE]; + + if (s1 == NULL && s2 == NULL) + return 1; + if (n1 != n2) { + test_fail_message(NULL, file, line, "memory", + "size mismatch %s %s [%"OSSLzu"] != %s %s [%"OSSLzu"]", + st1, print_mem_maybe_null(s1, n1, b1), n1, + st2, print_mem_maybe_null(s2, n2, b2), n2); + return 0; + } + if (s1 == NULL || s2 == NULL || memcmp(s1, s2, n1) != 0) { + test_fail_message(NULL, file, line, "memory", + "%s %s [%"OSSLzu"] != %s %s [%"OSSLzu"]", + st1, print_mem_maybe_null(s1, n1, b1), n1, + st2, print_mem_maybe_null(s2, n2, b2), n2); + return 0; + } + return 1; +} + +int test_mem_ne(const char *file, int line, const char *st1, const char *st2, + const void *s1, size_t n1, const void *s2, size_t n2) +{ + char b1[MEM_BUFFER_SIZE], b2[MEM_BUFFER_SIZE]; -void add_test(const char* test_case_name, int (*test_fn)()) - { - assert(num_tests != (sizeof(all_tests) / sizeof(all_tests)[0])); - all_tests[num_tests].test_case_name = test_case_name; - all_tests[num_tests].test_fn = test_fn; - ++num_tests; - } - -int run_tests(const char* test_prog_name) - { - int num_failed = 0; - int i = 0; - - printf("%s: %d test case%s\n", test_prog_name, num_tests, - num_tests == 1 ? "" : "s"); - for (i = 0; i != num_tests; ++i) - { - if (all_tests[i].test_fn()) - { - printf("** %s failed **\n--------\n", - all_tests[i].test_case_name); - ++num_failed; - } - } - - if (num_failed != 0) - { - printf("%s: %d test%s failed (out of %d)\n", test_prog_name, - num_failed, num_failed != 1 ? "s" : "", num_tests); - return EXIT_FAILURE; - } - printf(" All tests passed.\n"); - return EXIT_SUCCESS; - } + if ((s1 == NULL) ^ (s2 == NULL)) + return 1; + if (n1 != n2) + return 1; + if (s1 == NULL || memcmp(s1, s2, n1) == 0) { + test_fail_message(NULL, file, line, "memory", + "%s %s [%"OSSLzu"] != %s %s [%"OSSLzu"]", + st1, print_mem_maybe_null(s1, n1, b1), n1, + st2, print_mem_maybe_null(s2, n2, b2), n2); + return 0; + } + return 1; +}