/* * UnitTest.h * * This source file is part of the FoundationDB open source project * * Copyright 2013-2018 Apple Inc. and the FoundationDB project authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef FLOW_UNITTEST_H #define FLOW_UNITTEST_H #pragma once /* * Flow unit testing framework * * This is an *extremely* lightweight framework for writing optionally asynchronous, * optionally randomized unit tests. * * Usage: * * TEST_CASE("/product/module/testcase") { * double random_test_parameter = deterministicRandom()->random01(); * ASSERT( something ); * return Void(); * } * * In an `.actor.cpp` file, the body of a TEST_CASE is an actor (may contain `wait`, `state`, etc) * In a `.cpp` file, the body of a TEST_CASE is an ordinary function returning a Future * * Our tools for actually executing tests are external to flow (and use g_unittests to find test cases). * See the `UnitTestWorkload` class. */ #include "flow/flow.h" #include class UnitTestParameters { Optional dataDir; public: // Map of named case-sensitive parameters std::map params; // Set a named parameter to a string value, replacing any existing value void set(const std::string& name, const std::string& value); // Set a named parameter to an integer converted to a string value, replacing any existing value void set(const std::string& name, int64_t value); // Set a named parameter to a double converted to a string value, replacing any existing value void set(const std::string& name, double value); // Get a parameter's value, will return !present() if parameter was not set Optional get(const std::string& name) const; // Get a parameter's value as an integer, will return !present() if parameter was not set Optional getInt(const std::string& name) const; // Get a parameter's value parsed as a double, will return !present() if parameter was not set Optional getDouble(const std::string& name) const; // This is separate because it assumes data directory has already been set, and doesn't return an optional std::string getDataDir() const; // Set the data directory used to persist data for this test void setDataDir(std::string const&); }; // Unit test definition structured as a linked list item struct UnitTest { typedef Future (*TestFunction)(const UnitTestParameters& params); const char* name; const char* file; int line; TestFunction func; UnitTest* next; UnitTest(const char* name, const char* file, int line, TestFunction func); }; // Collection of unit tests in the form of a linked list struct UnitTestCollection { UnitTest* tests; }; extern UnitTestCollection g_unittests; #define APPEND(a, b) a##b // FILE_UNIQUE_NAME(basename) expands to a name like basename456 if on line 456 #define FILE_UNIQUE_NAME1(name, line) APPEND(name, line) #define FILE_UNIQUE_NAME(name) FILE_UNIQUE_NAME1(name, __LINE__) #ifdef FLOW_DISABLE_UNIT_TESTS #define TEST_CASE(name) static Future FILE_UNIQUE_NAME(disabled_testcase_func)(const UnitTestParameters& params) #define ACTOR_TEST_CASE(actorname, name) #else #define TEST_CASE(name) \ static Future FILE_UNIQUE_NAME(testcase_func)(const UnitTestParameters& params); \ namespace { \ static UnitTest FILE_UNIQUE_NAME(testcase)(name, __FILE__, __LINE__, &FILE_UNIQUE_NAME(testcase_func)); \ } \ static Future FILE_UNIQUE_NAME(testcase_func)(const UnitTestParameters& params) // ACTOR_TEST_CASE generated by actorcompiler; don't use directly #define ACTOR_TEST_CASE(actorname, name) \ namespace { \ UnitTest APPEND(testcase_, actorname)(name, __FILE__, __LINE__, &actorname); \ } #endif #endif