├── Makefile ├── tests ├── Makefile ├── main.cc └── acutest.h ├── azure-pipelines.yml ├── example.cc ├── LICENSE ├── README.md └── dynamic_bitset.hh /Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | clang++ -std=c++11 -Weverything -Werror -Wno-c++98-compat example.cc 3 | -------------------------------------------------------------------------------- /tests/Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | clang++ -std=c++11 -I../ -Weverything -Werror -Wno-c++98-compat main.cc -o test 3 | -------------------------------------------------------------------------------- /azure-pipelines.yml: -------------------------------------------------------------------------------- 1 | # C/C++ with GCC 2 | # Build your C/C++ project with GCC using make. 3 | # Add steps that publish test results, save build artifacts, deploy, and more: 4 | # https://docs.microsoft.com/azure/devops/pipelines/apps/c-cpp/gcc 5 | 6 | trigger: 7 | - master 8 | 9 | pool: 10 | vmImage: 'ubuntu-latest' 11 | 12 | steps: 13 | - script: | 14 | make 15 | displayName: 'make' 16 | - script: | 17 | cd tests 18 | make 19 | ./test 20 | displayName: 'test' 21 | -------------------------------------------------------------------------------- /example.cc: -------------------------------------------------------------------------------- 1 | #include "dynamic_bitset.hh" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | int main(int argc, char **argv) 9 | { 10 | (void)argc; 11 | (void)argv; 12 | 13 | { 14 | dynamic_bitset db(12, 0); 15 | 16 | db.set(0); 17 | db.set(3); 18 | std::cout << db.to_string() << std::endl; 19 | 20 | db.resize(8); 21 | std::cout << db.to_string() << std::endl; 22 | } 23 | 24 | { 25 | std::cout << "Initialize with value\n"; 26 | dynamic_bitset db(/* bits */12, /* value */42); 27 | std::bitset<12> b(42); 28 | 29 | std::cout << db[3] << "\n"; 30 | 31 | db.set(3, false); 32 | 33 | std::cout << db[3] << "\n"; 34 | 35 | std::cout << db.to_string() << std::endl; 36 | std::cout << b.to_string() << std::endl; 37 | } 38 | 39 | { 40 | // initial size = 0 41 | dynamic_bitset db; 42 | 43 | db.resize(8); 44 | 45 | // resized bitfield is undefined. so clear with 'false' 46 | db.reset(); 47 | db.set(7); 48 | 49 | std::cout << db.to_string() << std::endl; 50 | } 51 | 52 | return EXIT_SUCCESS; 53 | } 54 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Syoyo Fujita 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Simple dynamic_bitset implementation in C++11 2 | 3 | [![Build Status](https://dev.azure.com/syoyo/lte%20oss/_apis/build/status/syoyo.dynamic_bitset?branchName=master)](https://dev.azure.com/syoyo/lte%20oss/_build/latest?definitionId=3&branchName=master) 4 | 5 | `dynamic_bitset` is implemented as a template class, so you can just include the header file to use it. 6 | `dynamic_bitset` does not depend any library except for STL. 7 | 8 | ## Requirements 9 | 10 | * C++11 compiler 11 | 12 | ## Usage 13 | 14 | ``` 15 | #include "dynamic_bitset.hh" 16 | 17 | // init with 13 bits. fill bitfield with value `42` 18 | dynamic_bitset db(13, 42); 19 | 20 | // init with 0bits 21 | dynamic_bitset db; 22 | 23 | // resize to 12 bits 24 | db.resize(12); 25 | 26 | // resize does not initialize extended memory region. 27 | // Use `reset()` to clear all bitfields. 28 | db.reset(); 29 | 30 | // set 3rd bit true 31 | db.set(3); 32 | 33 | // get 5th bit state 34 | std::cout << db[5] << "\n"; 35 | 36 | // print bitfield 37 | std::cout << db.to_string() << "\n"; 38 | 39 | // set all bits with true or false 40 | db.setall(true); 41 | 42 | ``` 43 | 44 | ## Note 45 | 46 | `dynamic_bitset` has similar functions defined in `std::bitset` and `std::vector`. 47 | `dynamic_bitset` is not compatible with `boost::dynamic_bitset` 48 | 49 | ## TODO 50 | 51 | * [ ] Implement more features. 52 | * [ ] Validate endianness. 53 | * [ ] Write more tests. 54 | 55 | 56 | ## Similar projects 57 | 58 | * Feature-complete dynamic_bitset in C++17 https://github.com/pinam45/dynamic_bitset 59 | 60 | ## License 61 | 62 | dynamic_bitset is licensed under MIT license. 63 | 64 | ### Thrid party license 65 | 66 | * acutest : For unit test. MIT license. 67 | -------------------------------------------------------------------------------- /tests/main.cc: -------------------------------------------------------------------------------- 1 | #ifdef __clang__ 2 | #pragma clang diagnostic push 3 | #pragma clang diagnostic ignored "-Weverything" 4 | #endif 5 | 6 | #include "acutest.h" 7 | 8 | #ifdef __clang__ 9 | #pragma clang diagnostic pop 10 | #endif 11 | 12 | #include 13 | #include 14 | 15 | #include "dynamic_bitset.hh" 16 | 17 | static void test_dynamic_bitset() 18 | { 19 | dynamic_bitset db; 20 | db.resize(1); 21 | 22 | TEST_CHECK(db[0] == false); 23 | 24 | db.set(0, true); 25 | TEST_CHECK(db[0] == true); 26 | 27 | } 28 | 29 | static void test_initial_value() 30 | { 31 | { 32 | dynamic_bitset db(/* bits */12, /* value */42); 33 | std::bitset<12> b(42); 34 | 35 | for (size_t i = 0; i < 12; i++) { 36 | TEST_CHECK(db[i] == b[i]); 37 | } 38 | 39 | TEST_CHECK(db.to_string().compare(b.to_string()) == 0); 40 | } 41 | 42 | { 43 | dynamic_bitset db(/* bits */48, /* value */0x123f80000); 44 | std::bitset<48> b(0x123f80000); 45 | 46 | for (size_t i = 0; i < 48; i++) { 47 | TEST_CHECK(db[i] == b[i]); 48 | } 49 | 50 | TEST_CHECK(db.to_string().compare(b.to_string()) == 0); 51 | } 52 | 53 | } 54 | 55 | static void test_set() 56 | { 57 | { 58 | dynamic_bitset db(/* bits */12, /* value */42); 59 | db.setall(true); 60 | 61 | for (size_t i = 0; i < 12; i++) { 62 | TEST_CHECK(true == db[i]); 63 | } 64 | 65 | db.set(11, false); 66 | TEST_CHECK(false == db.test(11)); 67 | 68 | db.setall(false); 69 | 70 | for (size_t i = 0; i < 12; i++) { 71 | TEST_CHECK(false == db[i]); 72 | } 73 | } 74 | } 75 | 76 | static void test_flip() 77 | { 78 | { 79 | dynamic_bitset db(/* bits */12, /* value */42); 80 | std::bitset<12> b(42); 81 | 82 | db.flip(); 83 | b.flip(); 84 | 85 | for (size_t i = 0; i < 12; i++) { 86 | TEST_CHECK(b[i] == db[i]); 87 | } 88 | 89 | db.flip(3); 90 | b.flip(3); 91 | TEST_CHECK(b[3] == db[3]); 92 | } 93 | 94 | } 95 | 96 | static void test_to_ulong() 97 | { 98 | // TODO(LTE): Write test for bits > 32. 99 | { 100 | dynamic_bitset db(/* bits */12, /* value */42); 101 | std::bitset<12> b(42); 102 | 103 | uint64_t db_value = db.to_ulong(); 104 | uint64_t b_value = b.to_ulong(); 105 | 106 | TEST_CHECK(db_value == b_value); 107 | } 108 | } 109 | 110 | static void test_to_ullong() 111 | { 112 | // TODO(LTE): Write test for bits > 64. 113 | { 114 | dynamic_bitset db(/* bits */12, /* value */42); 115 | std::bitset<12> b(42); 116 | 117 | uint64_t db_value = db.to_ullong(); 118 | uint64_t b_value = b.to_ullong(); 119 | 120 | TEST_CHECK(db_value == b_value); 121 | } 122 | } 123 | 124 | 125 | static void test_data() 126 | { 127 | { 128 | dynamic_bitset db(/* bits */12, /* value */42); 129 | uint8_t value = (*db.data()); 130 | 131 | TEST_CHECK(42 == value); 132 | 133 | } 134 | 135 | { 136 | dynamic_bitset db(/* bits */12, /* value */42); 137 | 138 | TEST_CHECK(12 == db.size()); 139 | 140 | // size is in byte size. 1 + (bits - 1) / 8 141 | TEST_CHECK(2 == db.storage_size()); 142 | } 143 | 144 | { 145 | dynamic_bitset db(/* bits */64, /* value */42); 146 | 147 | TEST_CHECK(64 == db.size()); 148 | TEST_CHECK(8 == db.storage_size()); 149 | } 150 | } 151 | 152 | TEST_LIST = { 153 | { "dynamic_bitset", test_dynamic_bitset }, 154 | { "test_initial_value", test_initial_value }, 155 | { "test_set", test_set }, 156 | { "test_flip", test_flip }, 157 | { "test_data", test_data }, 158 | { "test_to_ulong", test_to_ulong }, 159 | { "test_to_ullong", test_to_ullong }, 160 | { nullptr, nullptr } 161 | }; 162 | 163 | -------------------------------------------------------------------------------- /dynamic_bitset.hh: -------------------------------------------------------------------------------- 1 | // 2 | // Simple dynamic bitset implementation in C++11 3 | // 4 | 5 | /* 6 | The MIT License (MIT) 7 | 8 | Copyright (c) 2019 Syoyo Fujita. 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining a copy 11 | of this software and associated documentation files (the "Software"), to deal 12 | in the Software without restriction, including without limitation the rights 13 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | copies of the Software, and to permit persons to whom the Software is 15 | furnished to do so, subject to the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be included in 18 | all copies or substantial portions of the Software. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 26 | THE SOFTWARE. 27 | */ 28 | 29 | 30 | #pragma once 31 | 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | 39 | //#include // dbg 40 | 41 | // TODO(syoyo): Consider endianness 42 | 43 | /// 44 | /// @brief dynamically allocatable bitset 45 | /// 46 | class dynamic_bitset { 47 | public: 48 | dynamic_bitset() = default; 49 | dynamic_bitset(dynamic_bitset &&) = default; 50 | dynamic_bitset(const dynamic_bitset &) = default; 51 | 52 | dynamic_bitset &operator=(const dynamic_bitset &) = default; 53 | 54 | ~dynamic_bitset() = default; 55 | 56 | /// 57 | /// @brief Construct dynamic_bitset with given number of bits. 58 | /// 59 | /// @param[in] nbits The number of bits to use. 60 | /// @param[in] value Initize bitfield with this value. 61 | /// 62 | explicit dynamic_bitset(size_t nbits, uint64_t value) { 63 | _num_bits = nbits; 64 | 65 | size_t num_bytes; 66 | if (nbits < 8) { 67 | num_bytes = 1; 68 | } else { 69 | num_bytes = 1 + (nbits - 1) / 8; 70 | } 71 | 72 | _data.resize(num_bytes); 73 | 74 | // init with zeros 75 | std::fill_n(_data.begin(), _data.size(), 0); 76 | 77 | // init with `value`. 78 | 79 | if (nbits < sizeof(uint64_t)) { 80 | 81 | assert(num_bytes < 3); 82 | 83 | uint64_t masked_value = value & ((1 << (nbits +1)) - 1); 84 | 85 | for (size_t i = 0; i < _data.size(); i++) { 86 | _data[i] = (masked_value >> (i * 8)) & 0xff; 87 | } 88 | 89 | } else { 90 | for (size_t i = 0; i < sizeof(uint64_t); i++) { 91 | _data[i] = (value >> (i * 8)) & 0xff; 92 | } 93 | } 94 | 95 | } 96 | 97 | /// 98 | /// Equivalent to std::bitset::any() 99 | /// 100 | bool any() const { 101 | for (size_t i = 0; i < _num_bits; i++) { 102 | if ((*this)[i]) { 103 | return true; 104 | } 105 | } 106 | 107 | return false; 108 | } 109 | 110 | /// 111 | /// Equivalent to std::bitset::all() 112 | /// 113 | bool all() const { 114 | for (size_t i = 0; i < _num_bits; i++) { 115 | if (false == (*this)[i]) { 116 | return false; 117 | } 118 | } 119 | 120 | return true; 121 | } 122 | 123 | /// 124 | /// Equivalent to std::bitset::none() 125 | /// 126 | bool none() const { 127 | for (size_t i = 0; i < _num_bits; i++) { 128 | if ((*this)[i]) { 129 | return false; 130 | } 131 | } 132 | 133 | return true; 134 | } 135 | 136 | /// 137 | /// Equivalent to std::bitset::flip() 138 | /// 139 | dynamic_bitset &flip() { 140 | for (size_t i = 0; i < _num_bits; i++) { 141 | set(i, (*this)[i] ? false : true); 142 | } 143 | 144 | return (*this); 145 | } 146 | 147 | /// 148 | /// Equivalent to std::bitset::flip(size_t pos) 149 | /// 150 | dynamic_bitset &flip(size_t pos) { 151 | set(pos, (*this)[pos] ? false : true); 152 | 153 | return (*this); 154 | } 155 | 156 | /// 157 | /// @brief Resize dynamic_bitset. 158 | /// 159 | /// @details Resize dynamic_bitset. Resize behavior is similar to std::vector::resize. 160 | /// 161 | /// @param[in] nbits The number of bits to use. 162 | /// 163 | void resize(size_t nbits) { 164 | 165 | _num_bits = nbits; 166 | 167 | size_t num_bytes; 168 | if (nbits < 8) { 169 | num_bytes = 1; 170 | } else { 171 | num_bytes = 1 + (nbits - 1) / 8; 172 | } 173 | 174 | _data.resize(num_bytes); 175 | } 176 | 177 | /// 178 | /// @return The number of bits that are set to `true` 179 | /// 180 | uint32_t count() const { 181 | 182 | uint32_t c = 0; 183 | 184 | for (size_t i = 0; i < _num_bits; i++) { 185 | c += (*this)[i] ? 1 : 0; 186 | } 187 | 188 | return c; 189 | } 190 | 191 | bool test(size_t pos) const { 192 | // TODO(syoyo): Do range check and throw when out-of-bounds access. 193 | return (*this)[pos]; 194 | } 195 | 196 | void reset() { 197 | std::fill_n(_data.begin(), _data.size(), 0); 198 | } 199 | 200 | // Set all bitfield with `value` 201 | void setall(bool value) { 202 | for (size_t i = 0; i < _num_bits; i++) { 203 | set(i, value); 204 | } 205 | } 206 | 207 | void set(size_t pos, bool value = true) { 208 | size_t byte_loc = pos / 8; 209 | uint8_t offset = pos % 8; 210 | 211 | uint8_t bitfield = uint8_t(1 << offset); 212 | 213 | if (value == true) { 214 | // bit on 215 | _data[byte_loc] |= bitfield; 216 | } else { 217 | // turn off bit 218 | _data[byte_loc] &= (~bitfield); 219 | } 220 | } 221 | 222 | std::string to_string() const { 223 | std::stringstream ss; 224 | 225 | for (size_t i = 0; i < _num_bits; i++) { 226 | ss << ((*this)[_num_bits - i - 1] ? "1" : "0"); 227 | } 228 | 229 | return ss.str(); 230 | } 231 | 232 | // 233 | // bits are truncated to 32bit uint 234 | // In contrast to `std::bitset::to_ulong()`, this function does not throw overflow_error even num_bits are larger than 32. 235 | // 236 | uint32_t to_ulong() { 237 | uint32_t value = 0; 238 | 239 | // TODO(syoyo): optimize code 240 | size_t n = std::min(_num_bits, size_t(32)); 241 | for (size_t i = 0; i < n; i++) { 242 | value |= uint32_t(((*this)[i])) << i; 243 | } 244 | 245 | return value; 246 | } 247 | 248 | // 249 | // bits are truncated to 64bit uint 250 | // In contrast to `std::bitset::to_ullong()`, this function does not throw overflow_error even num_bits are larger than 64. 251 | // 252 | uint64_t to_ullong() { 253 | uint64_t value = 0; 254 | 255 | // TODO(syoyo): optimize code 256 | size_t n = std::min(_num_bits, size_t(64)); 257 | for (size_t i = 0; i < n; i++) { 258 | value |= uint64_t(((*this)[i])) << i; 259 | } 260 | 261 | return value; 262 | } 263 | 264 | bool operator[](size_t pos) const { 265 | size_t byte_loc = pos / 8; 266 | size_t offset = pos % 8; 267 | 268 | return (_data[byte_loc] >> offset) & 0x1; 269 | } 270 | 271 | // Return the storage size(in bytes) 272 | size_t storage_size() const { 273 | return _data.size(); 274 | } 275 | 276 | // Return the number of bits. 277 | // equivalent to std::bitset::size 278 | size_t size() const { 279 | return _num_bits; 280 | } 281 | 282 | // Return memory address of bitfield(as an byte array) 283 | const uint8_t *data() const { 284 | return _data.data(); 285 | } 286 | 287 | // Return memory address of bitfield(as an byte array) 288 | uint8_t *data() { 289 | return _data.data(); 290 | } 291 | 292 | 293 | private: 294 | size_t _num_bits{0}; 295 | 296 | // bitfields are reprentated as an array of bytes. 297 | std::vector _data; 298 | }; 299 | -------------------------------------------------------------------------------- /tests/acutest.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Acutest -- Another C/C++ Unit Test facility 3 | * 4 | * 5 | * Copyright (c) 2013-2019 Martin Mitas 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a 8 | * copy of this software and associated documentation files (the "Software"), 9 | * to deal in the Software without restriction, including without limitation 10 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 11 | * and/or sell copies of the Software, and to permit persons to whom the 12 | * Software is furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in 15 | * all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 18 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 22 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 23 | * IN THE SOFTWARE. 24 | */ 25 | 26 | #ifndef ACUTEST_H__ 27 | #define ACUTEST_H__ 28 | 29 | 30 | /************************ 31 | *** Public interface *** 32 | ************************/ 33 | 34 | /* By default, "acutest.h" provides the main program entry point (function 35 | * main()). However, if the test suite is composed of multiple source files 36 | * which include "acutest.h", then this causes a problem of multiple main() 37 | * definitions. To avoid this problem, #define macro TEST_NO_MAIN in all 38 | * compilation units but one. 39 | */ 40 | 41 | /* Macro to specify list of unit tests in the suite. 42 | * The unit test implementation MUST provide list of unit tests it implements 43 | * with this macro: 44 | * 45 | * TEST_LIST = { 46 | * { "test1_name", test1_func_ptr }, 47 | * { "test2_name", test2_func_ptr }, 48 | * ... 49 | * { 0 } 50 | * }; 51 | * 52 | * The list specifies names of each test (must be unique) and pointer to 53 | * a function implementing it. The function does not take any arguments 54 | * and has no return values, i.e. every test function has to be compatible 55 | * with this prototype: 56 | * 57 | * void test_func(void); 58 | */ 59 | #define TEST_LIST const struct test__ test_list__[] 60 | 61 | 62 | /* Macros for testing whether an unit test succeeds or fails. These macros 63 | * can be used arbitrarily in functions implementing the unit tests. 64 | * 65 | * If any condition fails throughout execution of a test, the test fails. 66 | * 67 | * TEST_CHECK takes only one argument (the condition), TEST_CHECK_ allows 68 | * also to specify an error message to print out if the condition fails. 69 | * (It expects printf-like format string and its parameters). The macros 70 | * return non-zero (condition passes) or 0 (condition fails). 71 | * 72 | * That can be useful when more conditions should be checked only if some 73 | * preceding condition passes, as illustrated in this code snippet: 74 | * 75 | * SomeStruct* ptr = allocate_some_struct(); 76 | * if(TEST_CHECK(ptr != NULL)) { 77 | * TEST_CHECK(ptr->member1 < 100); 78 | * TEST_CHECK(ptr->member2 > 200); 79 | * } 80 | */ 81 | #define TEST_CHECK_(cond,...) test_check__((cond), __FILE__, __LINE__, __VA_ARGS__) 82 | #define TEST_CHECK(cond) test_check__((cond), __FILE__, __LINE__, "%s", #cond) 83 | 84 | #ifdef __cplusplus 85 | /* Macros to verify that the code (the 1st argument) throws exception of given 86 | * type (the 2nd argument). (Note these macros are only available in C++.) 87 | * 88 | * TEST_EXCEPTION_ is like TEST_EXCEPTION but accepts custom printf-like 89 | * message. 90 | * 91 | * For example: 92 | * 93 | * TEST_EXCEPTION(function_that_throw(), ExpectedExceptionType); 94 | * 95 | * If the function_that_throw() throws ExpectedExceptionType, the check passes. 96 | * If the function throws anything incompatible with ExpectedExceptionType 97 | * (or if it does not thrown an exception at all), the check fails. 98 | */ 99 | #define TEST_EXCEPTION(code, exctype) \ 100 | do { \ 101 | bool exc_ok__ = false; \ 102 | const char *msg__ = NULL; \ 103 | try { \ 104 | code; \ 105 | msg__ = "No exception thrown."; \ 106 | } catch(exctype const&) { \ 107 | exc_ok__= true; \ 108 | } catch(...) { \ 109 | msg__ = "Unexpected exception thrown."; \ 110 | } \ 111 | test_check__(exc_ok__, __FILE__, __LINE__, #code " throws " #exctype); \ 112 | if(msg__ != NULL) \ 113 | test_message__("%s", msg__); \ 114 | } while(0) 115 | #define TEST_EXCEPTION_(code, exctype, ...) \ 116 | do { \ 117 | bool exc_ok__ = false; \ 118 | const char *msg__ = NULL; \ 119 | try { \ 120 | code; \ 121 | msg__ = "No exception thrown."; \ 122 | } catch(exctype const&) { \ 123 | exc_ok__= true; \ 124 | } catch(...) { \ 125 | msg__ = "Unexpected exception thrown."; \ 126 | } \ 127 | test_check__(exc_ok__, __FILE__, __LINE__, __VA_ARGS__); \ 128 | if(msg__ != NULL) \ 129 | test_message__("%s", msg__); \ 130 | } while(0) 131 | #endif /* #ifdef __cplusplus */ 132 | 133 | 134 | /* Sometimes it is useful to split execution of more complex unit tests to some 135 | * smaller parts and associate those parts with some names. 136 | * 137 | * This is especially handy if the given unit test is implemented as a loop 138 | * over some vector of multiple testing inputs. Using these macros allow to use 139 | * sort of subtitle for each iteration of the loop (e.g. outputting the input 140 | * itself or a name associated to it), so that if any TEST_CHECK condition 141 | * fails in the loop, it can be easily seen which iteration triggers the 142 | * failure, without the need to manually output the iteration-specific data in 143 | * every single TEST_CHECK inside the loop body. 144 | * 145 | * TEST_CASE allows to specify only single string as the name of the case, 146 | * TEST_CASE_ provides all the power of printf-like string formatting. 147 | * 148 | * Note that the test cases cannot be nested. Starting a new test case ends 149 | * implicitly the previous one. To end the test case explicitly (e.g. to end 150 | * the last test case after exiting the loop), you may use TEST_CASE(NULL). 151 | */ 152 | #define TEST_CASE_(...) test_case__(__VA_ARGS__) 153 | #define TEST_CASE(name) test_case__("%s", name); 154 | 155 | 156 | /* printf-like macro for outputting an extra information about a failure. 157 | * 158 | * Intended use is to output some computed output versus the expected value, 159 | * e.g. like this: 160 | * 161 | * if(!TEST_CHECK(produced == expected)) { 162 | * TEST_MSG("Expected: %d", expected); 163 | * TEST_MSG("Produced: %d", produced); 164 | * } 165 | * 166 | * Note the message is only written down if the most recent use of any checking 167 | * macro (like e.g. TEST_CHECK or TEST_EXCEPTION) in the current test failed. 168 | * This means the above is equivalent to just this: 169 | * 170 | * TEST_CHECK(produced == expected); 171 | * TEST_MSG("Expected: %d", expected); 172 | * TEST_MSG("Produced: %d", produced); 173 | * 174 | * The macro can deal with multi-line output fairly well. It also automatically 175 | * adds a final new-line if there is none present. 176 | */ 177 | #define TEST_MSG(...) test_message__(__VA_ARGS__) 178 | 179 | 180 | /* Maximal output per TEST_MSG call. Longer messages are cut. 181 | * You may define another limit prior including "acutest.h" 182 | */ 183 | #ifndef TEST_MSG_MAXSIZE 184 | #define TEST_MSG_MAXSIZE 1024 185 | #endif 186 | 187 | 188 | /* Macro for dumping a block of memory. 189 | * 190 | * Its intended use is very similar to what TEST_MSG is for, but instead of 191 | * generating any printf-like message, this is for dumping raw block of a 192 | * memory in a hexadecimal form: 193 | * 194 | * TEST_CHECK(size_produced == size_expected && memcmp(addr_produced, addr_expected, size_produced) == 0); 195 | * TEST_DUMP("Expected:", addr_expected, size_expected); 196 | * TEST_DUMP("Produced:", addr_produced, size_produced); 197 | */ 198 | #define TEST_DUMP(title, addr, size) test_dump__(title, addr, size) 199 | 200 | /* Maximal output per TEST_DUMP call (in bytes to dump). Longer blocks are cut. 201 | * You may define another limit prior including "acutest.h" 202 | */ 203 | #ifndef TEST_DUMP_MAXSIZE 204 | #define TEST_DUMP_MAXSIZE 1024 205 | #endif 206 | 207 | 208 | /********************** 209 | *** Implementation *** 210 | **********************/ 211 | 212 | /* The unit test files should not rely on anything below. */ 213 | 214 | #include 215 | #include 216 | #include 217 | #include 218 | #include 219 | 220 | #if defined(unix) || defined(__unix__) || defined(__unix) || defined(__APPLE__) 221 | #define ACUTEST_UNIX__ 1 222 | #include 223 | #include 224 | #include 225 | #include 226 | #include 227 | #include 228 | #include 229 | 230 | #if defined CLOCK_PROCESS_CPUTIME_ID && defined CLOCK_MONOTONIC 231 | #define ACUTEST_HAS_POSIX_TIMER__ 1 232 | #endif 233 | #endif 234 | 235 | #if defined(__gnu_linux__) 236 | #define ACUTEST_LINUX__ 1 237 | #include 238 | #include 239 | #endif 240 | 241 | #if defined(_WIN32) || defined(__WIN32__) || defined(__WINDOWS__) 242 | #define ACUTEST_WIN__ 1 243 | #include 244 | #include 245 | #endif 246 | 247 | #ifdef __cplusplus 248 | #include 249 | #endif 250 | 251 | 252 | /* Note our global private identifiers end with '__' to mitigate risk of clash 253 | * with the unit tests implementation. */ 254 | 255 | 256 | #ifdef __cplusplus 257 | extern "C" { 258 | #endif 259 | 260 | 261 | struct test__ { 262 | const char* name; 263 | void (*func)(void); 264 | }; 265 | 266 | struct test_detail__ { 267 | unsigned char flags; 268 | double duration; 269 | }; 270 | 271 | enum { 272 | TEST_FLAG_RUN__ = 1 << 0, 273 | TEST_FLAG_SUCCESS__ = 1 << 1, 274 | TEST_FLAG_FAILURE__ = 1 << 2, 275 | }; 276 | 277 | extern const struct test__ test_list__[]; 278 | 279 | int test_check__(int cond, const char* file, int line, const char* fmt, ...); 280 | void test_case__(const char* fmt, ...); 281 | void test_message__(const char* fmt, ...); 282 | void test_dump__(const char* title, const void* addr, size_t size); 283 | 284 | 285 | #ifndef TEST_NO_MAIN 286 | 287 | static char* test_argv0__ = NULL; 288 | static size_t test_list_size__ = 0; 289 | static struct test_detail__ *test_details__ = NULL; 290 | static size_t test_count__ = 0; 291 | static int test_no_exec__ = -1; 292 | static int test_no_summary__ = 0; 293 | static int test_tap__ = 0; 294 | static int test_skip_mode__ = 0; 295 | static int test_worker__ = 0; 296 | static int test_worker_index__ = 0; 297 | static int test_cond_failed__ = 0; 298 | static FILE *test_xml_output__ = NULL; 299 | 300 | static int test_stat_failed_units__ = 0; 301 | static int test_stat_run_units__ = 0; 302 | 303 | static const struct test__* test_current_unit__ = NULL; 304 | static int test_current_index__ = 0; 305 | static char test_case_name__[64] = ""; 306 | static int test_current_already_logged__ = 0; 307 | static int test_case_current_already_logged__ = 0; 308 | static int test_verbose_level__ = 2; 309 | static int test_current_failures__ = 0; 310 | static int test_colorize__ = 0; 311 | static int test_timer__ = 0; 312 | 313 | #if defined ACUTEST_WIN__ 314 | typedef LARGE_INTEGER test_timer_type__; 315 | static LARGE_INTEGER test_timer_freq__; 316 | static test_timer_type__ test_timer_start__; 317 | static test_timer_type__ test_timer_end__; 318 | 319 | static void 320 | test_timer_init__(void) 321 | { 322 | QueryPerformanceFrequency(&test_timer_freq__); 323 | } 324 | 325 | static void 326 | test_timer_get_time__(LARGE_INTEGER* ts) 327 | { 328 | QueryPerformanceCounter(ts); 329 | } 330 | 331 | static double 332 | test_timer_diff__(LARGE_INTEGER start, LARGE_INTEGER end) 333 | { 334 | double duration = end.QuadPart - start.QuadPart; 335 | duration /= test_timer_freq__.QuadPart; 336 | return duration; 337 | } 338 | 339 | static void 340 | test_timer_print_diff__(void) 341 | { 342 | printf("%.6lf secs", test_timer_diff__(test_timer_start__, test_timer_end__)); 343 | } 344 | #elif defined ACUTEST_HAS_POSIX_TIMER__ 345 | static clockid_t test_timer_id__; 346 | typedef struct timespec test_timer_type__; 347 | static test_timer_type__ test_timer_start__; 348 | static test_timer_type__ test_timer_end__; 349 | 350 | static void 351 | test_timer_init__(void) 352 | { 353 | if(test_timer__ == 1) 354 | #ifdef CLOCK_MONOTONIC_RAW 355 | /* linux specific; not subject of NTP adjustments or adjtime() */ 356 | test_timer_id__ = CLOCK_MONOTONIC_RAW; 357 | #else 358 | test_timer_id__ = CLOCK_MONOTONIC; 359 | #endif 360 | else if(test_timer__ == 2) 361 | test_timer_id__ = CLOCK_PROCESS_CPUTIME_ID; 362 | } 363 | 364 | static void 365 | test_timer_get_time__(struct timespec* ts) 366 | { 367 | clock_gettime(test_timer_id__, ts); 368 | } 369 | 370 | static double 371 | test_timer_diff__(struct timespec start, struct timespec end) 372 | { 373 | return ((double) end.tv_sec + 374 | (double) end.tv_nsec * 10e-9) 375 | - 376 | ((double) start.tv_sec + 377 | (double) start.tv_nsec * 10e-9); 378 | } 379 | 380 | static void 381 | test_timer_print_diff__(void) 382 | { 383 | printf("%.6lf secs", 384 | test_timer_diff__(test_timer_start__, test_timer_end__)); 385 | } 386 | #else 387 | typedef int test_timer_type__; 388 | static test_timer_type__ test_timer_start__; 389 | static test_timer_type__ test_timer_end__; 390 | 391 | void 392 | test_timer_init__(void) 393 | {} 394 | 395 | static void 396 | test_timer_get_time__(int* ts) 397 | { 398 | (void) ts; 399 | } 400 | 401 | static double 402 | test_timer_diff__(int start, int end) 403 | { 404 | (void) start; 405 | (void) end; 406 | return 0.0; 407 | } 408 | 409 | static void 410 | test_timer_print_diff__(void) 411 | {} 412 | #endif 413 | 414 | #define TEST_COLOR_DEFAULT__ 0 415 | #define TEST_COLOR_GREEN__ 1 416 | #define TEST_COLOR_RED__ 2 417 | #define TEST_COLOR_DEFAULT_INTENSIVE__ 3 418 | #define TEST_COLOR_GREEN_INTENSIVE__ 4 419 | #define TEST_COLOR_RED_INTENSIVE__ 5 420 | 421 | static int 422 | test_print_in_color__(int color, const char* fmt, ...) 423 | { 424 | va_list args; 425 | char buffer[256]; 426 | int n; 427 | 428 | va_start(args, fmt); 429 | vsnprintf(buffer, sizeof(buffer), fmt, args); 430 | va_end(args); 431 | buffer[sizeof(buffer)-1] = '\0'; 432 | 433 | if(!test_colorize__) { 434 | return printf("%s", buffer); 435 | } 436 | 437 | #if defined ACUTEST_UNIX__ 438 | { 439 | const char* col_str; 440 | switch(color) { 441 | case TEST_COLOR_GREEN__: col_str = "\033[0;32m"; break; 442 | case TEST_COLOR_RED__: col_str = "\033[0;31m"; break; 443 | case TEST_COLOR_GREEN_INTENSIVE__: col_str = "\033[1;32m"; break; 444 | case TEST_COLOR_RED_INTENSIVE__: col_str = "\033[1;31m"; break; 445 | case TEST_COLOR_DEFAULT_INTENSIVE__: col_str = "\033[1m"; break; 446 | default: col_str = "\033[0m"; break; 447 | } 448 | printf("%s", col_str); 449 | n = printf("%s", buffer); 450 | printf("\033[0m"); 451 | return n; 452 | } 453 | #elif defined ACUTEST_WIN__ 454 | { 455 | HANDLE h; 456 | CONSOLE_SCREEN_BUFFER_INFO info; 457 | WORD attr; 458 | 459 | h = GetStdHandle(STD_OUTPUT_HANDLE); 460 | GetConsoleScreenBufferInfo(h, &info); 461 | 462 | switch(color) { 463 | case TEST_COLOR_GREEN__: attr = FOREGROUND_GREEN; break; 464 | case TEST_COLOR_RED__: attr = FOREGROUND_RED; break; 465 | case TEST_COLOR_GREEN_INTENSIVE__: attr = FOREGROUND_GREEN | FOREGROUND_INTENSITY; break; 466 | case TEST_COLOR_RED_INTENSIVE__: attr = FOREGROUND_RED | FOREGROUND_INTENSITY; break; 467 | case TEST_COLOR_DEFAULT_INTENSIVE__: attr = FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_INTENSITY; break; 468 | default: attr = 0; break; 469 | } 470 | if(attr != 0) 471 | SetConsoleTextAttribute(h, attr); 472 | n = printf("%s", buffer); 473 | SetConsoleTextAttribute(h, info.wAttributes); 474 | return n; 475 | } 476 | #else 477 | n = printf("%s", buffer); 478 | return n; 479 | #endif 480 | } 481 | 482 | static void 483 | test_begin_test_line__(const struct test__* test) 484 | { 485 | if(!test_tap__) { 486 | if(test_verbose_level__ >= 3) { 487 | test_print_in_color__(TEST_COLOR_DEFAULT_INTENSIVE__, "Test %s:\n", test->name); 488 | test_current_already_logged__++; 489 | } else if(test_verbose_level__ >= 1) { 490 | int n; 491 | char spaces[48]; 492 | 493 | n = test_print_in_color__(TEST_COLOR_DEFAULT_INTENSIVE__, "Test %s... ", test->name); 494 | memset(spaces, ' ', sizeof(spaces)); 495 | if(n < (int) sizeof(spaces)) 496 | printf("%.*s", (int) sizeof(spaces) - n, spaces); 497 | } else { 498 | test_current_already_logged__ = 1; 499 | } 500 | } 501 | } 502 | 503 | static void 504 | test_finish_test_line__(int result) 505 | { 506 | if(test_tap__) { 507 | const char* str = (result == 0) ? "ok" : "not ok"; 508 | 509 | printf("%s %u - %s\n", str, test_current_index__ + 1, test_current_unit__->name); 510 | 511 | if(result == 0 && test_timer__) { 512 | printf("# Duration: "); 513 | test_timer_print_diff__(); 514 | printf("\n"); 515 | } 516 | } else { 517 | int color = (result == 0) ? TEST_COLOR_GREEN_INTENSIVE__ : TEST_COLOR_RED_INTENSIVE__; 518 | const char* str = (result == 0) ? "OK" : "FAILED"; 519 | printf("[ "); 520 | test_print_in_color__(color, str); 521 | printf(" ]"); 522 | 523 | if(result == 0 && test_timer__) { 524 | printf(" "); 525 | test_timer_print_diff__(); 526 | } 527 | 528 | printf("\n"); 529 | } 530 | } 531 | 532 | static void 533 | test_line_indent__(int level) 534 | { 535 | static const char spaces[] = " "; 536 | int n = level * 2; 537 | 538 | if(test_tap__ && n > 0) { 539 | n--; 540 | printf("#"); 541 | } 542 | 543 | while(n > 16) { 544 | printf("%s", spaces); 545 | n -= 16; 546 | } 547 | printf("%.*s", n, spaces); 548 | } 549 | 550 | int 551 | test_check__(int cond, const char* file, int line, const char* fmt, ...) 552 | { 553 | const char *result_str; 554 | int result_color; 555 | int verbose_level; 556 | 557 | if(cond) { 558 | result_str = "ok"; 559 | result_color = TEST_COLOR_GREEN__; 560 | verbose_level = 3; 561 | } else { 562 | if(!test_current_already_logged__ && test_current_unit__ != NULL) 563 | test_finish_test_line__(-1); 564 | 565 | result_str = "failed"; 566 | result_color = TEST_COLOR_RED__; 567 | verbose_level = 2; 568 | test_current_failures__++; 569 | test_current_already_logged__++; 570 | } 571 | 572 | if(test_verbose_level__ >= verbose_level) { 573 | va_list args; 574 | 575 | if(!test_case_current_already_logged__ && test_case_name__[0]) { 576 | test_line_indent__(1); 577 | test_print_in_color__(TEST_COLOR_DEFAULT_INTENSIVE__, "Case %s:\n", test_case_name__); 578 | test_current_already_logged__++; 579 | test_case_current_already_logged__++; 580 | } 581 | 582 | test_line_indent__(test_case_name__[0] ? 2 : 1); 583 | if(file != NULL) { 584 | if(test_verbose_level__ < 3) { 585 | #ifdef ACUTEST_WIN__ 586 | const char* lastsep1 = strrchr(file, '\\'); 587 | const char* lastsep2 = strrchr(file, '/'); 588 | if(lastsep1 == NULL) 589 | lastsep1 = file-1; 590 | if(lastsep2 == NULL) 591 | lastsep2 = file-1; 592 | file = (lastsep1 > lastsep2 ? lastsep1 : lastsep2) + 1; 593 | #else 594 | const char* lastsep = strrchr(file, '/'); 595 | if(lastsep != NULL) 596 | file = lastsep+1; 597 | #endif 598 | } 599 | printf("%s:%d: Check ", file, line); 600 | } 601 | 602 | va_start(args, fmt); 603 | vprintf(fmt, args); 604 | va_end(args); 605 | 606 | printf("... "); 607 | test_print_in_color__(result_color, result_str); 608 | printf("\n"); 609 | test_current_already_logged__++; 610 | } 611 | 612 | test_cond_failed__ = (cond == 0); 613 | return !test_cond_failed__; 614 | } 615 | 616 | void 617 | test_case__(const char* fmt, ...) 618 | { 619 | va_list args; 620 | 621 | if(test_verbose_level__ < 2) 622 | return; 623 | 624 | if(test_case_name__[0]) { 625 | test_case_current_already_logged__ = 0; 626 | test_case_name__[0] = '\0'; 627 | } 628 | 629 | if(fmt == NULL) 630 | return; 631 | 632 | va_start(args, fmt); 633 | vsnprintf(test_case_name__, sizeof(test_case_name__) - 1, fmt, args); 634 | va_end(args); 635 | test_case_name__[sizeof(test_case_name__) - 1] = '\0'; 636 | 637 | if(test_verbose_level__ >= 3) { 638 | test_line_indent__(1); 639 | test_print_in_color__(TEST_COLOR_DEFAULT_INTENSIVE__, "Case %s:\n", test_case_name__); 640 | test_current_already_logged__++; 641 | test_case_current_already_logged__++; 642 | } 643 | } 644 | 645 | void 646 | test_message__(const char* fmt, ...) 647 | { 648 | char buffer[TEST_MSG_MAXSIZE]; 649 | char* line_beg; 650 | char* line_end; 651 | va_list args; 652 | 653 | if(test_verbose_level__ < 2) 654 | return; 655 | 656 | /* We allow extra message only when something is already wrong in the 657 | * current test. */ 658 | if(test_current_unit__ == NULL || !test_cond_failed__) 659 | return; 660 | 661 | va_start(args, fmt); 662 | vsnprintf(buffer, TEST_MSG_MAXSIZE, fmt, args); 663 | va_end(args); 664 | buffer[TEST_MSG_MAXSIZE-1] = '\0'; 665 | 666 | line_beg = buffer; 667 | while(1) { 668 | line_end = strchr(line_beg, '\n'); 669 | if(line_end == NULL) 670 | break; 671 | test_line_indent__(test_case_name__[0] ? 3 : 2); 672 | printf("%.*s\n", (int)(line_end - line_beg), line_beg); 673 | line_beg = line_end + 1; 674 | } 675 | if(line_beg[0] != '\0') { 676 | test_line_indent__(test_case_name__[0] ? 3 : 2); 677 | printf("%s\n", line_beg); 678 | } 679 | } 680 | 681 | void 682 | test_dump__(const char* title, const void* addr, size_t size) 683 | { 684 | static const size_t BYTES_PER_LINE = 16; 685 | size_t line_beg; 686 | size_t truncate = 0; 687 | 688 | if(test_verbose_level__ < 2) 689 | return; 690 | 691 | /* We allow extra message only when something is already wrong in the 692 | * current test. */ 693 | if(test_current_unit__ == NULL || !test_cond_failed__) 694 | return; 695 | 696 | if(size > TEST_DUMP_MAXSIZE) { 697 | truncate = size - TEST_DUMP_MAXSIZE; 698 | size = TEST_DUMP_MAXSIZE; 699 | } 700 | 701 | test_line_indent__(test_case_name__[0] ? 3 : 2); 702 | printf((title[strlen(title)-1] == ':') ? "%s\n" : "%s:\n", title); 703 | 704 | for(line_beg = 0; line_beg < size; line_beg += BYTES_PER_LINE) { 705 | size_t line_end = line_beg + BYTES_PER_LINE; 706 | size_t off; 707 | 708 | test_line_indent__(test_case_name__[0] ? 4 : 3); 709 | printf("%08lx: ", (unsigned long)line_beg); 710 | for(off = line_beg; off < line_end; off++) { 711 | if(off < size) 712 | printf(" %02x", ((unsigned char*)addr)[off]); 713 | else 714 | printf(" "); 715 | } 716 | 717 | printf(" "); 718 | for(off = line_beg; off < line_end; off++) { 719 | unsigned char byte = ((unsigned char*)addr)[off]; 720 | if(off < size) 721 | printf("%c", (iscntrl(byte) ? '.' : byte)); 722 | else 723 | break; 724 | } 725 | 726 | printf("\n"); 727 | } 728 | 729 | if(truncate > 0) { 730 | test_line_indent__(test_case_name__[0] ? 4 : 3); 731 | printf(" ... (and more %u bytes)\n", (unsigned) truncate); 732 | } 733 | } 734 | 735 | static void 736 | test_list_names__(void) 737 | { 738 | const struct test__* test; 739 | 740 | printf("Unit tests:\n"); 741 | for(test = &test_list__[0]; test->func != NULL; test++) 742 | printf(" %s\n", test->name); 743 | } 744 | 745 | static void 746 | test_remember__(int i) 747 | { 748 | if(test_details__[i].flags & TEST_FLAG_RUN__) 749 | return; 750 | 751 | test_details__[i].flags |= TEST_FLAG_RUN__; 752 | test_count__++; 753 | } 754 | 755 | static void 756 | test_set_success__(int i, int success) 757 | { 758 | test_details__[i].flags |= success ? TEST_FLAG_SUCCESS__ : TEST_FLAG_FAILURE__; 759 | } 760 | 761 | static void 762 | test_set_duration__(int i, double duration) 763 | { 764 | test_details__[i].duration = duration; 765 | } 766 | 767 | static int 768 | test_name_contains_word__(const char* name, const char* pattern) 769 | { 770 | static const char word_delim[] = " \t-_."; 771 | const char* substr; 772 | size_t pattern_len; 773 | int starts_on_word_boundary; 774 | int ends_on_word_boundary; 775 | 776 | pattern_len = strlen(pattern); 777 | 778 | substr = strstr(name, pattern); 779 | while(substr != NULL) { 780 | starts_on_word_boundary = (substr == name || strchr(word_delim, substr[-1]) != NULL); 781 | ends_on_word_boundary = (substr[pattern_len] == '\0' || strchr(word_delim, substr[pattern_len]) != NULL); 782 | 783 | if(starts_on_word_boundary && ends_on_word_boundary) 784 | return 1; 785 | 786 | substr = strstr(substr+1, pattern); 787 | } 788 | 789 | return 0; 790 | } 791 | 792 | static int 793 | test_lookup__(const char* pattern) 794 | { 795 | int i; 796 | int n = 0; 797 | 798 | /* Try exact match. */ 799 | for(i = 0; i < (int) test_list_size__; i++) { 800 | if(strcmp(test_list__[i].name, pattern) == 0) { 801 | test_remember__(i); 802 | n++; 803 | break; 804 | } 805 | } 806 | if(n > 0) 807 | return n; 808 | 809 | /* Try word match. */ 810 | for(i = 0; i < (int) test_list_size__; i++) { 811 | if(test_name_contains_word__(test_list__[i].name, pattern)) { 812 | test_remember__(i); 813 | n++; 814 | } 815 | } 816 | if(n > 0) 817 | return n; 818 | 819 | /* Try relaxed match. */ 820 | for(i = 0; i < (int) test_list_size__; i++) { 821 | if(strstr(test_list__[i].name, pattern) != NULL) { 822 | test_remember__(i); 823 | n++; 824 | } 825 | } 826 | 827 | return n; 828 | } 829 | 830 | 831 | /* Called if anything goes bad in Acutest, or if the unit test ends in other 832 | * way then by normal returning from its function (e.g. exception or some 833 | * abnormal child process termination). */ 834 | static void 835 | test_error__(const char* fmt, ...) 836 | { 837 | va_list args; 838 | 839 | if(test_verbose_level__ == 0) 840 | return; 841 | 842 | if(test_verbose_level__ <= 2 && !test_current_already_logged__ && test_current_unit__ != NULL) { 843 | if(test_tap__) { 844 | test_finish_test_line__(-1); 845 | } else { 846 | printf("[ "); 847 | test_print_in_color__(TEST_COLOR_RED_INTENSIVE__, "FAILED"); 848 | printf(" ]\n"); 849 | } 850 | } 851 | 852 | if(test_verbose_level__ >= 2) { 853 | test_line_indent__(1); 854 | if(test_verbose_level__ >= 3) 855 | test_print_in_color__(TEST_COLOR_RED_INTENSIVE__, "ERROR: "); 856 | va_start(args, fmt); 857 | vprintf(fmt, args); 858 | va_end(args); 859 | printf("\n"); 860 | } 861 | 862 | if(test_verbose_level__ >= 3) { 863 | printf("\n"); 864 | } 865 | } 866 | 867 | /* Call directly the given test unit function. */ 868 | static int 869 | test_do_run__(const struct test__* test, int index) 870 | { 871 | test_current_unit__ = test; 872 | test_current_index__ = index; 873 | test_current_failures__ = 0; 874 | test_current_already_logged__ = 0; 875 | test_cond_failed__ = 0; 876 | 877 | test_begin_test_line__(test); 878 | 879 | #ifdef __cplusplus 880 | try { 881 | #endif 882 | 883 | /* This is good to do for case the test unit e.g. crashes. */ 884 | fflush(stdout); 885 | fflush(stderr); 886 | 887 | test_timer_get_time__(&test_timer_start__); 888 | test->func(); 889 | test_timer_get_time__(&test_timer_end__); 890 | 891 | if(test_verbose_level__ >= 3) { 892 | test_line_indent__(1); 893 | if(test_current_failures__ == 0) { 894 | test_print_in_color__(TEST_COLOR_GREEN_INTENSIVE__, "SUCCESS: "); 895 | printf("All conditions have passed.\n"); 896 | 897 | if(test_timer__) { 898 | test_line_indent__(1); 899 | printf("Duration: "); 900 | test_timer_print_diff__(); 901 | printf("\n"); 902 | } 903 | } else { 904 | test_print_in_color__(TEST_COLOR_RED_INTENSIVE__, "FAILED: "); 905 | printf("%d condition%s %s failed.\n", 906 | test_current_failures__, 907 | (test_current_failures__ == 1) ? "" : "s", 908 | (test_current_failures__ == 1) ? "has" : "have"); 909 | } 910 | printf("\n"); 911 | } else if(test_verbose_level__ >= 1 && test_current_failures__ == 0) { 912 | test_finish_test_line__(0); 913 | } 914 | 915 | test_case__(NULL); 916 | test_current_unit__ = NULL; 917 | return (test_current_failures__ == 0) ? 0 : -1; 918 | 919 | #ifdef __cplusplus 920 | } catch(std::exception& e) { 921 | const char* what = e.what(); 922 | if(what != NULL) 923 | test_error__("Threw std::exception: %s", what); 924 | else 925 | test_error__("Threw std::exception"); 926 | return -1; 927 | } catch(...) { 928 | test_error__("Threw an exception"); 929 | return -1; 930 | } 931 | #endif 932 | } 933 | 934 | /* Trigger the unit test. If possible (and not suppressed) it starts a child 935 | * process who calls test_do_run__(), otherwise it calls test_do_run__() 936 | * directly. */ 937 | static void 938 | test_run__(const struct test__* test, int index, int master_index) 939 | { 940 | int failed = 1; 941 | test_timer_type__ start, end; 942 | 943 | test_current_unit__ = test; 944 | test_current_already_logged__ = 0; 945 | test_timer_get_time__(&start); 946 | 947 | if(!test_no_exec__) { 948 | 949 | #if defined(ACUTEST_UNIX__) 950 | 951 | pid_t pid; 952 | int exit_code; 953 | 954 | /* Make sure the child starts with empty I/O buffers. */ 955 | fflush(stdout); 956 | fflush(stderr); 957 | 958 | pid = fork(); 959 | if(pid == (pid_t)-1) { 960 | test_error__("Cannot fork. %s [%d]", strerror(errno), errno); 961 | failed = 1; 962 | } else if(pid == 0) { 963 | /* Child: Do the test. */ 964 | failed = (test_do_run__(test, index) != 0); 965 | exit(failed ? 1 : 0); 966 | } else { 967 | /* Parent: Wait until child terminates and analyze its exit code. */ 968 | waitpid(pid, &exit_code, 0); 969 | if(WIFEXITED(exit_code)) { 970 | switch(WEXITSTATUS(exit_code)) { 971 | case 0: failed = 0; break; /* test has passed. */ 972 | case 1: /* noop */ break; /* "normal" failure. */ 973 | default: test_error__("Unexpected exit code [%d]", WEXITSTATUS(exit_code)); 974 | } 975 | } else if(WIFSIGNALED(exit_code)) { 976 | char tmp[32]; 977 | const char* signame; 978 | switch(WTERMSIG(exit_code)) { 979 | case SIGINT: signame = "SIGINT"; break; 980 | case SIGHUP: signame = "SIGHUP"; break; 981 | case SIGQUIT: signame = "SIGQUIT"; break; 982 | case SIGABRT: signame = "SIGABRT"; break; 983 | case SIGKILL: signame = "SIGKILL"; break; 984 | case SIGSEGV: signame = "SIGSEGV"; break; 985 | case SIGILL: signame = "SIGILL"; break; 986 | case SIGTERM: signame = "SIGTERM"; break; 987 | default: sprintf(tmp, "signal %d", WTERMSIG(exit_code)); signame = tmp; break; 988 | } 989 | test_error__("Test interrupted by %s", signame); 990 | } else { 991 | test_error__("Test ended in an unexpected way [%d]", exit_code); 992 | } 993 | } 994 | 995 | #elif defined(ACUTEST_WIN__) 996 | 997 | char buffer[512] = {0}; 998 | STARTUPINFOA startupInfo; 999 | PROCESS_INFORMATION processInfo; 1000 | DWORD exitCode; 1001 | 1002 | /* Windows has no fork(). So we propagate all info into the child 1003 | * through a command line arguments. */ 1004 | _snprintf(buffer, sizeof(buffer)-1, 1005 | "%s --worker=%d %s --no-exec --no-summary %s --verbose=%d --color=%s -- \"%s\"", 1006 | test_argv0__, index, test_timer__ ? "--timer" : "", 1007 | test_tap__ ? "--tap" : "", test_verbose_level__, 1008 | test_colorize__ ? "always" : "never", 1009 | test->name); 1010 | memset(&startupInfo, 0, sizeof(startupInfo)); 1011 | startupInfo.cb = sizeof(STARTUPINFO); 1012 | if(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0, NULL, NULL, &startupInfo, &processInfo)) { 1013 | WaitForSingleObject(processInfo.hProcess, INFINITE); 1014 | GetExitCodeProcess(processInfo.hProcess, &exitCode); 1015 | CloseHandle(processInfo.hThread); 1016 | CloseHandle(processInfo.hProcess); 1017 | failed = (exitCode != 0); 1018 | } else { 1019 | test_error__("Cannot create unit test subprocess [%ld].", GetLastError()); 1020 | failed = 1; 1021 | } 1022 | 1023 | #else 1024 | 1025 | /* A platform where we don't know how to run child process. */ 1026 | failed = (test_do_run__(test, index) != 0); 1027 | 1028 | #endif 1029 | 1030 | } else { 1031 | /* Child processes suppressed through --no-exec. */ 1032 | failed = (test_do_run__(test, index) != 0); 1033 | } 1034 | test_timer_get_time__(&end); 1035 | 1036 | test_current_unit__ = NULL; 1037 | 1038 | test_stat_run_units__++; 1039 | if(failed) 1040 | test_stat_failed_units__++; 1041 | 1042 | test_set_success__(master_index, !failed); 1043 | test_set_duration__(master_index, test_timer_diff__(start, end)); 1044 | } 1045 | 1046 | #if defined(ACUTEST_WIN__) 1047 | /* Callback for SEH events. */ 1048 | static LONG CALLBACK 1049 | test_exception_filter__(EXCEPTION_POINTERS *ptrs) 1050 | { 1051 | test_error__("Unhandled SEH exception %08lx at %p.", 1052 | ptrs->ExceptionRecord->ExceptionCode, 1053 | ptrs->ExceptionRecord->ExceptionAddress); 1054 | fflush(stdout); 1055 | fflush(stderr); 1056 | return EXCEPTION_EXECUTE_HANDLER; 1057 | } 1058 | #endif 1059 | 1060 | 1061 | #define TEST_CMDLINE_OPTFLAG_OPTIONALARG__ 0x0001 1062 | #define TEST_CMDLINE_OPTFLAG_REQUIREDARG__ 0x0002 1063 | 1064 | #define TEST_CMDLINE_OPTID_NONE__ 0 1065 | #define TEST_CMDLINE_OPTID_UNKNOWN__ (-0x7fffffff + 0) 1066 | #define TEST_CMDLINE_OPTID_MISSINGARG__ (-0x7fffffff + 1) 1067 | #define TEST_CMDLINE_OPTID_BOGUSARG__ (-0x7fffffff + 2) 1068 | 1069 | typedef struct TEST_CMDLINE_OPTION__ { 1070 | char shortname; 1071 | const char* longname; 1072 | int id; 1073 | unsigned flags; 1074 | } TEST_CMDLINE_OPTION__; 1075 | 1076 | static int 1077 | test_cmdline_handle_short_opt_group__(const TEST_CMDLINE_OPTION__* options, 1078 | const char* arggroup, 1079 | int (*callback)(int /*optval*/, const char* /*arg*/)) 1080 | { 1081 | const TEST_CMDLINE_OPTION__* opt; 1082 | int i; 1083 | int ret = 0; 1084 | 1085 | for(i = 0; arggroup[i] != '\0'; i++) { 1086 | for(opt = options; opt->id != 0; opt++) { 1087 | if(arggroup[i] == opt->shortname) 1088 | break; 1089 | } 1090 | 1091 | if(opt->id != 0 && !(opt->flags & TEST_CMDLINE_OPTFLAG_REQUIREDARG__)) { 1092 | ret = callback(opt->id, NULL); 1093 | } else { 1094 | /* Unknown option. */ 1095 | char badoptname[3]; 1096 | badoptname[0] = '-'; 1097 | badoptname[1] = arggroup[i]; 1098 | badoptname[2] = '\0'; 1099 | ret = callback((opt->id != 0 ? TEST_CMDLINE_OPTID_MISSINGARG__ : TEST_CMDLINE_OPTID_UNKNOWN__), 1100 | badoptname); 1101 | } 1102 | 1103 | if(ret != 0) 1104 | break; 1105 | } 1106 | 1107 | return ret; 1108 | } 1109 | 1110 | #define TEST_CMDLINE_AUXBUF_SIZE__ 32 1111 | 1112 | static int 1113 | test_cmdline_read__(const TEST_CMDLINE_OPTION__* options, int argc, char** argv, 1114 | int (*callback)(int /*optval*/, const char* /*arg*/)) 1115 | { 1116 | 1117 | const TEST_CMDLINE_OPTION__* opt; 1118 | char auxbuf[TEST_CMDLINE_AUXBUF_SIZE__+1]; 1119 | int after_doubledash = 0; 1120 | int i = 1; 1121 | int ret = 0; 1122 | 1123 | auxbuf[TEST_CMDLINE_AUXBUF_SIZE__] = '\0'; 1124 | 1125 | while(i < argc) { 1126 | if(after_doubledash || strcmp(argv[i], "-") == 0) { 1127 | /* Non-option argument. */ 1128 | ret = callback(TEST_CMDLINE_OPTID_NONE__, argv[i]); 1129 | } else if(strcmp(argv[i], "--") == 0) { 1130 | /* End of options. All the remaining members are non-option arguments. */ 1131 | after_doubledash = 1; 1132 | } else if(argv[i][0] != '-') { 1133 | /* Non-option argument. */ 1134 | ret = callback(TEST_CMDLINE_OPTID_NONE__, argv[i]); 1135 | } else { 1136 | for(opt = options; opt->id != 0; opt++) { 1137 | if(opt->longname != NULL && strncmp(argv[i], "--", 2) == 0) { 1138 | size_t len = strlen(opt->longname); 1139 | if(strncmp(argv[i]+2, opt->longname, len) == 0) { 1140 | /* Regular long option. */ 1141 | if(argv[i][2+len] == '\0') { 1142 | /* with no argument provided. */ 1143 | if(!(opt->flags & TEST_CMDLINE_OPTFLAG_REQUIREDARG__)) 1144 | ret = callback(opt->id, NULL); 1145 | else 1146 | ret = callback(TEST_CMDLINE_OPTID_MISSINGARG__, argv[i]); 1147 | break; 1148 | } else if(argv[i][2+len] == '=') { 1149 | /* with an argument provided. */ 1150 | if(opt->flags & (TEST_CMDLINE_OPTFLAG_OPTIONALARG__ | TEST_CMDLINE_OPTFLAG_REQUIREDARG__)) { 1151 | ret = callback(opt->id, argv[i]+2+len+1); 1152 | } else { 1153 | sprintf(auxbuf, "--%s", opt->longname); 1154 | ret = callback(TEST_CMDLINE_OPTID_BOGUSARG__, auxbuf); 1155 | } 1156 | break; 1157 | } else { 1158 | continue; 1159 | } 1160 | } 1161 | } else if(opt->shortname != '\0' && argv[i][0] == '-') { 1162 | if(argv[i][1] == opt->shortname) { 1163 | /* Regular short option. */ 1164 | if(opt->flags & TEST_CMDLINE_OPTFLAG_REQUIREDARG__) { 1165 | if(argv[i][2] != '\0') 1166 | ret = callback(opt->id, argv[i]+2); 1167 | else if(i+1 < argc) 1168 | ret = callback(opt->id, argv[++i]); 1169 | else 1170 | ret = callback(TEST_CMDLINE_OPTID_MISSINGARG__, argv[i]); 1171 | break; 1172 | } else { 1173 | ret = callback(opt->id, NULL); 1174 | 1175 | /* There might be more (argument-less) short options 1176 | * grouped together. */ 1177 | if(ret == 0 && argv[i][2] != '\0') 1178 | ret = test_cmdline_handle_short_opt_group__(options, argv[i]+2, callback); 1179 | break; 1180 | } 1181 | } 1182 | } 1183 | } 1184 | 1185 | if(opt->id == 0) { /* still not handled? */ 1186 | if(argv[i][0] != '-') { 1187 | /* Non-option argument. */ 1188 | ret = callback(TEST_CMDLINE_OPTID_NONE__, argv[i]); 1189 | } else { 1190 | /* Unknown option. */ 1191 | char* badoptname = argv[i]; 1192 | 1193 | if(strncmp(badoptname, "--", 2) == 0) { 1194 | /* Strip any argument from the long option. */ 1195 | char* assignment = strchr(badoptname, '='); 1196 | if(assignment != NULL) { 1197 | size_t len = assignment - badoptname; 1198 | if(len > TEST_CMDLINE_AUXBUF_SIZE__) 1199 | len = TEST_CMDLINE_AUXBUF_SIZE__; 1200 | strncpy(auxbuf, badoptname, len); 1201 | auxbuf[len] = '\0'; 1202 | badoptname = auxbuf; 1203 | } 1204 | } 1205 | 1206 | ret = callback(TEST_CMDLINE_OPTID_UNKNOWN__, badoptname); 1207 | } 1208 | } 1209 | } 1210 | 1211 | if(ret != 0) 1212 | return ret; 1213 | i++; 1214 | } 1215 | 1216 | return ret; 1217 | } 1218 | 1219 | static void 1220 | test_help__(void) 1221 | { 1222 | printf("Usage: %s [options] [test...]\n", test_argv0__); 1223 | printf("\n"); 1224 | printf("Run the specified unit tests; or if the option '--skip' is used, run all\n"); 1225 | printf("tests in the suite but those listed. By default, if no tests are specified\n"); 1226 | printf("on the command line, all unit tests in the suite are run.\n"); 1227 | printf("\n"); 1228 | printf("Options:\n"); 1229 | printf(" -s, --skip Execute all unit tests but the listed ones\n"); 1230 | printf(" --exec[=WHEN] If supported, execute unit tests as child processes\n"); 1231 | printf(" (WHEN is one of 'auto', 'always', 'never')\n"); 1232 | #if defined ACUTEST_WIN__ 1233 | printf(" -t, --timer Measure test duration\n"); 1234 | #elif defined ACUTEST_HAS_POSIX_TIMER__ 1235 | printf(" -t, --timer Measure test duration (real time)\n"); 1236 | printf(" --timer=TIMER Measure test duration, using given timer\n"); 1237 | printf(" (TIMER is one of 'real', 'cpu')\n"); 1238 | #endif 1239 | printf(" -E, --no-exec Same as --exec=never\n"); 1240 | printf(" --no-summary Suppress printing of test results summary\n"); 1241 | printf(" --tap Produce TAP-compliant output\n"); 1242 | printf(" (See https://testanything.org/)\n"); 1243 | printf(" -x, --xml-output=FILE Enable XUnit output to the given file\n"); 1244 | printf(" -l, --list List unit tests in the suite and exit\n"); 1245 | printf(" -v, --verbose Make output more verbose\n"); 1246 | printf(" --verbose=LEVEL Set verbose level to LEVEL:\n"); 1247 | printf(" 0 ... Be silent\n"); 1248 | printf(" 1 ... Output one line per test (and summary)\n"); 1249 | printf(" 2 ... As 1 and failed conditions (this is default)\n"); 1250 | printf(" 3 ... As 1 and all conditions (and extended summary)\n"); 1251 | printf(" --color[=WHEN] Enable colorized output\n"); 1252 | printf(" (WHEN is one of 'auto', 'always', 'never')\n"); 1253 | printf(" --no-color Same as --color=never\n"); 1254 | printf(" -h, --help Display this help and exit\n"); 1255 | 1256 | if(test_list_size__ < 16) { 1257 | printf("\n"); 1258 | test_list_names__(); 1259 | } 1260 | } 1261 | 1262 | static const TEST_CMDLINE_OPTION__ test_cmdline_options__[] = { 1263 | { 's', "skip", 's', 0 }, 1264 | { 0, "exec", 'e', TEST_CMDLINE_OPTFLAG_OPTIONALARG__ }, 1265 | { 'E', "no-exec", 'E', 0 }, 1266 | #if defined ACUTEST_WIN__ 1267 | { 't', "timer", 't', 0 }, 1268 | #elif defined ACUTEST_HAS_POSIX_TIMER__ 1269 | { 't', "timer", 't', TEST_CMDLINE_OPTFLAG_OPTIONALARG__ }, 1270 | #endif 1271 | { 0, "no-summary", 'S', 0 }, 1272 | { 0, "tap", 'T', 0 }, 1273 | { 'l', "list", 'l', 0 }, 1274 | { 'v', "verbose", 'v', TEST_CMDLINE_OPTFLAG_OPTIONALARG__ }, 1275 | { 0, "color", 'c', TEST_CMDLINE_OPTFLAG_OPTIONALARG__ }, 1276 | { 0, "no-color", 'C', 0 }, 1277 | { 'h', "help", 'h', 0 }, 1278 | { 0, "worker", 'w', TEST_CMDLINE_OPTFLAG_REQUIREDARG__ }, /* internal */ 1279 | { 'x', "xml-output", 'x', TEST_CMDLINE_OPTFLAG_REQUIREDARG__ }, 1280 | { 0, NULL, 0, 0 } 1281 | }; 1282 | 1283 | static int 1284 | test_cmdline_callback__(int id, const char* arg) 1285 | { 1286 | switch(id) { 1287 | case 's': 1288 | test_skip_mode__ = 1; 1289 | break; 1290 | 1291 | case 'e': 1292 | if(arg == NULL || strcmp(arg, "always") == 0) { 1293 | test_no_exec__ = 0; 1294 | } else if(strcmp(arg, "never") == 0) { 1295 | test_no_exec__ = 1; 1296 | } else if(strcmp(arg, "auto") == 0) { 1297 | /*noop*/ 1298 | } else { 1299 | fprintf(stderr, "%s: Unrecognized argument '%s' for option --exec.\n", test_argv0__, arg); 1300 | fprintf(stderr, "Try '%s --help' for more information.\n", test_argv0__); 1301 | exit(2); 1302 | } 1303 | break; 1304 | 1305 | case 'E': 1306 | test_no_exec__ = 1; 1307 | break; 1308 | 1309 | case 't': 1310 | #if defined ACUTEST_WIN__ || defined ACUTEST_HAS_POSIX_TIMER__ 1311 | if(arg == NULL || strcmp(arg, "real") == 0) { 1312 | test_timer__ = 1; 1313 | #ifndef ACUTEST_WIN__ 1314 | } else if(strcmp(arg, "cpu") == 0) { 1315 | test_timer__ = 2; 1316 | #endif 1317 | } else { 1318 | fprintf(stderr, "%s: Unrecognized argument '%s' for option --timer.\n", test_argv0__, arg); 1319 | fprintf(stderr, "Try '%s --help' for more information.\n", test_argv0__); 1320 | exit(2); 1321 | } 1322 | #endif 1323 | break; 1324 | 1325 | case 'S': 1326 | test_no_summary__ = 1; 1327 | break; 1328 | 1329 | case 'T': 1330 | test_tap__ = 1; 1331 | break; 1332 | 1333 | case 'l': 1334 | test_list_names__(); 1335 | exit(0); 1336 | 1337 | case 'v': 1338 | test_verbose_level__ = (arg != NULL ? atoi(arg) : test_verbose_level__+1); 1339 | break; 1340 | 1341 | case 'c': 1342 | if(arg == NULL || strcmp(arg, "always") == 0) { 1343 | test_colorize__ = 1; 1344 | } else if(strcmp(arg, "never") == 0) { 1345 | test_colorize__ = 0; 1346 | } else if(strcmp(arg, "auto") == 0) { 1347 | /*noop*/ 1348 | } else { 1349 | fprintf(stderr, "%s: Unrecognized argument '%s' for option --color.\n", test_argv0__, arg); 1350 | fprintf(stderr, "Try '%s --help' for more information.\n", test_argv0__); 1351 | exit(2); 1352 | } 1353 | break; 1354 | 1355 | case 'C': 1356 | test_colorize__ = 0; 1357 | break; 1358 | 1359 | case 'h': 1360 | test_help__(); 1361 | exit(0); 1362 | 1363 | case 'w': 1364 | test_worker__ = 1; 1365 | test_worker_index__ = atoi(arg); 1366 | break; 1367 | case 'x': 1368 | test_xml_output__ = fopen(arg, "w"); 1369 | if (!test_xml_output__) { 1370 | fprintf(stderr, "Unable to open '%s': %s\n", arg, strerror(errno)); 1371 | exit(2); 1372 | } 1373 | break; 1374 | 1375 | case 0: 1376 | if(test_lookup__(arg) == 0) { 1377 | fprintf(stderr, "%s: Unrecognized unit test '%s'\n", test_argv0__, arg); 1378 | fprintf(stderr, "Try '%s --list' for list of unit tests.\n", test_argv0__); 1379 | exit(2); 1380 | } 1381 | break; 1382 | 1383 | case TEST_CMDLINE_OPTID_UNKNOWN__: 1384 | fprintf(stderr, "Unrecognized command line option '%s'.\n", arg); 1385 | fprintf(stderr, "Try '%s --help' for more information.\n", test_argv0__); 1386 | exit(2); 1387 | 1388 | case TEST_CMDLINE_OPTID_MISSINGARG__: 1389 | fprintf(stderr, "The command line option '%s' requires an argument.\n", arg); 1390 | fprintf(stderr, "Try '%s --help' for more information.\n", test_argv0__); 1391 | exit(2); 1392 | 1393 | case TEST_CMDLINE_OPTID_BOGUSARG__: 1394 | fprintf(stderr, "The command line option '%s' does not expect an argument.\n", arg); 1395 | fprintf(stderr, "Try '%s --help' for more information.\n", test_argv0__); 1396 | exit(2); 1397 | } 1398 | 1399 | return 0; 1400 | } 1401 | 1402 | 1403 | #ifdef ACUTEST_LINUX__ 1404 | static int 1405 | test_is_tracer_present__(void) 1406 | { 1407 | char buf[256+32+1]; 1408 | int tracer_present = 0; 1409 | int fd; 1410 | ssize_t n_read; 1411 | 1412 | fd = open("/proc/self/status", O_RDONLY); 1413 | if(fd == -1) 1414 | return 0; 1415 | 1416 | n_read = read(fd, buf, sizeof(buf)-1); 1417 | while(n_read > 0) { 1418 | static const char pattern[] = "TracerPid:"; 1419 | const char* field; 1420 | 1421 | buf[n_read] = '\0'; 1422 | field = strstr(buf, pattern); 1423 | if(field != NULL && field < buf + sizeof(buf) - 32) { 1424 | pid_t tracer_pid = (pid_t) atoi(field + sizeof(pattern) - 1); 1425 | tracer_present = (tracer_pid != 0); 1426 | break; 1427 | } 1428 | 1429 | if(n_read == sizeof(buf)-1) { 1430 | memmove(buf, buf + sizeof(buf)-1 - 32, 32); 1431 | n_read = read(fd, buf+32, sizeof(buf)-1-32); 1432 | if(n_read > 0) 1433 | n_read += 32; 1434 | } 1435 | } 1436 | 1437 | close(fd); 1438 | return tracer_present; 1439 | } 1440 | #endif 1441 | 1442 | int 1443 | main(int argc, char** argv) 1444 | { 1445 | int i; 1446 | test_argv0__ = argv[0]; 1447 | 1448 | #if defined ACUTEST_UNIX__ 1449 | test_colorize__ = isatty(STDOUT_FILENO); 1450 | #elif defined ACUTEST_WIN__ 1451 | #if defined __BORLANDC__ 1452 | test_colorize__ = isatty(_fileno(stdout)); 1453 | #else 1454 | test_colorize__ = _isatty(_fileno(stdout)); 1455 | #endif 1456 | #else 1457 | test_colorize__ = 0; 1458 | #endif 1459 | 1460 | test_timer_init__(); 1461 | 1462 | /* Count all test units */ 1463 | test_list_size__ = 0; 1464 | for(i = 0; test_list__[i].func != NULL; i++) 1465 | test_list_size__++; 1466 | 1467 | test_details__ = (struct test_detail__*)calloc(test_list_size__, sizeof(struct test_detail__)); 1468 | if(test_details__ == NULL) { 1469 | fprintf(stderr, "Out of memory.\n"); 1470 | exit(2); 1471 | } 1472 | 1473 | /* Parse options */ 1474 | test_cmdline_read__(test_cmdline_options__, argc, argv, test_cmdline_callback__); 1475 | 1476 | #if defined(ACUTEST_WIN__) 1477 | SetUnhandledExceptionFilter(test_exception_filter__); 1478 | #endif 1479 | 1480 | /* By default, we want to run all tests. */ 1481 | if(test_count__ == 0) { 1482 | for(i = 0; test_list__[i].func != NULL; i++) 1483 | test_remember__(i); 1484 | } 1485 | 1486 | /* Guess whether we want to run unit tests as child processes. */ 1487 | if(test_no_exec__ < 0) { 1488 | test_no_exec__ = 0; 1489 | 1490 | if(test_count__ <= 1) { 1491 | test_no_exec__ = 1; 1492 | } else { 1493 | #ifdef ACUTEST_WIN__ 1494 | if(IsDebuggerPresent()) 1495 | test_no_exec__ = 1; 1496 | #endif 1497 | #ifdef ACUTEST_LINUX__ 1498 | if(test_is_tracer_present__()) 1499 | test_no_exec__ = 1; 1500 | #endif 1501 | } 1502 | } 1503 | 1504 | if(test_tap__) { 1505 | /* TAP requires we know test result ("ok", "not ok") before we output 1506 | * anything about the test, and this gets problematic for larger verbose 1507 | * levels. */ 1508 | if(test_verbose_level__ > 2) 1509 | test_verbose_level__ = 2; 1510 | 1511 | /* TAP harness should provide some summary. */ 1512 | test_no_summary__ = 1; 1513 | 1514 | if(!test_worker__) 1515 | printf("1..%d\n", (int) test_count__); 1516 | } 1517 | 1518 | int index = test_worker_index__; 1519 | for(i = 0; test_list__[i].func != NULL; i++) { 1520 | int run = (test_details__[i].flags & TEST_FLAG_RUN__); 1521 | if (test_skip_mode__) /* Run all tests except those listed. */ 1522 | run = !run; 1523 | if(run) 1524 | test_run__(&test_list__[i], index++, i); 1525 | } 1526 | 1527 | /* Write a summary */ 1528 | if(!test_no_summary__ && test_verbose_level__ >= 1) { 1529 | if(test_verbose_level__ >= 3) { 1530 | test_print_in_color__(TEST_COLOR_DEFAULT_INTENSIVE__, "Summary:\n"); 1531 | 1532 | printf(" Count of all unit tests: %4d\n", (int) test_list_size__); 1533 | printf(" Count of run unit tests: %4d\n", test_stat_run_units__); 1534 | printf(" Count of failed unit tests: %4d\n", test_stat_failed_units__); 1535 | printf(" Count of skipped unit tests: %4d\n", (int) test_list_size__ - test_stat_run_units__); 1536 | } 1537 | 1538 | if(test_stat_failed_units__ == 0) { 1539 | test_print_in_color__(TEST_COLOR_GREEN_INTENSIVE__, "SUCCESS:"); 1540 | printf(" All unit tests have passed.\n"); 1541 | } else { 1542 | test_print_in_color__(TEST_COLOR_RED_INTENSIVE__, "FAILED:"); 1543 | printf(" %d of %d unit tests %s failed.\n", 1544 | test_stat_failed_units__, test_stat_run_units__, 1545 | (test_stat_failed_units__ == 1) ? "has" : "have"); 1546 | } 1547 | 1548 | if(test_verbose_level__ >= 3) 1549 | printf("\n"); 1550 | } 1551 | 1552 | if (test_xml_output__) { 1553 | #if defined ACUTEST_UNIX__ 1554 | char *suite_name = basename(argv[0]); 1555 | #elif defined ACUTEST_WIN__ 1556 | char suite_name[_MAX_FNAME]; 1557 | _splitpath(argv[0], NULL, NULL, suite_name, NULL); 1558 | #else 1559 | const char *suite_name = argv[0]; 1560 | #endif 1561 | fprintf(test_xml_output__, "\n"); 1562 | fprintf(test_xml_output__, "\n", 1563 | suite_name, (int)test_list_size__, test_stat_failed_units__, test_stat_failed_units__, 1564 | (int)test_list_size__ - test_stat_run_units__); 1565 | for(i = 0; test_list__[i].func != NULL; i++) { 1566 | struct test_detail__ *details = &test_details__[i]; 1567 | fprintf(test_xml_output__, " \n", test_list__[i].name, details->duration); 1568 | if (details->flags & TEST_FLAG_FAILURE__) 1569 | fprintf(test_xml_output__, " \n"); 1570 | if (!(details->flags & TEST_FLAG_FAILURE__) && !(details->flags & TEST_FLAG_SUCCESS__)) 1571 | fprintf(test_xml_output__, " \n"); 1572 | fprintf(test_xml_output__, " \n"); 1573 | } 1574 | fprintf(test_xml_output__, "\n"); 1575 | fclose(test_xml_output__); 1576 | } 1577 | 1578 | free((void*) test_details__); 1579 | 1580 | return (test_stat_failed_units__ == 0) ? 0 : 1; 1581 | } 1582 | 1583 | 1584 | #endif /* #ifndef TEST_NO_MAIN */ 1585 | 1586 | #ifdef __cplusplus 1587 | } /* extern "C" */ 1588 | #endif 1589 | 1590 | 1591 | #endif /* #ifndef ACUTEST_H__ */ 1592 | --------------------------------------------------------------------------------