/* uTest * Copyright (C) 2011 Data Differential, http://datadifferential.com/ * Copyright (C) 2006-2009 Brian Aker * All rights reserved. * * Use and distribution licensed under the BSD license. See * the COPYING file in the parent directory for full text. */ #include <libtest/common.h> #include <cassert> #include <cstdlib> #include <cstring> #include <sys/time.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/wait.h> #include <unistd.h> #include <ctime> #include <fnmatch.h> #include <iostream> #include <libtest/stats.h> #ifndef __INTEL_COMPILER #pragma GCC diagnostic ignored "-Wold-style-cast" #endif static in_port_t global_port= 0; in_port_t default_port() { assert(global_port); return global_port; } void set_default_port(in_port_t port) { global_port= port; } static void stats_print(Stats *stats) { std::cout << "\tTotal Collections\t\t\t\t" << stats->collection_total << std::endl; std::cout << "\tFailed Collections\t\t\t\t" << stats->collection_failed << std::endl; std::cout << "\tSkipped Collections\t\t\t\t" << stats->collection_skipped << std::endl; std::cout << "\tSucceeded Collections\t\t\t\t" << stats->collection_success << std::endl; std::cout << std::endl; std::cout << "Total\t\t\t\t" << stats->total << std::endl; std::cout << "\tFailed\t\t\t" << stats->failed << std::endl; std::cout << "\tSkipped\t\t\t" << stats->skipped << std::endl; std::cout << "\tSucceeded\t\t" << stats->success << std::endl; } static long int timedif(struct timeval a, struct timeval b) { long us, s; us = (long)(a.tv_usec - b.tv_usec); us /= 1000; s = (long)(a.tv_sec - b.tv_sec); s *= 1000; return s + us; } const char *test_strerror(test_return_t code) { switch (code) { case TEST_SUCCESS: return "ok"; case TEST_FAILURE: return "failed"; case TEST_MEMORY_ALLOCATION_FAILURE: return "memory allocation"; case TEST_SKIPPED: return "skipped"; case TEST_FATAL: break; } return "failed"; } void create_core(void) { if (getenv("LIBMEMCACHED_NO_COREDUMP") == NULL) { pid_t pid= fork(); if (pid == 0) { abort(); } else { while (waitpid(pid, NULL, 0) != pid) {}; } } } static test_return_t _runner_default(test_callback_fn func, void *p) { if (func) { return func(p); } return TEST_SUCCESS; } static Runner defualt_runners= { _runner_default, _runner_default, _runner_default }; static test_return_t _default_callback(void *p) { (void)p; return TEST_SUCCESS; } Framework::Framework() : collections(NULL), _create(NULL), _destroy(NULL), collection_startup(_default_callback), collection_shutdown(_default_callback), _on_error(NULL), runner(&defualt_runners) { } int main(int argc, char *argv[]) { Framework world; Stats stats; get_world(&world); if (not world.runner) { world.runner= &defualt_runners; } test_return_t error; void *world_ptr= world.create(&error); if (test_failed(error)) { return EXIT_FAILURE; } char *collection_to_run= NULL; if (argc > 1) { collection_to_run= argv[1]; } else if (getenv("TEST_COLLECTION")) { collection_to_run= getenv("TEST_COLLECTION"); } if (collection_to_run) { std::cout << "Only testing " << collection_to_run << std::endl; } char *wildcard= NULL; if (argc == 3) { wildcard= argv[2]; } for (collection_st *next= world.collections; next->name; next++) { test_return_t collection_rc= TEST_SUCCESS; bool failed= false; bool skipped= false; if (collection_to_run && fnmatch(collection_to_run, next->name, 0)) continue; stats.collection_total++; collection_rc= world.startup(world_ptr); if (collection_rc == TEST_SUCCESS and next->pre) { collection_rc= world.runner->pre(next->pre, world_ptr); } switch (collection_rc) { case TEST_SUCCESS: std::cerr << std::endl << next->name << std::endl << std::endl; break; case TEST_FATAL: case TEST_FAILURE: std::cerr << std::endl << next->name << " [ failed ]" << std::endl << std::endl; stats.collection_failed++; goto cleanup; case TEST_SKIPPED: std::cerr << std::endl << next->name << " [ skipping ]" << std::endl << std::endl; stats.collection_skipped++; goto cleanup; case TEST_MEMORY_ALLOCATION_FAILURE: test_assert(0, "Allocation failure, or unknown return"); } for (test_st *run= next->tests; run->name; run++) { struct timeval start_time, end_time; long int load_time= 0; if (wildcard && fnmatch(wildcard, run->name, 0)) { continue; } std::cerr << "\tTesting " << run->name; world.item.startup(world_ptr); world.item.flush(world_ptr, run); world.item.pre(world_ptr); test_return_t return_code; { // Runner Code gettimeofday(&start_time, NULL); return_code= world.runner->run(run->test_fn, world_ptr); gettimeofday(&end_time, NULL); load_time= timedif(end_time, start_time); } world.item.post(world_ptr); stats.total++; std::cerr << "\t\t\t\t\t"; switch (return_code) { case TEST_SUCCESS: std::cerr << load_time / 1000 << "." << load_time % 1000; stats.success++; break; case TEST_FATAL: case TEST_FAILURE: stats.failed++; failed= true; break; case TEST_SKIPPED: stats.skipped++; skipped= true; break; case TEST_MEMORY_ALLOCATION_FAILURE: test_assert(0, "Memory Allocation Error"); } std::cerr << "[ " << test_strerror(return_code) << " ]" << std::endl; if (test_failed(world.on_error(return_code, world_ptr))) { break; } } if (next->post && world.runner->post) { (void) world.runner->post(next->post, world_ptr); } if (failed == 0 and skipped == 0) { stats.collection_success++; } cleanup: world.shutdown(world_ptr); } if (stats.collection_failed || stats.collection_skipped) { std::cerr << std::endl << std::endl << "Some test failures and/or skipped test occurred." << std::endl << std::endl; #if 0 print_failed_test(); #endif } else { std::cout << std::endl << std::endl << "All tests completed successfully." << std::endl << std::endl; } if (test_failed(world.destroy(world_ptr))) { stats.failed++; // We do this to make our exit code return EXIT_FAILURE } stats_print(&stats); return stats.failed == 0 ? 0 : 1; }