├── .gitignore ├── COPYING ├── README.md ├── doc └── README.md └── src ├── .ivk ├── Forgefile.hsl ├── Forgeutils.hsl ├── Toolsets.hsl ├── cutest.c ├── cutest.h ├── cutest_memory.c ├── cutest_memory.h ├── cutest_mmap.c ├── cutest_mmap.h ├── kutest.h └── test ├── .ivk ├── Forgefile.hsl ├── Toolsets.hsl ├── alien_test_case.c ├── alien_test_case.h └── main.c /.gitignore: -------------------------------------------------------------------------------- 1 | */lib/ 2 | */obj/ 3 | src/test/obj/ 4 | src/test/bin/ -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Cutest 2 | 3 | Cutest is an acronym that can be understood as ``C`` ``u``nit ``test``. 4 | 5 | This library brings a bunch of macros in order to guide the implementation of unit tests for ``C`` projects. 6 | 7 | ``Cutest`` brings a minimal memory leak detection system (fully working in ``Linux``, ``Windows``, ``FreeBSD``, ``Solaris``, ``NetBSD``, ``MINIX`` and ``OpenBSD``). 8 | You can also use cutest to test kernel mode stuff in ``Linux``, ``FreeBSD``, ``NetBSD`` and ``Windows``. 9 | 10 | All supported platforms by this library and other general features are listed on **Table 1**. 11 | 12 | On ``Cutest`` is also possible customize the logs generated by your tests if you want to. 13 | 14 | **Table 1**: Places where I have been running it. A.k.a. supported platforms. In additional, the compiler/linker options to be passed when using ``cutest`` as your unit testing library. 15 | 16 | | **Operating System** | **Has *GNU* Backtracing?** | **Thread safe?** | **Compiler/Linker flags to use (besides ``-lcutest``)** | **Kernel mode** | 17 | |:--------------------:|:--------------------------:|:--------------------:|:---------------------------------------------------------:|:---------------:| 18 | | ``Linux`` | *Yes* | *Yes* | ``-ldl``, ``-lpthread`` | *Yes* | 19 | | ``FreeBSD`` | *Yes* | *Yes* | ``-lexecinfo``, ``-lpthread`` | *Yes* | 20 | | ``NetBSD`` | *Yes* | *Yes* | ``-lexecinfo``, ``-lpthread`` | *Yes* | 21 | | ``OpenBSD`` | *Yes* | *Yes* | ``-lexecinfo``, ``-lpthread`` | *No* | 22 | | ``MINIX`` | *Yes* | *No* | ``-lexecinfo`` | *No* | 23 | | ``Solaris`` | *No* | *Yes* | ``-DNO_CUTEST_BACKTRACING``, ``-lpthread``| *No* | 24 | | ``Windows`` | *Yes* | *Yes* | | *Yes* | 25 | 26 | Of course, that it should run in a bunch of ``UNIX`` boxes, however I prefer listing the places where I actually watched it running 27 | and working pretty fine. If there is a specific non-listed platform that you want to use ``cutest``, let me know, maybe I 28 | can adjust the library's build to accomplish your requirements ;) 29 | 30 | **Remark**: When using cutest to test kernel mode code you do not need compiler flags because all is available in one single 31 | header file. For more details read the documentation. 32 | 33 | ## How to build it 34 | 35 | To build ``cutest`` is necessary to use my own build system called [hefesto](https://github.com/rafael-santiago/hefesto.git). Being 36 | ``Hefesto`` installed in your system all you need to emit on a ``shell`` inside the cutest's ``src`` sub-directory is: 37 | 38 | ``hefesto`` 39 | 40 | After the build a file named ``libcutest.a`` that stands for the library will be generated under the path ``src/lib``. 41 | You should use this file and the header ``src/cutest.h`` to develop your further ``unit tests``. 42 | 43 | Note that the build was written based on ``GCC``. So, you need to have the ``gcc``/``mingw`` installed (and well exported) 44 | in your system before going ahead. 45 | 46 | Maybe you should read the [documentation](https://github.com/rafael-santiago/cutest/blob/master/doc/README.md) before starting. 47 | 48 | **Tip**: In case of trying to compile it under a ``UNIX`` environment with no support for ``PTHREADS`` before calling ``hefesto`` 49 | you need to edit the file ``src/.ivk`` and add the following ``C Macro`` definition ``-DHAS_NO_PTHREAD`` to ``--cflags`` option: 50 | 51 | ``` 52 | --cflags=-DHAS_NO_PTHREAD 53 | ``` 54 | 55 | ### Note for Visual Studio users 56 | 57 | If you want to build ``cutest`` under ``MSVC`` you need to invoke ``Hefesto`` as follows: 58 | 59 | ``` 60 | > hefesto --toolset=msvc-c-lib 61 | ``` 62 | 63 | The command above will build ``lib/libcutestmt.lib``. 64 | 65 | If you want to build ``lib/libcutestmtd.lib``, use: 66 | 67 | ``` 68 | > hefesto --toolset=msvc-c-lib --compile-model=debug 69 | ``` 70 | 71 | On ``64-bit`` environments ``64-bit`` lib artifacts will be built, for ``32-bit``, use: 72 | 73 | ``` 74 | >hefesto --toolset=msvc-c-lib --cpu-arch=x86 75 | ``` 76 | 77 | After run one of the commands listed above, a file named ``libcutest.lib`` will be generated inside ``src/lib`` path. 78 | Note that use ``cutest`` on ``MSVC`` is a little bit tricky. It envolves composite the ``cutest`` with your current ``msvcrt.lib`` 79 | (it explains why the ``cutest.lib`` is so huge). 80 | 81 | **Important**: When linking your test artifact. You need to add the following link option: ``/NODEFAULTLIB:MSVCRT,MSVCRTD``. 82 | 83 | I have tested it over ``Microsoft Visual Studio 2019``. 84 | -------------------------------------------------------------------------------- /doc/README.md: -------------------------------------------------------------------------------- 1 | # Libcutest usage guide 2 | 3 | This document summarizes what is necessary to do in order to write unit tests for your project using ``libcutest``. 4 | 5 | ## How to use it 6 | 7 | Firstly you need to include the main library header which stands for: 8 | 9 | ```c 10 | #include 11 | ``` 12 | 13 | This header has lots of macro definitions including macros for test assertions, test running, etc. 14 | 15 | Basically the ``main`` function from your test binary should include a call to the unit test entry point: 16 | 17 | ```c 18 | int main(int argc, char **argv) { 19 | CUTE_RUN(entry); 20 | return 0; 21 | } 22 | ``` 23 | 24 | But for convenience ``cutest`` brings a short way of defining it: 25 | 26 | ```c 27 | CUTE_MAIN(entry) 28 | ``` 29 | 30 | The entry point is merely a unit test case which must be declared using special definition macros: 31 | 32 | ```c 33 | CUTE_TEST_CASE(entry) 34 | (...) 35 | CUTE_TEST_CASE_END 36 | ``` 37 | 38 | The entry point is the place where you really should call your test-cases. The following 39 | macro is destinated to call a ``test case``: 40 | 41 | ```c 42 | CUTE_RUN_TEST(my_previous_well_defined_test_case); 43 | ``` 44 | 45 | The ``test cases`` should use the same definition schema that is used with the ``entry-point``. Look an 46 | example: 47 | 48 | ```c 49 | CUTE_TEST_CASE(one_should_differ_zero) 50 | CUTE_CHECK("0 == 1!!", 1 != 0); 51 | CUTE_TEST_CASE_END 52 | ``` 53 | 54 | The example above will break if for some reason one be equals to zero. In this case the error message 55 | passed as first argument will be shown. The second argument is the logical test assertion. 56 | 57 | If your ``test case`` needs fixtures you must declare the fixtures using the following schemas: 58 | 59 | ```c 60 | CUTE_FIXTURE_SETUP(my_test_case) 61 | // Some setup operations goes here. 62 | CUTE_FIXTURE_END 63 | 64 | CUTE_FIXTURE_TEARDOWN(my_test_case) 65 | // Some teardown operations goes here. 66 | CUTE_FIXTURE_END 67 | 68 | CUTE_TEST_CASE(my_test_case) // now you only need to declare the test case in standard form. 69 | // Some test asserts goes here. 70 | CUTE_TEST_CASE_END 71 | 72 | CUTE_TEST_CASE(entry) 73 | CUTE_RUN_TEST_WITH_FIXTURE(my_test_case); // the test invocation macro differs from tests that do not have fixtures. 74 | CUTE_TEST_CASE_END 75 | ``` 76 | 77 | Tests with fixtures must define the both fixtures, the ``setup`` and the ``teardown``, otherwise you will get errors related with unresolved externals. 78 | 79 | ## Not implementing all test cases in just one source file 80 | 81 | Sometimes is necessary divide the implementation among several files finding to keep our sanity. Then in this case you should use the macros destinated to do the tests prototypes. Take a look: 82 | 83 | ```c 84 | // filesystem_test_cases.h 85 | #ifndef _FILESYSTEM_TEST_CASES_H 86 | #define _FILESYSTEM_TEST_CASES_H 1 87 | 88 | #include 89 | 90 | CUTE_DECLARE_TEST_CASE(test_fopen); 91 | 92 | CUTE_DECLARE_TEST_CASE_WITH_FIXTURE(test_remove); 93 | 94 | CUTE_DECLARE_TEST_CASE_WITH_FIXTURE(test_mkdir); 95 | 96 | #endif 97 | ``` 98 | 99 | Now supposing that ``filesystem_test_cases.c`` has already implemented all you need to do inside of your main source file 100 | or at least where you plan call your test cases: 101 | 102 | ```c 103 | // main.c 104 | #include "filesystem_test_cases.h" 105 | 106 | (...) 107 | 108 | CUTE_TEST_CASE(entry) 109 | CUTE_RUN_TEST(test_fopen); 110 | CUTE_RUN_TEST_WITH_FIXTURE(test_remove); 111 | CUTE_RUN_TEST_WITH_FIXTURE(test_mkdir); 112 | CUTE_TEST_CASE_END 113 | ``` 114 | 115 | ## Available assertion macros 116 | 117 | Until now these are the available assertion macros. 118 | 119 | ### CUTE_CHECK() 120 | 121 | Implements a generic assertion. 122 | 123 | ``CUTE_CHECK(, )`` 124 | 125 | Example: 126 | 127 | ```c 128 | CUTE_CHECK("boo!", strcmp(buffer, "foobar") != 0); 129 | ``` 130 | 131 | ### CUTE_CHECK_EQ() 132 | 133 | Implements an ``equals-to`` assertion. 134 | 135 | ``CUTE_CHECK_EQ(, , )`` 136 | 137 | Example: 138 | 139 | ```c 140 | CUTE_CHECK_EQ("error exit_code != 0", x, 0); 141 | ``` 142 | 143 | ### CUTE_CHECK_NEQ() 144 | 145 | Implements a ``not-equals-to`` assertion. 146 | 147 | ``CUTE_CHECK_NEQ(, , )`` 148 | 149 | Example: 150 | 151 | ```c 152 | CUTE_CHECK_NEQ("error x == -1", x, -1); 153 | ``` 154 | 155 | ### CUTE_CHECK_LE() 156 | 157 | Implements a ``less-than`` assertion. 158 | 159 | ``CUTE_CHECK_LE(, , )`` 160 | 161 | Example: 162 | 163 | ```c 164 | CUTE_CHECK_LE("x > 19", x, 20); 165 | ``` 166 | 167 | ### CUTE_CHECK_LEQ() 168 | 169 | Implements a ``less-than-or-equals-to`` assertion. 170 | 171 | ``CUTE_CHECK_LEQ(, , )`` 172 | 173 | Example: 174 | 175 | CUTE_CHECK_LEQ("x >= 21", x, 20); 176 | 177 | ### CUTE_CHECK_GR() 178 | 179 | Implements a ``greater-than`` assertion. 180 | 181 | ``CUTE_CHECK_GE(, , )`` 182 | 183 | Example: 184 | 185 | ```c 186 | CUTE_CHECK_GR("x <= 99", x, 100); 187 | ``` 188 | 189 | ### CUTE_CHECK_GEQ() 190 | 191 | Implements a ``greater-than-or-equals-to`` assertion. 192 | 193 | ``CUTE_CHECK_GEQ(, , )`` 194 | 195 | Example: 196 | 197 | ```c 198 | CUTE_CHECK_GEQ("x <= 19", x, 20); 199 | ``` 200 | 201 | ### Ok, maybe you do not want to specify error messages on assertions 202 | 203 | In this case you should use the ``CUTE_ASSERT`` macro group instead of ``CUTE_CHECK``. So take a look at 204 | ``Table 1`` to know more details about them. 205 | 206 | **Table 1**: Single assertion macros with no custom messages. 207 | 208 | | *Macro* | *Assertion type* | *Prototype* | *Usage example* | 209 | |:-----------------:|:--------------------------:|:-------------------------------------------:|:--------------------------:| 210 | |``CUTE_ASSERT`` | ``generic`` | ``CUTE_ASSERT()`` | ``CUTE_ASSERT(0 == 1)`` | 211 | |``CUTE_ASSERT_EQ`` | ``equals`` | ``CUTE_ASSERT_EQ(, )`` | ``CUTE_ASSERT_EQ(0, 1)`` | 212 | |``CUTE_ASSERT_NEQ``| ``not-equals`` | ``CUTE_ASSERT_NEQ(, )`` | ``CUTE_ASSERT_NEQ(0, 0)`` | 213 | |``CUTE_ASSERT_LE`` | ``less-than`` | ``CUTE_ASSERT_LE(, )`` | ``CUTE_ASSERT_LE(0, 0)`` | 214 | |``CUTE_ASSERT_GR`` | ``greater-than`` | ``CUTE_ASSERT_GR(, )`` | ``CUTE_ASSERT_GR(0, 0)`` | 215 | |``CUTE_ASSERT_LEQ``| ``less-than-or-equals`` | ``CUTE_ASSERT_LEQ(, )`` | ``CUTE_ASSERT_LEQ(x, 0)`` | 216 | |``CUTE_ASSERT_GEQ``| ``greater-than-or-equals`` | ``CUTE_ASSERT_GEQ(, )`` | ``CUTE_ASSERT_GEQ(x, -1)`` | 217 | 218 | However, I think that the lack of good custom assertion error messages can sometimes obfuscate the test report and as a result 219 | it also can consume more time on understanding and applying the correct fix. In my opinion you should use it with care. 220 | 221 | ## Grouping tests inside suites 222 | 223 | For it is necessary use two specific macros. One for suite declaring and another for test suite running. Look: 224 | 225 | ```c 226 | CUTE_TEST_CASE_SUITE(my_sample_suite) 227 | CUTE_RUN_TEST(my_sample_suite_case_a); 228 | CUTE_RUN_TEST(my_sample_suite_case_b); 229 | CUTE_RUN_TEST(my_sample_suite_case_z); 230 | CUTE_TEST_CASE_SUITE_END 231 | 232 | CUTE_TEST_CASE(tests_entry) 233 | CUTE_RUN_TEST_SUITE(my_sample_suite); 234 | CUTE_TEST_CASE_END 235 | 236 | CUTE_MAIN(tests_entry) 237 | ``` 238 | 239 | All tests between ``CUTE_TEST_CASE_SUITE(my_sample_suite)`` and ``CUTE_TEST_CASE_SUITE_END`` are owned by suite ``my_sample_suite``. 240 | For this suite running is used a special macro called ``CUTE_RUN_TEST_SUITE`` as you can see above on ``tests_entry``. 241 | 242 | If you want to run only specific test suites you should use the option ``--cutest-run-suite`` as follows: 243 | 244 | ``./your-tidy-unit --cutest-run-suite=my_sample_suite,suite_x,suite_y,suite_z`` 245 | 246 | Tests called directly with ``CUTE_RUN_TEST`` macro are unconditional and for this reason always executed. 247 | 248 | If for some reason you need to prototype the suites use the macro ``CUTE_DECLARE_TEST_CASE_SUITE(suite_name)``. 249 | 250 | ## Running only specific tests 251 | 252 | If you just want to run one or few ``test-cases`` you should use the option ``--cutest-run-test`` as follows: 253 | 254 | ``./your-huge-unit --cutest-run-test=test_a,test_b,test_d,test_w`` 255 | 256 | In this example only the tests ``test_a``, ``test_b``, ``test_d`` and ``test_w`` will run. Beware that the ``--cutest-run-test`` 257 | option has more precedence than ``--cutest-run-suite`` option. 258 | 259 | ## How can I print the current case name? 260 | 261 | Sometimes for debug issues you may need to print this piece of information. So you could try this: 262 | 263 | ```c 264 | printf("Oh my God! Houston we got Raptors in %s\n", CUTE_CASE_NAME); 265 | ``` 266 | 267 | Or: 268 | 269 | ```c 270 | cute_log("Oh my God! Houston, we got Raptors in %s!\n", CUTE_CASE_NAME); 271 | ``` 272 | 273 | Or still: 274 | 275 | ```c 276 | cute_log("Oh my God! Houston, we got Raptors in $CASE_NAME!\n"); 277 | ``` 278 | 279 | ## How can I link my test with cutest? 280 | 281 | In order to ``link`` your test binary you can proceed as follows: 282 | 283 | ``gcc your-test.c -oyour-test libcutest.a`` 284 | 285 | Or still (if you put ``libcutest`` in a well-known place): 286 | 287 | ``gcc your-test.c -oyour-test -lcutest`` 288 | 289 | ### Additional Linker flags 290 | 291 | Being on ``Linux`` is also necessary to use the additional linker flag ``-ldl``. 292 | 293 | On ``FreeBSD``, ``NetBSD``, ``OpenBSD`` and ``MINIX`` you need to use the additional linker flag ``-lexecinfo``. 294 | 295 | If your ``libexecinfo`` is installed into a non well-known place, you need to specify its ``include`` and ``library`` directories. 296 | Using ``gcc``/``clang`` the options ``-I`` and ``-L`` can get the job done. 297 | 298 | If you are on ``Solaris`` you need to define the macro ``NO_CUTEST_BACKTRACING``. The related ``GCC`` compiler flag 299 | is ``-DNO_CUTEST_BACKTRACING``. However, if your ``Solaris`` has ``libexecinfo`` let's give it a try but firstly you 300 | need to recompile the ``libcutest`` with support for ``backtracing``. 301 | 302 | Excepting ``MINIX`` (which currently does not offer support for ``Pthreads``) on all supported UNIX-like is necessary to use 303 | the linker flag ``-lpthread``. 304 | 305 | ## Dumping test log to a file 306 | 307 | To do it use the option ``--cutest-log-path=``. This option specifies a file path where the test log will be dumped. 308 | 309 | ## Reading command line options from your test cases 310 | 311 | Sometimes is necessary... To do it you should use the macro ``CUTE_GET_OPTION()``. Supposing that you want to read the option ``--foobar`` try this: 312 | 313 | ```c 314 | char *foobar_value = CUTE_GET_OPTION("foobar"); 315 | ``` 316 | 317 | Note that the options should be passed in this form to your test binary: 318 | 319 | ``somewhere/over/the/rainbow/your-test --foobar=option-with-content --flag-option`` 320 | 321 | When you try to read an unknown option the return is always ``NULL`` and ``flag options`` always return ``"1"``. 322 | 323 | ## Detecting memory leaks 324 | 325 | Firstly be aware that [``valgrind``](http://valgrind.org) is the best way to catch this kind of [raptors](https://xkcd.com/292/) inside your code. However ``cutest`` brings a minimal system that performs memory leak detection. It could be a profitable way to detect this kind of issue as soon as possible. Because you will be looking for memory leaks being still on test phase. 326 | 327 | The usage of this system must be enabled with the option ``--cutest-leak-check`` to your unit test binary. Something like: 328 | 329 | ``something/leaking/over/the/rainbow/your-test --cutest-leak-check`` 330 | 331 | When some memory leak is detected a report about this issue is included at the end of your general test report and the test binary exits with a non-zero ``exit code``. 332 | 333 | The format of this memory leak report is as follows: 334 | 335 | cutest INTERNAL ERROR: Memory leak(s) detected!! 336 | 337 | >>> 338 | Id=4 Address=0x0804C008 File=/leak-sample/main.c [The last check before leak was at line #113] < leak bloody leak...o > 20 byte(s). 339 | 340 | Leak total: 20 byte(s). 341 | <<< 342 | 343 | The report includes: 344 | 345 | - An allocation id. 346 | - The effective leak's initial memory address. 347 | - The file path where this leak is being caused. 348 | - The line number where was done the last test assertion before leak. 349 | - A content listing from this memory address and how many bytes are leaking. 350 | 351 | At the end is also included a sum of all detected leaks. 352 | 353 | The allocation id can be used in order to force a debug break. When you pass to your test binary the option ``--cutest-leak-id=`` a ``trap`` signal will be raised at the moment that the allocation id be equals to the passed value. Being the debugger attached you will be able to inspect the ``callstack`` and then know more about this leak. 354 | 355 | ## Log customizing 356 | 357 | It is possible to customize the test logs. ``Cutest`` divide the log in three parts which are: ``header``, ``detail`` and ``footer``. 358 | 359 | There are three options that you need to pass to your test binary if you intend to modify the log layout. Table 2 summarizes 360 | these options. 361 | 362 | **Table 2**: The ``test-log`` options. 363 | 364 | | **Option** | **Receives** | 365 | |:-----------------------:|--------------------------------------------:| 366 | | ``--cutest-log-header`` | a file path pointing to the header template | 367 | | ``--cutest-log-detail`` | a file path pointing to the detail template | 368 | | ``--cutest-log-footer`` | a file path pointing to the footer template | 369 | 370 | Into the template goes the specific tokens related with the chosen format well as the variables that carry relevant infos about the logged process. 371 | 372 | Table 3 brings a listing of these variables and what they represent. 373 | 374 | **Table 3**: The ``test-log`` recognized variables. 375 | 376 | | **Variable** | **Represents** | 377 | |:--------------------------:|-------------------------------------------:| 378 | | ``$FILE`` | the test file path | 379 | | ``$LINE`` | the test file line number | 380 | | ``$STATUS`` | the test status (``passed`` or ``failed``) | 381 | | ``$CASE_NAME`` | the test name | 382 | | ``$RAN_TEST_NR`` | the ran tests amount | 383 | | ``$ASSERTION_MESSAGE`` | the user defined assertion message | 384 | 385 | Following you can see test template samples. 386 | 387 | Here goes the log ``header``: 388 | 389 | ```html 390 | 391 | Log template sample 392 |

Unit test results

393 | 394 | 395 | ``` 396 | 397 | 398 | now the ``detail``... 399 | 400 | ```html 401 | 402 | 403 | ``` 404 | 405 | and then the ``footer``. 406 | 407 | ```html 408 | 409 |
Test nameResultMessage
$CASE_NAME in $FILE at $LINE$STATUS$ASSERTION_MESSAGE
410 | Total tests executed: $RAN_TESTS_NR 411 | 412 | ``` 413 | 414 | To use these templates you must indicate them by command line in this following way: 415 | 416 | ``somewhere/over/the/rainbow/your-test --cutest-log-header=templates/test-log-header.html --cutest-log-detail=templates/test-log-detail.html --cutest-log-footer=templates/test-log-footer.html`` 417 | 418 | **Remark**: Under ``unix-like`` platforms we call ``setbuf()`` in order to set ``stdout`` and ``stderr`` to ``NULL`` when we 419 | are logging the test's output to a file. It is done to get rid of the initial 4k allocation done by the ``stdio`` and 420 | never freed (for performance issues). When not skipped it can add some noise to your memory leak's reports. 421 | 422 | ### Customizing the memory leak report 423 | 424 | It is possible too. The Table 4 brings all variables recognized by this kind of template. 425 | 426 | **Table 4**: The ``leak-log`` recognized variables. 427 | 428 | | **Variable** | **Represents** | 429 | |:---------------------------------:|----------------------------------------------------------------:| 430 | | ``$LEAK_ID`` | the leak id | 431 | | ``$LEAK_ADDR`` | the start leak memory address | 432 | | ``$LEAK_FILE_PATH`` | the file path where the leak is occurring | 433 | | ``$LEAK_LINE`` | the file line number of the last test assertion before the leak | 434 | | ``$LEAK_DATA`` | the data which is leaking | 435 | | ``$LEAK_SIZE`` | the size of leak | 436 | | ``$LEAK_SUM`` | the total (in bytes) that are leaking | 437 | 438 | Similar to the test log custom you need to pass the options specifying the file path of ``header``, ``detail`` and ``footer``. 439 | These options are respectively: ``--cutest-leak-log-header``, ``--cutest-leak-log-detail`` and ``--cutest-leak-log-footer``. 440 | 441 | ## Testing kernel mode stuff with cutest 442 | 443 | First off you should bear in mind that in kernel mode the cutest's features are reduced. The only available features are 444 | the test assertion macros and the abstraction macros in order to produce your loadable kernel module (a.k.a your test binary). 445 | 446 | Until now the supported platforms are ``Linux``, ``FreeBSD``, ``NetBSD`` and also ``Windows``. 447 | 448 | There is no compiler flags to pass. All you should do is to include the reader file ``kutest.h``. 449 | Now about the macros, the general rule is: any ``CUTE`` from user mode becomes ``KUTE`` in kernel mode. 450 | Take a look: 451 | 452 | ```c 453 | #include 454 | 455 | KUTE_DECLARE_TEST_CASE(tests_entry); 456 | 457 | KUTE_DECLARE_TEST_CASE(one_plus_two_test); 458 | 459 | KUTE_TEST_CASE(tests_entry) 460 | // Call all tests from here. 461 | KUTE_RUN_TEST(one_plus_two_test); 462 | KUTE_TEST_CASE_END 463 | 464 | KUTE_TEST_CASE(one_plus_two_test) 465 | KUTE_ASSERT(2 + 1 == 3); 466 | KUTE_TEST_CASE_END 467 | 468 | KUTE_MAIN(tests_entry); 469 | ``` 470 | 471 | This is about a LKM so you should compile it as your operating system expects. To run the tests insert the kernel module. If 472 | there is no error during this operation your tests has passed. Once inserted is up to you remove it. 473 | 474 | Yes, of course if you have made some stupid thing during the tests the machine can reset, enter in a panic state, etc. This 475 | is also up to you. 476 | 477 | On ``Windows`` "insert LKM" is a bit trickier task. You need to create a service based on your ``.sys`` that stands for your unit test binary: 478 | 479 | ``` 480 | _ sc create kunit101 binpath=c:\path\to\your\device-driver.sys type=kernel 481 | ``` 482 | 483 | After, You need start it: 484 | 485 | ``` 486 | _ sc start kunit101 487 | ``` 488 | 489 | Watch for the ``%ERRORLEVEL%`` (Zero stands for all OK). Yes, ``BSoD`` stands for really bad bugs. 490 | 491 | Unload your kernel mode unit tests: 492 | 493 | ``` 494 | _ sc stop kunit101 495 | ``` 496 | 497 | Finally, delete the service: 498 | 499 | ``` 500 | _ sc delete kunit101 501 | ``` 502 | 503 | Notice that, all those ``sc`` stuff must be done from an administrator command prompt. Otherwise it will not work properly. Your driver also should 504 | be signed or you can enable test signed driver loading on the machine where those unit test should run. 505 | 506 | In order to enable test signing, on an administrator command prompt execute: 507 | 508 | ``` 509 | _ bcdedit -set testsigning on 510 | ``` 511 | 512 | Of course, you can pack those four ``sc`` commands into a build task. In fact it would be more professional. Anyway, it is up to you, too. 513 | 514 | Another alterative for ``Windows`` is using the ``OSRLoader`` for driver loading issues or you can easily create your tiny custom loader on your own. 515 | Again, it is up to you. 516 | -------------------------------------------------------------------------------- /src/.ivk: -------------------------------------------------------------------------------- 1 | --forgefiles=Forgefile.hsl --Forgefile-projects=cutest --obj-output-dir=obj --bin-output-dir=lib 2 | -------------------------------------------------------------------------------- /src/Forgefile.hsl: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (C) 2015 by Rafael Santiago 3 | # 4 | # This is a free software. You can redistribute it and/or modify under 5 | # the terms of the GNU General Public License version 2. 6 | # 7 | # 8 | include Toolsets.hsl 9 | include Forgeutils.hsl 10 | include ~/fsutil.hsl 11 | 12 | local var sources type list; 13 | local var includes type list; 14 | local var cflags type list; 15 | local var libraries type list; 16 | local var ldflags type list; 17 | local var libname type string; 18 | 19 | local var toolset_name type string; 20 | 21 | project cutest : toolset $toolset_name : $sources, $includes, $cflags, $libraries, $ldflags, $libname ; 22 | 23 | cutest.preloading() { 24 | $toolset_name = get_toolset_basename() + "lib"; 25 | } 26 | 27 | cutest.prologue() { 28 | var option type list; 29 | 30 | $option = hefesto.sys.get_option("build-skip"); 31 | 32 | if ($option.index_of("cutest") > -1) { 33 | hefesto.sys.echo("BUILD INFO: cutest build was skipped.\n"); 34 | hefesto.project.abort(0); 35 | } 36 | 37 | $sources.ls(".*\\.c$"); 38 | 39 | $cflags = hefesto.sys.get_option("cflags"); 40 | 41 | if (hefesto.sys.os_name() == "minix") { 42 | $cflags.add_item("-DHAS_NO_PTHREAD"); 43 | } else if (hefesto.sys.os_name() == "sunos") { 44 | $cflags.add_item("-DNO_CUTEST_BACKTRACING"); 45 | } 46 | 47 | $libname = "libcutest.a"; 48 | 49 | if ($toolset_name == "clang-c-lib") { 50 | $cflags.add_item("-Wno-format"); 51 | } else if ($toolset_name == "msvc-c-lib") { 52 | $cflags.add_item("-D_CRT_SECURE_NO_WARNINGS"); 53 | $libname = "libcuteststaged.lib"; 54 | } 55 | 56 | hefesto.sys.echo("*** NOW BUILDING " + $libname + "... wait...\n\n"); 57 | 58 | if (hefesto.sys.os_name() != "windows") { 59 | if (isdir("/usr/local/include")) { 60 | $includes.add_item("/usr/local/include"); 61 | } 62 | 63 | if (isdir("/usr/local/lib")) { 64 | $libraries.add_item("/usr/local/lib"); 65 | } 66 | } 67 | } 68 | 69 | cutest.epilogue() { 70 | if (hefesto.sys.last_forge_result() == 0) { 71 | if ($toolset_name == "msvc-c-lib") { 72 | # INFO(Rafael): On Windows (MSVC) since we have local definitions for malloc, realloc, alloc and free. 73 | # We need to link the test with /NODEFAULTLIB:MSVCRT,MSVCRTD, otherwise we will get a symbol redefinition error when linking. 74 | # But what about handling other possible libc dependencies? Instead of implementing a one from scratch let's composite libcutest 75 | # with the original MSVCRT[D].lib a dirty trick but works. 76 | if (composite_libcutest() != 0) { 77 | hefesto.sys.echo("ERROR: Unable to composite libcutest.a\n"); 78 | hefesto.project.abort(1); 79 | } 80 | } 81 | hefesto.sys.echo("\n*** libcutest.a WAS BUILT.\n\n"); 82 | 83 | var exit_code type int; 84 | $exit_code = 1; 85 | if (hefesto.sys.cd("test")) { 86 | var compile_model type string; 87 | $compile_model = "release"; 88 | var option type list; 89 | $option = hefesto.sys.get_option("compile-model"); 90 | if ($option.count() > 0) { 91 | $compile_model = $option.item(0); 92 | } 93 | var cpu_arch type string; 94 | $option = hefesto.sys.get_option("cpu-arch"); 95 | if ($option.count() > 0) { 96 | $cpu_arch = " --cpu-arch=" + $option.item(0); 97 | } 98 | $exit_code = hefesto.sys.forge("cutest-test", "Forgefile.hsl", "--obj-output-dir=obj --bin-output-dir=bin --toolset=" + $toolset_name + 99 | " --compile-model=" + $compile_model + $cpu_arch); 100 | hefesto.sys.cd(".."); 101 | } 102 | if ($exit_code != 0) { 103 | hefesto.project.abort($exit_code); 104 | } else { 105 | hefesto.sys.echo("\n*** ALL DONE.\n\n"); 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/Forgeutils.hsl: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (C) 2016 by Rafael Santiago 3 | # 4 | # This is a free software. You can redistribute it and/or modify under 5 | # the terms of the GNU General Public License version 2. 6 | # 7 | # 8 | include on windows ~/toolsets/msvc/util/msvc_utils.hsl 9 | 10 | local function run_cmd_without_output(cmdline type string) : result type int { 11 | if (hefesto.sys.os_name() != "windows") { 12 | $cmdline = $cmdline + " > /dev/null 2>&1"; 13 | } else { 14 | $cmdline = $cmdline + " > nul 2>&1"; 15 | } 16 | result hefesto.sys.run($cmdline); 17 | } 18 | 19 | local function has_gcc() : result type int { 20 | result (run_cmd_without_output("gcc --version") == 0); 21 | } 22 | 23 | local function has_clang() : result type int { 24 | result (run_cmd_without_output("clang --version") == 0); 25 | } 26 | 27 | function get_toolset_basename() : result type string { 28 | var chosen_toolset type string; 29 | var toolset_option type list; 30 | 31 | $toolset_option = hefesto.sys.get_option("toolset"); 32 | 33 | if ($toolset_option.count() > 0) { 34 | $chosen_toolset = $toolset_option.item(0); 35 | $chosen_toolset.replace("(lib|app)$", ""); 36 | } else { 37 | $chosen_toolset = ""; 38 | if (has_gcc()) { 39 | $chosen_toolset = "gcc-c-"; 40 | } else if (has_clang()) { 41 | $chosen_toolset = "clang-c-"; 42 | } 43 | } 44 | 45 | if ($chosen_toolset.len() == 0) { 46 | hefesto.sys.echo("PANIC: You need to install GCC or Clang before continuing.\nAborted.\n"); 47 | hefesto.project.abort(1); 48 | } 49 | 50 | result $chosen_toolset; 51 | } 52 | 53 | function composite_libcutest() : result type int { 54 | var vcvarsall type string; 55 | $vcvarsall = get_vcvarsall_path(); 56 | 57 | if ($vcvarsall.len() == 0) { 58 | hefesto.sys.echo("ERROR: Unable to find MSVC install location.\n"); 59 | result 1; 60 | } 61 | 62 | var cfg_postfix type string; 63 | $cfg_postfix = get_msvc_artifact_postfix(); 64 | 65 | var msvcrtlib type string; 66 | var compile_model type list; 67 | 68 | $compile_model = hefesto.sys.get_option("compile-model"); 69 | $msvcrtlib = "msvcrt.lib"; 70 | if ($compile_model.count() > 0) { 71 | var compile_model_data type string; 72 | $compile_model_data = $compile_model.item(0); 73 | if ($compile_model_data == "debug") { 74 | $msvcrtlib = "msvcrtd.lib"; 75 | } 76 | } 77 | 78 | var arch type string; 79 | $arch = get_msvc_platform(); 80 | if ($arch == "Win32") { 81 | $arch = "x86"; 82 | } 83 | 84 | var composite_batch type string; 85 | $composite_batch = "@cd \"" + hefesto.sys.pwd() + "\" >nul 2>&1 \n" + 86 | "@call \"" + $vcvarsall + "\" " + $arch + " >nul 2>&1 \n" + 87 | "@lib.exe /VERBOSE:lib /OUT:lib\\libcutest" + $cfg_postfix + 88 | ".lib lib\\libcuteststaged.lib \"%VCToolsInstallDir%lib\\" + $arch + 89 | "\\" + $msvcrtlib + "\" >nul 2>&1 \n@if ErrorLevel 1 ( @exit /b 1 ) else ( @exit /b 0 )"; 90 | var fp type file; 91 | $fp = hefesto.sys.fopen(".cplib.cmd", "wb"); 92 | if ($fp == 0) { 93 | result 1; 94 | } 95 | hefesto.sys.fwrite($composite_batch, $composite_batch.len(), $fp); 96 | hefesto.sys.fclose($fp); 97 | 98 | var exit_code type int; 99 | $exit_code = hefesto.sys.run("cmd /c .cplib.cmd"); 100 | 101 | if ($exit_code == 0) { 102 | var path type string; 103 | $path = hefesto.sys.make_path(hefesto.sys.pwd(), "lib\\libcuteststaged.lib"); 104 | hefesto.sys.rm($path); 105 | $path.replace("lib$", "pdb"); 106 | hefesto.sys.rm($path); 107 | } 108 | 109 | hefesto.sys.rm(".cplib.cmd"); 110 | 111 | result $exit_code; 112 | } 113 | 114 | local function get_vcvarsall_path() : result type string { 115 | var program_files type list; 116 | $program_files.add_item("Program Files (x86)"); 117 | $program_files.add_item("Program Files"); 118 | 119 | var year type list; 120 | $year.add_item("2022"); 121 | $year.add_item("2019"); 122 | $year.add_item("2017"); 123 | $year.add_item("2015"); 124 | $year.add_item("2013"); 125 | $year.add_item("2012"); 126 | $year.add_item("2010"); 127 | $year.add_item("2008"); 128 | $year.add_item("2005"); 129 | 130 | var vsinstall_type type list; 131 | $vsinstall_type.add_item("Community"); 132 | $vsinstall_type.add_item("Professional"); 133 | $vsinstall_type.add_item("Enterprise"); 134 | 135 | var metapath type string; 136 | 137 | $metapath = "C:\\{{PROGRAMFILES}}\\Microsoft Visual Studio\\{{YEAR}}\\{{VSINSTALLTYPE}}\\VC\\Auxiliary\\Build\\vcvarsall.bat"; 138 | 139 | var y type int; 140 | var p type int; 141 | var v type int; 142 | 143 | $y = 0; 144 | while ($y < $year.count()) { 145 | var curr_year type string; 146 | $curr_year = $year.item($y); 147 | $p = 0; 148 | while ($p < $program_files.count()) { 149 | var curr_program_files type string; 150 | $curr_program_files = $program_files.item($p); 151 | $v = 0; 152 | while ($v < $vsinstall_type.count()) { 153 | var curr_vsinstall_type type string; 154 | $curr_vsinstall_type = $vsinstall_type.item($v); 155 | var vcvarsall_path type string; 156 | $vcvarsall_path = $metapath; 157 | $vcvarsall_path.replace("\\{\\{PROGRAMFILES\\}\\}", $curr_program_files); 158 | $vcvarsall_path.replace("\\{\\{YEAR\\}\\}", $curr_year); 159 | $vcvarsall_path.replace("\\{\\{VSINSTALLTYPE\\}\\}", $curr_vsinstall_type); 160 | if (isfile($vcvarsall_path)) { 161 | result $vcvarsall_path; 162 | } 163 | $v = $v + 1; 164 | } 165 | $p = $p + 1; 166 | } 167 | $y = $y + 1; 168 | } 169 | 170 | result ""; 171 | } 172 | 173 | function get_msvc_artifact_postfix() : result type string { 174 | var cfg_postfix type string; 175 | $cfg_postfix = "mt"; 176 | 177 | var option type list; 178 | $option = hefesto.sys.get_option("link-model"); 179 | 180 | # if ($option.count() > 0) { 181 | # var link_model type string; 182 | # $link_model = $option.item(0); 183 | # if ($link_model == "shared") { 184 | # $cfg_postfix = "md"; 185 | # } 186 | # } 187 | 188 | $option = hefesto.sys.get_option("compile-model"); 189 | if ($option.count() > 0) { 190 | var compile_model type string; 191 | $compile_model = $option.item(0); 192 | if ($compile_model == "debug") { 193 | $cfg_postfix = $cfg_postfix + "d"; 194 | } 195 | } 196 | 197 | result $cfg_postfix; 198 | } 199 | 200 | function get_msvc_platform() : result type string { 201 | var option type list; 202 | var arch type string; 203 | 204 | $option = hefesto.sys.get_option("cpu-arch"); 205 | if ($option.count() == 0) { 206 | var programfilesx86 type string; 207 | $programfilesx86 = hefesto.sys.env("ProgramFiles(x86)"); 208 | if ($programfilesx86.len() > 0) { 209 | result "x64"; 210 | } 211 | result "Win32"; 212 | } 213 | 214 | $arch = $option.item(0); 215 | 216 | if($arch == "x86" || $arch == "X86") { 217 | result "Win32"; 218 | } 219 | 220 | result $arch; 221 | } 222 | -------------------------------------------------------------------------------- /src/Toolsets.hsl: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (C) 2016 by Rafael Santiago 3 | # 4 | # This is a free software. You can redistribute it and/or modify under 5 | # the terms of the GNU General Public License version 2. 6 | # 7 | # 8 | include ~/toolsets/gcc/gcc-lib.hsl 9 | include ~/toolsets/clang/clang-lib.hsl 10 | include on windows ~/toolsets/msvc/msvc.hsl 11 | -------------------------------------------------------------------------------- /src/cutest.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 by Rafael Santiago 3 | * 4 | * This is a free software. You can redistribute it and/or modify under 5 | * the terms of the GNU General Public License version 2. 6 | * 7 | */ 8 | #if !(defined(_KERNEL) && defined(_LKM) && defined(__NetBSD__)) 9 | 10 | #include "cutest.h" 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #if _MSC_VER 17 | #include 18 | 19 | #define ssize_t SSIZE_T 20 | #endif 21 | 22 | int g_cute_ran_tests = 0; 23 | 24 | int g_cute_general_counter = 0; 25 | 26 | FILE *g_cute_log_fd = NULL; 27 | 28 | char **g_cute_argv = NULL; 29 | 30 | int g_cute_argc = 0; 31 | 32 | int g_cute_leak_check = 0; 33 | 34 | int g_leak_sum = 0; 35 | 36 | void (*g_cute_fixture_setup)() = NULL; 37 | 38 | void (*g_cute_fixture_teardown)() = NULL; 39 | 40 | int g_cute_last_exec_line = -1; 41 | 42 | char *g_cute_last_ref_file = NULL; 43 | 44 | struct cute_mmap_ctx *g_cute_mmap = NULL, *g_cute_mmap_tail = NULL; 45 | 46 | struct cute_mmap_ctx *g_mp = NULL; 47 | 48 | char *g_cute_test_status = "idle"; 49 | 50 | char *g_cute_test_name = "(null)"; 51 | 52 | char g_cute_user_template[0xffff]; 53 | 54 | char *g_cute_assertion_message = NULL; 55 | 56 | int g_cute_leak_id = 0; 57 | 58 | static void *get_file(); 59 | 60 | static void *get_line(); 61 | 62 | static void *get_status(); 63 | 64 | static void *get_test_name(); 65 | 66 | static void *get_ran_tests(); 67 | 68 | static void *get_assertion_message(); 69 | 70 | static void *get_leak_id(); 71 | 72 | static void *get_leak_addr(); 73 | 74 | static void *get_leak_file_path(); 75 | 76 | static void *get_leak_line(); 77 | 78 | static void *get_leak_size(); 79 | 80 | static void *get_leak_sum(); 81 | 82 | static ssize_t get_cute_var_data(const char *vname); 83 | 84 | static int cute_find_option_in_option_list(const char *option, const char *option_list); 85 | 86 | enum { 87 | kInt, 88 | kStr, 89 | kRaw, 90 | kAddr, 91 | kSize 92 | }; 93 | 94 | struct cute_vars_ctx { 95 | char *vname; 96 | void *(*get)(); 97 | int dtype; 98 | }; 99 | 100 | static void *get_file() { 101 | return &g_cute_last_ref_file[0]; 102 | } 103 | 104 | static void *get_line() { 105 | return &g_cute_last_exec_line; 106 | } 107 | 108 | static void *get_status() { 109 | return &g_cute_test_status[0]; 110 | } 111 | 112 | static void *get_test_name() { 113 | return &g_cute_test_name[0]; 114 | } 115 | 116 | static void *get_ran_tests() { 117 | return &g_cute_ran_tests; 118 | } 119 | 120 | static void *get_assertion_message() { 121 | return &g_cute_assertion_message[0]; 122 | } 123 | 124 | static void *get_leak_id() { 125 | return &g_mp->id; 126 | } 127 | 128 | static void *get_leak_addr() { 129 | return g_mp->addr; 130 | } 131 | 132 | static void *get_leak_file_path() { 133 | return &g_mp->file_path[0]; 134 | } 135 | 136 | static void *get_leak_line() { 137 | return &g_mp->line_nr; 138 | } 139 | 140 | static void *get_leak_size() { 141 | return &g_mp->size; 142 | } 143 | 144 | static void *get_leak_sum() { 145 | return &g_leak_sum; 146 | } 147 | 148 | #define CUTE_VARS_NR 13 149 | 150 | static struct cute_vars_ctx cute_vars[CUTE_VARS_NR] = { 151 | { "FILE", get_file, kStr}, 152 | { "LINE", get_line, kInt}, 153 | { "STATUS", get_status, kStr}, 154 | { "CASE_NAME", get_test_name, kStr}, 155 | { "RAN_TESTS_NR", get_ran_tests, kInt}, 156 | { "ASSERTION_MESSAGE", get_assertion_message, kStr}, 157 | { "LEAK_ID", get_leak_id, kInt}, 158 | { "LEAK_ADDR", get_leak_addr, kAddr}, 159 | { "LEAK_FILE_PATH", get_leak_file_path, kStr}, 160 | { "LEAK_LINE", get_leak_line, kInt}, 161 | { "LEAK_DATA", get_leak_addr, kRaw}, 162 | { "LEAK_SIZE", get_leak_size, kInt}, 163 | { "LEAK_SUM", get_leak_sum, kInt} 164 | }; 165 | 166 | static ssize_t get_cute_var_data(const char *vname) { 167 | size_t v = 0; 168 | if (vname == NULL) { 169 | return -1; 170 | } 171 | for (v = 0; v < CUTE_VARS_NR; v++) { 172 | if (strcmp(cute_vars[v].vname, vname) == 0) { 173 | return v; 174 | } 175 | } 176 | return -1; 177 | } 178 | 179 | void cute_open_log_fd(const char *filepath) { 180 | cute_close_log_fd(); 181 | g_cute_log_fd = fopen(filepath, "wb"); 182 | if (g_cute_log_fd == NULL) { 183 | printf("cutest WARNING: Unable to create file \"%s\". All will be logged to stdout.\n", filepath); 184 | } 185 | } 186 | 187 | void cute_close_log_fd() { 188 | if (g_cute_log_fd != NULL && g_cute_log_fd != stdout) { 189 | fclose(g_cute_log_fd); 190 | g_cute_log_fd = NULL; 191 | } 192 | } 193 | 194 | void cute_log(const char *msg, ...) { 195 | const char *mp = msg; 196 | char *s = NULL, c = 0; 197 | int d = 0; 198 | void *m = NULL; 199 | int should_log_to_stdout = (g_cute_log_fd == NULL); 200 | va_list val; 201 | long long l = 0; 202 | char vname[0xff]; 203 | void *vdata = NULL; 204 | size_t v = 0; 205 | size_t a = 0; 206 | ssize_t vindex = -1; 207 | const char *temp_mp = NULL; 208 | char assertion_message[0xffff] = ""; 209 | if (mp == NULL) { 210 | return; 211 | } 212 | if (should_log_to_stdout) { 213 | g_cute_log_fd = stdout; 214 | } 215 | if (g_cute_user_template[0] != 0) { 216 | mp = (const char *) g_cute_user_template; 217 | } 218 | va_start(val, msg); 219 | while (*mp) { 220 | if(*mp == '%') { 221 | switch (*(mp + 1)) { 222 | case 's': 223 | s = va_arg(val, char *); 224 | fprintf(g_cute_log_fd, "%s", s); 225 | break; 226 | 227 | case 'd': 228 | d = va_arg(val, int); 229 | fprintf(g_cute_log_fd, "%d", d); 230 | break; 231 | 232 | case 'c': 233 | c = (char) va_arg(val, int); 234 | fprintf(g_cute_log_fd, "%c", c); 235 | break; 236 | 237 | case 'x': 238 | d = va_arg(val, int); 239 | fprintf(g_cute_log_fd, "0x%.8X", d); 240 | break; 241 | 242 | case 'm': 243 | m = va_arg(val, void *); 244 | fprintf(g_cute_log_fd, sizeof(void *) == 4 ? "0x%.8X" : "0x%.16X", m); 245 | break; 246 | 247 | case 'l': 248 | l = va_arg(val, long long); 249 | fprintf(g_cute_log_fd, "%lld", l); 250 | break; 251 | 252 | default: 253 | fprintf(g_cute_log_fd, "%%"); 254 | break; 255 | } 256 | mp++; 257 | } else if (*mp == '\\') { 258 | switch (*(mp + 1)) { 259 | case 'n': 260 | fprintf(g_cute_log_fd, "\n"); 261 | break; 262 | 263 | case 'r': 264 | fprintf(g_cute_log_fd, "\r"); 265 | break; 266 | 267 | case 't': 268 | fprintf(g_cute_log_fd, "\t"); 269 | break; 270 | 271 | default: 272 | fprintf(g_cute_log_fd, "%c", *(mp + 1)); 273 | break; 274 | } 275 | mp++; 276 | } else if (*mp == '$') { 277 | mp++; 278 | temp_mp = mp; 279 | memset(vname, '\0', sizeof(vname)); 280 | for (v = 0; v < sizeof(vname) && (isalpha(*mp) || *mp == '_'); v++, mp++) { 281 | vname[v] = *mp; 282 | } 283 | vindex = get_cute_var_data(vname); 284 | if (vindex == -1 || cute_vars[vindex].get == NULL) { 285 | fprintf(g_cute_log_fd, "$"); 286 | mp = temp_mp; 287 | continue; 288 | } else { 289 | mp--; 290 | switch (cute_vars[vindex].dtype) { 291 | 292 | case kStr: 293 | if (strcmp(vname, "ASSERTION_MESSAGE") != 0) { 294 | fprintf(g_cute_log_fd, "%s", (char *)cute_vars[vindex].get()); 295 | } else { 296 | if (g_cute_test_status == NULL || strcmp(g_cute_test_status, CUTE_PASSED_LABEL) == 0) { 297 | fprintf(g_cute_log_fd, "-"); 298 | } else { 299 | fprintf(g_cute_log_fd, "%s", (char *)cute_vars[vindex].get()); 300 | } 301 | } 302 | break; 303 | 304 | case kInt: 305 | fprintf(g_cute_log_fd, "%d", *(int *)cute_vars[vindex].get()); 306 | break; 307 | 308 | case kRaw: 309 | vdata = cute_vars[vindex].get(); 310 | for (a = 0; a < g_mp->size; a++) { 311 | if (isprint(((char *)vdata)[a])) { 312 | fprintf(g_cute_log_fd, "%c", ((char *)vdata)[a]); 313 | } else { 314 | fprintf(g_cute_log_fd, "."); 315 | } 316 | } 317 | break; 318 | 319 | case kAddr: 320 | vdata = cute_vars[vindex].get(); 321 | fprintf(g_cute_log_fd, sizeof(void *) == 4 ? "0x%.8X" : "0x%.16X", vdata); 322 | break; 323 | 324 | } 325 | } 326 | } else { 327 | if (g_cute_log_fd != NULL) { 328 | fprintf(g_cute_log_fd, "%c", *mp); 329 | } 330 | } 331 | mp++; 332 | } 333 | fflush(g_cute_log_fd); 334 | if (should_log_to_stdout) { 335 | g_cute_log_fd = NULL; 336 | } 337 | va_end(val); 338 | } 339 | 340 | char *cute_get_option(const char *option, int argc, char **argv, char *default_value) { 341 | int a = 0; 342 | char optlabel[0xff]; 343 | if (option == NULL || argv == NULL || *argv == NULL) { 344 | return default_value; 345 | } 346 | sprintf(optlabel, "--%s=", option); 347 | for (a = 0; a < argc; a++) { 348 | if (strstr(argv[a], optlabel) == argv[a]) { 349 | return argv[a] + strlen(optlabel); 350 | } 351 | } 352 | sprintf(optlabel, "--%s", option); 353 | for (a = 0; a < argc; a++) { 354 | if (strcmp(argv[a], optlabel) == 0) { 355 | return "1"; 356 | } 357 | } 358 | return default_value; 359 | } 360 | 361 | void cute_log_memory_leak() { 362 | struct cute_mmap_ctx *mp; 363 | char *template_path = NULL; 364 | size_t a = 0; 365 | size_t leak_total = 0; 366 | template_path = cute_get_option("cutest-leak-log-header", g_cute_argc, g_cute_argv, NULL); 367 | if (template_path == NULL) { 368 | cute_log("\n\ncutest INTERNAL ERROR: Memory leak(s) detected!!\n\n>>>\n"); 369 | } else { 370 | g_leak_sum = 0; 371 | for (mp = g_cute_mmap; mp != NULL; mp = mp->next) { 372 | g_leak_sum += mp->size; 373 | } 374 | cute_set_log_template(template_path); 375 | cute_log(""); 376 | cute_set_log_template(NULL); 377 | } 378 | template_path = cute_get_option("cutest-leak-log-detail", g_cute_argc, g_cute_argv, NULL); 379 | if (template_path != NULL) { 380 | cute_set_log_template(template_path); 381 | } 382 | for (mp = g_cute_mmap; mp != NULL; mp = mp->next) { 383 | if (template_path == NULL) { 384 | cute_log("Id=%l Address=%m File=%s [The last check before leak was at line #%d] < ", mp->id, mp->addr, mp->file_path, mp->line_nr); 385 | for (a = 0; a < mp->size; a++) { 386 | if (isprint(((char *)mp->addr)[a] & 0xff)) { 387 | cute_log("%c", ((char *)mp->addr)[a]); 388 | } else { 389 | cute_log("."); 390 | } 391 | } 392 | cute_log(" > %d byte(s).\n", mp->size); 393 | } else { 394 | g_mp = mp; 395 | cute_log(""); 396 | g_mp = NULL; 397 | } 398 | leak_total += mp->size; 399 | } 400 | if (template_path != NULL) { 401 | cute_set_log_template(NULL); 402 | } 403 | template_path = cute_get_option("cutest-leak-log-footer", g_cute_argc, g_cute_argv, NULL); 404 | if (template_path == NULL) { 405 | cute_log("\nLeak total: %d byte(s).\n<<<\n", leak_total); 406 | } else { 407 | cute_set_log_template(template_path); 408 | cute_log(""); 409 | cute_set_log_template(NULL); 410 | } 411 | } 412 | 413 | void cute_set_log_template(const char *template_file_path) { 414 | FILE *fp = NULL; 415 | long file_size = 0; 416 | memset(g_cute_user_template, 0, sizeof(g_cute_user_template)); 417 | if (template_file_path == NULL) { 418 | return; 419 | } 420 | fp = fopen(template_file_path, "rb"); 421 | if (fp == NULL) { 422 | return; 423 | } 424 | fseek(fp, 0L, SEEK_END); 425 | file_size = ftell(fp); 426 | fseek(fp, 0L, SEEK_SET); 427 | fread(g_cute_user_template, 1, sizeof(g_cute_user_template) - 1, fp); 428 | fclose(fp); 429 | } 430 | 431 | static int cute_find_option_in_option_list(const char *option, const char *option_list) { 432 | const char *op = option_list; 433 | if (op == NULL || option == NULL) { 434 | return 0; 435 | } 436 | while (*op != 0) { 437 | if (strstr(op, option) == op && (*(op + strlen(option)) == 0 || *(op + strlen(option)) == ',')) { 438 | return 1; 439 | } 440 | while (*op != ',' && *op != 0) { 441 | op++; 442 | } 443 | if (*op == ',') { 444 | op++; 445 | } 446 | while (*op == ' ' || *op == '\n' || *op == '\r') { 447 | op++; 448 | } 449 | } 450 | return 0; 451 | } 452 | 453 | int cute_should_run_suite(const char *suite) { 454 | const char *cutest_run_suite = CUTE_GET_OPTION("cutest-run-suite"); 455 | if (cutest_run_suite == NULL || suite == NULL) { 456 | return 1; 457 | } 458 | return cute_find_option_in_option_list(suite, cutest_run_suite); 459 | } 460 | 461 | int cute_should_run_test(const char *test) { 462 | const char *cutest_run_test = CUTE_GET_OPTION("cutest-run-test"); 463 | if (cutest_run_test == NULL || test == NULL) { 464 | return 1; 465 | } 466 | return cute_find_option_in_option_list(test, cutest_run_test); 467 | } 468 | 469 | #endif 470 | -------------------------------------------------------------------------------- /src/cutest.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 by Rafael Santiago 3 | * 4 | * This is a free software. You can redistribute it and/or modify under 5 | * the terms of the GNU General Public License version 2. 6 | * 7 | */ 8 | #ifndef CUTEST_H 9 | #define CUTEST_H 1 10 | 11 | #if !(defined(_KERNEL) && defined(_LKM) && defined(__NetBSD__)) 12 | 13 | #include 14 | #include 15 | #ifndef _WIN32 16 | #ifndef NO_CUTEST_BACKTRACING 17 | #include 18 | #endif 19 | #endif 20 | #ifndef _MSC_VER 21 | #include 22 | #endif 23 | #include 24 | 25 | #ifdef __cplusplus 26 | extern "C" { 27 | #endif 28 | 29 | #include "cutest_memory.h" 30 | #include "cutest_mmap.h" 31 | 32 | #define CUTE_PASSED_LABEL "passed" 33 | 34 | #define CUTE_FAILED_LABEL "failed" 35 | 36 | #define CUTE_CHECK(msg, chk) do { g_cute_last_exec_line = __LINE__; g_cute_last_ref_file = __FILE__; g_cute_test_status = CUTE_PASSED_LABEL; if ((chk) == 0) { g_cute_test_status = CUTE_FAILED_LABEL; g_cute_assertion_message = msg; cute_log("hmm bad, bad bug in %s at line %d: ", __FILE__, __LINE__); /*cute_close_log_fd();*/ return msg; } } while (0) 37 | 38 | #define CUTE_CHECK_EQ(msg, a, b) CUTE_CHECK(msg, (a) == (b)) 39 | 40 | #define CUTE_CHECK_NEQ(msg, a, b) CUTE_CHECK(msg, (a) != (b)) 41 | 42 | #define CUTE_CHECK_LE(msg, a, b) CUTE_CHECK(msg, (a) < (b)) 43 | 44 | #define CUTE_CHECK_GR(msg, a, b) CUTE_CHECK(msg, (a) > (b)) 45 | 46 | #define CUTE_CHECK_LEQ(msg, a, b) CUTE_CHECK(msg, (a) <= (b)) 47 | 48 | #define CUTE_CHECK_GEQ(msg, a, b) CUTE_CHECK(msg, (a) >= (b)) 49 | 50 | #define CUTE_ASSERT_CHECK(msg, chk) do { g_cute_last_exec_line = __LINE__; g_cute_last_ref_file = __FILE__; g_cute_test_status = CUTE_PASSED_LABEL; if ((chk) == 0) { g_cute_test_status = CUTE_FAILED_LABEL; g_cute_assertion_message = "("msg") is false"; cute_log("hmm bad, bad bug in %s at line %d: ", __FILE__, __LINE__); /*cute_close_log_fd();*/ return "("msg") is false"; } } while (0) 51 | 52 | #define CUTE_ASSERT(chk) CUTE_ASSERT_CHECK(#chk, chk) 53 | 54 | #define CUTE_ASSERT_EQ(a, b) CUTE_ASSERT_CHECK(#a " == " #b, (a) == (b)) 55 | 56 | #define CUTE_ASSERT_NEQ(a, b) CUTE_ASSERT_CHECK(#a " != " #b, (a) != (b)) 57 | 58 | #define CUTE_ASSERT_LE(a, b) CUTE_ASSERT_CHECK(#a " < " #b, (a) < (b)) 59 | 60 | #define CUTE_ASSERT_GR(a, b) CUTE_ASSERT_CHECK(#a " > " #b, (a) > (b)) 61 | 62 | #define CUTE_ASSERT_LEQ(a, b) CUTE_ASSERT_CHECK(#a " <= " #b, (a) <= (b)) 63 | 64 | #define CUTE_ASSERT_GEQ(a, b) CUTE_ASSERT_CHECK(#a " >= " #b, (a) >= (b)) 65 | 66 | #define CUTE_RUN_TEST(test) do {\ 67 | if (cute_should_run_test(#test)) {\ 68 | char *msg = NULL;\ 69 | if (g_cute_user_template[0] == 0) cute_log("-- running %s...\n", #test);\ 70 | g_cute_last_exec_line = __LINE__; g_cute_last_ref_file = __FILE__;\ 71 | g_cute_test_name = #test;\ 72 | g_cute_test_status = NULL;\ 73 | msg = test();\ 74 | g_cute_ran_tests++;\ 75 | if (msg != NULL) { g_cute_test_status = CUTE_FAILED_LABEL; return msg; }\ 76 | g_cute_test_status = CUTE_PASSED_LABEL;\ 77 | cute_log("-- passed.\n");\ 78 | }\ 79 | } while (0) 80 | 81 | #define CUTE_RUN_TEST_WITH_FIXTURE(test) do {\ 82 | if (cute_should_run_test(#test)) {\ 83 | char *msg = NULL;\ 84 | g_cute_last_exec_line = __LINE__; g_cute_last_ref_file = __FILE__;\ 85 | g_cute_test_name = #test;\ 86 | g_cute_fixture_setup = test ## _setup;\ 87 | g_cute_fixture_teardown = test ## _teardown;\ 88 | test ## _setup();\ 89 | g_cute_fixture_setup = NULL;\ 90 | if (g_cute_user_template[0] == 0) cute_log("-- running %s...\n", #test);\ 91 | msg = test();\ 92 | g_cute_ran_tests++;\ 93 | test ## _teardown();\ 94 | g_cute_fixture_teardown = NULL;\ 95 | if (msg != NULL) return msg;\ 96 | cute_log("-- passed.\n");\ 97 | }\ 98 | } while (0) 99 | 100 | 101 | #define CUTE_RUN_TEST_NTIMES(test, times) do {\ 102 | if (cute_should_run_test(#test)) {\ 103 | g_cute_last_exec_line = __LINE__; g_cute_last_ref_file = __FILE__;\ 104 | for (g_cute_general_counter = 0; g_cute_general_counter < times; g_cute_general_counter++) {\ 105 | CUTE_RUN_TEST(test);\ 106 | }\ 107 | }\ 108 | } while (0) 109 | 110 | #define CUTE_RUN(entry) do {\ 111 | char *entry_return = entry();\ 112 | if (entry_return == NULL) {\ 113 | cute_set_log_template(NULL);\ 114 | user_template = cute_get_option("cutest-log-footer", argc, argv, NULL);\ 115 | if (user_template != NULL) {\ 116 | cute_set_log_template(user_template);\ 117 | cute_log("");\ 118 | }\ 119 | if (g_cute_user_template[0] == 0) {\ 120 | cute_log("*** all tests passed. [%d test(s) ran]\n", g_cute_ran_tests);\ 121 | }\ 122 | } else {\ 123 | user_template = cute_get_option("cutest-log-footer", argc, argv, NULL);\ 124 | if (user_template != NULL) {\ 125 | cute_set_log_template(user_template);\ 126 | cute_log("");\ 127 | cute_set_log_template(NULL);\ 128 | } else {\ 129 | cute_log("*** fail: %s [%d test(s) ran]\n", entry_return, g_cute_ran_tests);\ 130 | }\ 131 | cute_close_log_fd();\ 132 | cute_set_log_template(NULL);\ 133 | return 1;\ 134 | }\ 135 | } while(0); 136 | 137 | #define CUTE_DECLARE_TEST_CASE(test) char *test() 138 | 139 | #define CUTE_DECLARE_TEST_CASE_WITH_FIXTURE(test) void test ## _setup();\ 140 | char *test();\ 141 | void test ## _teardown() 142 | #define CUTE_TEST_CASE(test) char *test() { 143 | 144 | #define CUTE_TEST_CASE_END return NULL;\ 145 | } 146 | 147 | #define CUTE_FIXTURE_SETUP(test) void test ## _setup() { 148 | 149 | #define CUTE_FIXTURE_END } 150 | 151 | #define CUTE_FIXTURE_TEARDOWN(test) void test ## _teardown() { 152 | 153 | #define CUTE_TEST_CASE_SUITE(suite) char *suite() {\ 154 | if (!cute_should_run_suite(#suite)) {\ 155 | return NULL;\ 156 | } 157 | 158 | #define CUTE_TEST_CASE_SUITE_END return NULL;\ 159 | } 160 | 161 | #define CUTE_DECLARE_TEST_CASE_SUITE(suite) CUTE_DECLARE_TEST_CASE(suite) 162 | 163 | #define CUTE_RUN_TEST_SUITE(suite) do {\ 164 | if (cute_should_run_suite(#suite)) {\ 165 | CUTE_RUN_TEST(suite);\ 166 | }\ 167 | } while (0); 168 | 169 | #if !defined(_WIN32) && !defined(NO_CUTEST_BACKTRACING) 170 | 171 | // WARN(Santiago): About the "setbuf(stdout, NULL)" and the "setbuf(stderr, NULL)" that you will find inside the following main(). 172 | // 173 | // This is a workaround for avoiding the 4k leak which stdio lets. 174 | // 175 | // It is caused due to an allocation done at the first time of printf's functions calling. 176 | // 177 | // It seems to be done for performance issues, but is a kind of annoying. When we are hunting for real leaks. 178 | // 179 | // Anyway, with the following setbuf()'s the 4k alloc is inhibited. What does it stop "leaking". 180 | // 181 | 182 | #define CUTE_MAIN(entry) void sigint_watchdog(int signum) {\ 183 | printf("-- CUTE INT TRAP --\n");\ 184 | if (g_cute_fixture_teardown != NULL) {\ 185 | g_cute_fixture_teardown();\ 186 | g_cute_fixture_teardown = NULL;\ 187 | }\ 188 | }\ 189 | void sigsegv_watchdog(int signum) {\ 190 | size_t size;\ 191 | void *array[50];\ 192 | printf("-- CUTEST PANIC TRAP --\n");\ 193 | if (g_cute_last_exec_line > -1) {\ 194 | printf("\n< The last successfully executed line was the line #%d from file \"%s\" >", g_cute_last_exec_line, g_cute_last_ref_file);\ 195 | }\ 196 | printf("\n\n");\ 197 | size = backtrace(array, 50);\ 198 | backtrace_symbols_fd(array, size, STDERR_FILENO);\ 199 | printf("\n\n");\ 200 | exit(1);\ 201 | }\ 202 | int main(int argc, char **argv) {\ 203 | char *logpath = NULL;\ 204 | char *user_template = NULL;\ 205 | char *leak_id = NULL;\ 206 | int exit_code = 0;\ 207 | setbuf(stdout, NULL);\ 208 | setbuf(stderr, NULL);\ 209 | signal(SIGSEGV, sigsegv_watchdog);\ 210 | signal(SIGBUS, sigsegv_watchdog);\ 211 | signal(SIGABRT, sigsegv_watchdog);\ 212 | signal(SIGINT, sigint_watchdog);\ 213 | signal(SIGTERM, sigint_watchdog);\ 214 | init_memory_func_ptr();\ 215 | init_mmap_mutex();\ 216 | logpath = cute_get_option("cutest-log-path", argc, argv, NULL);\ 217 | if (cute_get_option("cutest-leak-check", argc, argv, NULL) != NULL) {\ 218 | g_cute_leak_check = 1;\ 219 | }\ 220 | leak_id = cute_get_option("cutest-leak-id", argc, argv, NULL);\ 221 | if (leak_id != NULL) {\ 222 | g_cute_leak_id = atoi(leak_id);\ 223 | }\ 224 | g_cute_argv = argv;\ 225 | g_cute_argc = argc;\ 226 | if (logpath != NULL) {\ 227 | cute_open_log_fd(logpath);\ 228 | }\ 229 | user_template = cute_get_option("cutest-log-header", argc, argv, NULL);\ 230 | if (user_template != NULL) {\ 231 | cute_set_log_template(user_template);\ 232 | cute_log("");\ 233 | cute_set_log_template(NULL);\ 234 | }\ 235 | user_template = cute_get_option("cutest-log-detail", argc, argv, NULL);\ 236 | if (user_template != NULL) {\ 237 | cute_set_log_template(user_template);\ 238 | }\ 239 | CUTE_RUN(entry);\ 240 | if (g_cute_leak_check && g_cute_mmap != NULL) {\ 241 | cute_log_memory_leak();\ 242 | del_cute_mmap_ctx(g_cute_mmap);\ 243 | exit_code = 1;\ 244 | }\ 245 | cute_close_log_fd();\ 246 | deinit_mmap_mutex();\ 247 | return exit_code;\ 248 | } 249 | 250 | #elif !defined(_WIN32) && defined(NO_CUTEST_BACKTRACING) 251 | 252 | #define CUTE_MAIN(entry) void sigint_watchdog(int signum) {\ 253 | printf("-- CUTE INT TRAP --\n");\ 254 | if (g_cute_fixture_teardown != NULL) {\ 255 | g_cute_fixture_teardown();\ 256 | g_cute_fixture_teardown = NULL;\ 257 | }\ 258 | }\ 259 | void sigsegv_watchdog(int signum) {\ 260 | printf("-- CUTEST PANIC TRAP --\n");\ 261 | if (g_cute_last_exec_line > -1) {\ 262 | printf("\n< The last successfully executed line was the line #%d from file \"%s\" >", g_cute_last_exec_line, g_cute_last_ref_file);\ 263 | }\ 264 | printf("\n\n");\ 265 | exit(1);\ 266 | }\ 267 | int main(int argc, char **argv) {\ 268 | char *logpath = NULL;\ 269 | char *user_template = NULL;\ 270 | char *leak_id = NULL;\ 271 | int exit_code = 0;\ 272 | setbuf(stdout, NULL);\ 273 | setbuf(stderr, NULL);\ 274 | signal(SIGSEGV, sigsegv_watchdog);\ 275 | signal(SIGBUS, sigsegv_watchdog);\ 276 | signal(SIGABRT, sigsegv_watchdog);\ 277 | signal(SIGINT, sigint_watchdog);\ 278 | signal(SIGTERM, sigint_watchdog);\ 279 | init_memory_func_ptr();\ 280 | init_mmap_mutex();\ 281 | logpath = cute_get_option("cutest-log-path", argc, argv, NULL);\ 282 | if (cute_get_option("cutest-leak-check", argc, argv, NULL) != NULL) {\ 283 | g_cute_leak_check = 1;\ 284 | }\ 285 | leak_id = cute_get_option("cutest-leak-id", argc, argv, NULL);\ 286 | if (leak_id != NULL) {\ 287 | g_cute_leak_id = atoi(leak_id);\ 288 | }\ 289 | g_cute_argv = argv;\ 290 | g_cute_argc = argc;\ 291 | if (logpath != NULL) {\ 292 | cute_open_log_fd(logpath);\ 293 | }\ 294 | user_template = cute_get_option("cutest-log-header", argc, argv, NULL);\ 295 | if (user_template != NULL) {\ 296 | cute_set_log_template(user_template);\ 297 | cute_log("");\ 298 | cute_set_log_template(NULL);\ 299 | }\ 300 | user_template = cute_get_option("cutest-log-detail", argc, argv, NULL);\ 301 | if (user_template != NULL) {\ 302 | cute_set_log_template(user_template);\ 303 | }\ 304 | CUTE_RUN(entry);\ 305 | if (g_cute_leak_check && g_cute_mmap != NULL) {\ 306 | cute_log_memory_leak();\ 307 | del_cute_mmap_ctx(g_cute_mmap);\ 308 | exit_code = 1;\ 309 | }\ 310 | cute_close_log_fd();\ 311 | deinit_mmap_mutex();\ 312 | return exit_code;\ 313 | } 314 | 315 | 316 | #else 317 | 318 | #define CUTE_MAIN(entry) void sigint_watchdog(int signum) {\ 319 | printf("-- CUTE INT TRAP --\n");\ 320 | if (g_cute_fixture_teardown != NULL) {\ 321 | g_cute_fixture_teardown();\ 322 | g_cute_fixture_teardown = NULL;\ 323 | }\ 324 | }\ 325 | void sigsegv_watchdog(int signum) {\ 326 | size_t size;\ 327 | void *array[50];\ 328 | printf("-- CUTE PANIC TRAP --\n");\ 329 | if (g_cute_last_exec_line > -1) {\ 330 | printf("\n< The last successfully executed line was the line #%d from file \"%s\" >", g_cute_last_exec_line, g_cute_last_ref_file);\ 331 | }\ 332 | exit(1);\ 333 | }\ 334 | int main(int argc, char **argv) {\ 335 | char *logpath = NULL;\ 336 | char *user_template = NULL;\ 337 | char *leak_id = NULL;\ 338 | int exit_code = 0;\ 339 | signal(SIGSEGV, sigsegv_watchdog);\ 340 | signal(SIGABRT, sigsegv_watchdog);\ 341 | signal(SIGINT, sigint_watchdog);\ 342 | signal(SIGTERM, sigint_watchdog);\ 343 | init_memory_func_ptr();\ 344 | init_mmap_mutex();\ 345 | logpath = cute_get_option("cutest-log-path", argc, argv, NULL);\ 346 | if (cute_get_option("cutest-leak-check", argc, argv, NULL) != NULL) {\ 347 | g_cute_leak_check = 1;\ 348 | }\ 349 | leak_id = cute_get_option("cutest-leak-id", argc, argv, NULL);\ 350 | if (leak_id != NULL) {\ 351 | g_cute_leak_id = atoi(leak_id);\ 352 | }\ 353 | g_cute_argv = argv;\ 354 | g_cute_argc = argc;\ 355 | if (logpath != NULL) {\ 356 | cute_open_log_fd(logpath);\ 357 | }\ 358 | user_template = cute_get_option("cutest-log-header", argc, argv, NULL);\ 359 | if (user_template != NULL) {\ 360 | cute_set_log_template(user_template);\ 361 | cute_log("");\ 362 | cute_set_log_template(NULL);\ 363 | }\ 364 | user_template = cute_get_option("cutest-log-detail", argc, argv, NULL);\ 365 | if (user_template != NULL) {\ 366 | cute_set_log_template(user_template);\ 367 | }\ 368 | CUTE_RUN(entry);\ 369 | if (g_cute_leak_check && g_cute_mmap != NULL) {\ 370 | cute_log_memory_leak();\ 371 | del_cute_mmap_ctx(g_cute_mmap);\ 372 | exit_code = 1;\ 373 | }\ 374 | cute_close_log_fd();\ 375 | deinit_mmap_mutex();\ 376 | return exit_code;\ 377 | } 378 | 379 | #endif 380 | 381 | #define CUTE_GET_OPTION(o) ( cute_get_option(o, g_cute_argc, g_cute_argv, NULL) ) 382 | 383 | #define CUTE_CASE_NAME ( g_cute_test_name ) 384 | 385 | extern int g_cute_general_counter; 386 | 387 | extern int g_cute_ran_tests; 388 | 389 | extern FILE *g_cute_log_fd; 390 | 391 | extern char **g_cute_argv; 392 | 393 | extern int g_cute_argc; 394 | 395 | extern int g_cute_leak_check; 396 | 397 | extern void (*g_cute_fixture_setup)(); 398 | 399 | extern void (*g_cute_fixture_teardown)(); 400 | 401 | extern int g_cute_last_exec_line; 402 | 403 | extern char *g_cute_last_ref_file; 404 | 405 | extern struct cute_mmap_ctx *g_cute_mmap, *g_cute_mmap_tail; 406 | 407 | extern char *g_cute_test_status; 408 | 409 | extern char *g_cute_test_name; 410 | 411 | extern char *g_cute_test_log_header; 412 | 413 | extern char *g_cute_test_log_detail; 414 | 415 | extern char *g_cute_test_log_footer; 416 | 417 | extern char g_cute_user_template[0xffff]; 418 | 419 | extern char *g_cute_assertion_message; 420 | 421 | extern int g_cute_leak_id; 422 | 423 | void cute_open_log_fd(const char *filepath); 424 | 425 | void cute_close_log_fd(); 426 | 427 | void cute_log(const char *msg, ...); 428 | 429 | char *cute_get_option(const char *option, int argc, char **argv, char *default_value); 430 | 431 | void cute_log_memory_leak(); 432 | 433 | void cute_set_log_template(const char *template_file_path); 434 | 435 | int cute_should_run_suite(const char *suite); 436 | 437 | int cute_should_run_test(const char *test); 438 | 439 | #if __cplusplus 440 | } 441 | #endif 442 | 443 | #endif 444 | 445 | #endif 446 | -------------------------------------------------------------------------------- /src/cutest_memory.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 by Rafael Santiago 3 | * 4 | * This is a free software. You can redistribute it and/or modify under 5 | * the terms of the GNU General Public License version 2. 6 | * 7 | */ 8 | #if !(defined(_KERNEL) && defined(_LKM) && defined(__NetBSD__)) 9 | 10 | #include "cutest_memory.h" 11 | #include "cutest.h" 12 | #include "cutest_mmap.h" 13 | #ifndef _WIN32 14 | #include 15 | #else 16 | #include 17 | #endif 18 | 19 | #ifndef _WIN32 20 | 21 | #ifndef RTLD_NEXT 22 | 23 | #define RTLD_NEXT -1 24 | 25 | #endif 26 | 27 | #endif 28 | 29 | void *(*tru_calloc)(size_t, size_t) = NULL; 30 | 31 | void *(*tru_malloc)(size_t) = NULL; 32 | 33 | void *(*tru_free)(void *) = NULL; 34 | 35 | void *(*tru_realloc)(void *, size_t) = NULL; 36 | 37 | static int g_memhook_init_done = 0; 38 | 39 | void init_memory_func_ptr() { 40 | #ifdef _WIN32 41 | HMODULE handle = NULL; 42 | #endif 43 | if (tru_calloc != NULL && tru_malloc != NULL && tru_realloc != NULL && tru_free != NULL) { 44 | return; 45 | } 46 | #ifndef _WIN32 47 | void *handle = (void *)RTLD_NEXT; 48 | tru_calloc = (void *)dlsym(handle, "calloc"); 49 | tru_malloc = (void *)dlsym(handle, "malloc"); 50 | tru_free = (void *)dlsym(handle, "free"); 51 | tru_realloc = (void *)dlsym(handle, "realloc"); 52 | g_memhook_init_done = 1; 53 | #else 54 | handle = LoadLibrary("ucrtbase.dll"); 55 | if (handle != NULL) { 56 | tru_calloc = (void *)GetProcAddress(handle, "calloc"); 57 | tru_malloc = (void *)GetProcAddress(handle, "malloc"); 58 | tru_free = (void *)GetProcAddress(handle, "free"); 59 | tru_realloc = (void *)GetProcAddress(handle, "realloc"); 60 | g_memhook_init_done = 1; 61 | } else { 62 | cute_log("libcutest INTERNAL ERROR: unable to find \"MSVCRT.dll\".\n"); 63 | } 64 | #endif 65 | } 66 | 67 | void *calloc(size_t nmemb, size_t size) { 68 | void *retval = NULL; 69 | if (tru_calloc == NULL) { 70 | #if defined(_WIN32) || defined(__FreeBSD__) 71 | init_memory_func_ptr(); 72 | #endif 73 | //if (g_memhook_init_done) { 74 | // cute_log("libcutest INTERNAL ERROR: null tru_calloc().\n"); 75 | //} 76 | if (tru_calloc == NULL) { 77 | return NULL; 78 | } 79 | } 80 | retval = tru_calloc(nmemb, size); 81 | if (g_cute_leak_check) { 82 | g_cute_mmap = add_allocation_to_cute_mmap_ctx(g_cute_mmap, size, retval, &g_cute_mmap_tail); 83 | } 84 | return retval; 85 | } 86 | 87 | void *malloc(size_t size) { 88 | void *retval = NULL; 89 | if (tru_malloc == NULL) { 90 | #if defined(_WIN32) || defined(__FreeBSD__) 91 | init_memory_func_ptr(); 92 | #endif 93 | //if (g_memhook_init_done) { 94 | // cute_log("libcutest INTERNAL ERROR: null tru_malloc().\n"); 95 | //} 96 | if (tru_malloc == NULL) { 97 | return NULL; 98 | } 99 | } 100 | retval = tru_malloc(size); 101 | if (g_cute_leak_check) { 102 | g_cute_mmap = add_allocation_to_cute_mmap_ctx(g_cute_mmap, size, retval, &g_cute_mmap_tail); 103 | } 104 | return retval; 105 | } 106 | 107 | void free(void *ptr) { 108 | if (tru_free == NULL) { 109 | #if defined(_WIN32) || defined(__FreeBSD__) 110 | init_memory_func_ptr(); 111 | #endif 112 | //if (g_memhook_init_done) { 113 | // cute_log("libcutest INTERNAL ERROR: null tru_free().\n"); 114 | //} 115 | if (tru_free == NULL) { 116 | return; 117 | } 118 | } 119 | if (ptr == NULL) { 120 | return; 121 | } 122 | if (g_cute_leak_check) { 123 | g_cute_mmap = rm_allocation_from_cute_mmap_ctx(g_cute_mmap, ptr, &g_cute_mmap_tail); 124 | } 125 | tru_free(ptr); 126 | } 127 | 128 | void *realloc(void *ptr, size_t size) { 129 | void *retval = NULL; 130 | if (tru_realloc == NULL) { 131 | #if defined(_WIN32) || defined(__FreeBSD__) 132 | init_memory_func_ptr(); 133 | #endif 134 | //if (g_memhook_init_done) { 135 | // cute_log("libcutest INTERNAL ERROR: null tru_realloc().\n"); 136 | //} 137 | if (tru_realloc == NULL) { 138 | return NULL; 139 | } 140 | } 141 | retval = tru_realloc(ptr, size); 142 | if (g_cute_leak_check) { 143 | g_cute_mmap = rm_allocation_from_cute_mmap_ctx(g_cute_mmap, ptr, &g_cute_mmap_tail); 144 | g_cute_mmap = add_allocation_to_cute_mmap_ctx(g_cute_mmap, size, retval, &g_cute_mmap_tail); 145 | } 146 | return retval; 147 | } 148 | 149 | #endif 150 | -------------------------------------------------------------------------------- /src/cutest_memory.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 by Rafael Santiago 3 | * 4 | * This is a free software. You can redistribute it and/or modify under 5 | * the terms of the GNU General Public License version 2. 6 | * 7 | */ 8 | #ifndef CUTEST_MEMORY_H 9 | #define CUTEST_MEMORY_H 1 10 | 11 | #if !(defined(_KERNEL) && defined(_LKM) && defined(__NetBSD__)) 12 | 13 | #include 14 | 15 | #ifdef __cplusplus 16 | extern "C" { 17 | #endif 18 | 19 | extern void *(*tru_calloc)(size_t, size_t); 20 | 21 | extern void *(*tru_malloc)(size_t); 22 | 23 | extern void *(*tru_free)(void *); 24 | 25 | extern void *(*tru_realloc)(void *, size_t); 26 | 27 | #if !defined(_WIN32) && !defined(__FreeBSD__) 28 | 29 | void *calloc(size_t nmemb, size_t size); 30 | 31 | void *malloc(size_t size); 32 | 33 | void free(void *ptr); 34 | 35 | void *realloc(void *ptr, size_t size); 36 | 37 | #endif 38 | 39 | void init_memory_func_ptr(); 40 | 41 | #ifdef __cplusplus 42 | } 43 | #endif 44 | 45 | #endif 46 | 47 | #endif 48 | -------------------------------------------------------------------------------- /src/cutest_mmap.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 by Rafael Santiago 3 | * 4 | * This is a free software. You can redistribute it and/or modify under 5 | * the terms of the GNU General Public License version 2. 6 | * 7 | */ 8 | #if !(defined(_KERNEL) && defined(_LKM) && defined(__NetBSD__)) 9 | 10 | #include "cutest_mmap.h" 11 | #include "cutest.h" 12 | #include 13 | #ifndef _WIN32 14 | #ifndef HAS_NO_PTHREAD 15 | #include 16 | #endif 17 | #else 18 | #include 19 | #endif 20 | #include 21 | 22 | static int g_temp_cute_leak_check = 0; 23 | 24 | #define new_cute_mmap_ctx(m) ( g_temp_cute_leak_check = g_cute_leak_check,\ 25 | g_cute_leak_check = 0,\ 26 | (m) = tru_malloc(sizeof(struct cute_mmap_ctx)),\ 27 | (m)->next = NULL,\ 28 | (m)->line_nr = g_cute_last_exec_line,\ 29 | strncpy((m)->file_path, g_cute_last_ref_file, sizeof((m)->file_path)-1),\ 30 | g_cute_leak_check = g_temp_cute_leak_check ) 31 | 32 | #ifndef _WIN32 33 | 34 | #ifndef HAS_NO_PTHREAD 35 | 36 | static pthread_mutex_t mmap_mutex = PTHREAD_MUTEX_INITIALIZER; 37 | 38 | #endif 39 | 40 | #else 41 | 42 | HANDLE mmap_mutex; 43 | 44 | #endif 45 | 46 | void init_mmap_mutex() { 47 | #ifndef _WIN32 48 | 49 | #ifndef HAS_NO_PTHREAD 50 | 51 | pthread_mutexattr_t attr; 52 | pthread_mutexattr_init(&attr); 53 | pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); 54 | pthread_mutex_init(&mmap_mutex, &attr); 55 | pthread_mutexattr_destroy(&attr); 56 | #endif 57 | 58 | #else 59 | mmap_mutex = CreateMutex(NULL, FALSE, NULL); 60 | #endif 61 | } 62 | 63 | void deinit_mmap_mutex() { 64 | #ifdef _WIN32 65 | CloseHandle(mmap_mutex); 66 | #endif 67 | } 68 | 69 | static struct cute_mmap_ctx *get_cute_mmap_ctx_tail(struct cute_mmap_ctx *mmap) { 70 | struct cute_mmap_ctx *p; 71 | if (mmap == NULL) { 72 | return NULL; 73 | } 74 | for (p = mmap; p->next != NULL; p = p->next) 75 | ; 76 | return p; 77 | } 78 | 79 | struct cute_mmap_ctx *add_allocation_to_cute_mmap_ctx(struct cute_mmap_ctx *mmap, 80 | size_t size, void *addr, struct cute_mmap_ctx **tail) { 81 | struct cute_mmap_ctx *head = NULL; 82 | struct cute_mmap_ctx *p = NULL; 83 | 84 | if (g_cute_last_ref_file == NULL || addr == NULL) { 85 | return mmap; 86 | } 87 | #ifndef _WIN32 88 | 89 | #ifndef HAS_NO_PTHREAD 90 | 91 | pthread_mutex_lock(&mmap_mutex); 92 | 93 | #endif 94 | 95 | #else 96 | WaitForSingleObject(mmap_mutex, INFINITE); 97 | #endif 98 | head = mmap; 99 | if (head == NULL) { 100 | new_cute_mmap_ctx(head); 101 | p = head; 102 | if (tail != NULL) { 103 | (*tail) = head; 104 | } 105 | } else { 106 | if (tail == NULL) { 107 | p = get_cute_mmap_ctx_tail(mmap); 108 | } else { 109 | p = (*tail); 110 | } 111 | new_cute_mmap_ctx(p->next); 112 | p = p->next; 113 | if (tail != NULL) { 114 | (*tail) = p; 115 | } 116 | } 117 | p->id = ++g_cute_mmap_id; 118 | p->size = size; 119 | p->addr = addr; 120 | if (p->id == g_cute_leak_id) { 121 | #ifndef _WIN32 122 | raise(SIGTRAP); 123 | #else 124 | DebugBreak(); 125 | #endif 126 | } 127 | #ifndef _WIN32 128 | 129 | #ifndef HAS_NO_PTHREAD 130 | 131 | pthread_mutex_unlock(&mmap_mutex); 132 | 133 | #endif 134 | 135 | #else 136 | ReleaseMutex(mmap_mutex); 137 | #endif 138 | return head; 139 | } 140 | 141 | struct cute_mmap_ctx *rm_allocation_from_cute_mmap_ctx(struct cute_mmap_ctx *mmap, 142 | void *addr, struct cute_mmap_ctx **tail) { 143 | struct cute_mmap_ctx *head = NULL; 144 | struct cute_mmap_ctx *burn = NULL; 145 | struct cute_mmap_ctx *last = NULL; 146 | #ifndef _WIN32 147 | 148 | #ifndef HAS_NO_PTHREAD 149 | 150 | pthread_mutex_lock(&mmap_mutex); 151 | 152 | #endif 153 | 154 | #else 155 | WaitForSingleObject(mmap_mutex, INFINITE); 156 | #endif 157 | head = mmap; 158 | if (mmap == NULL) { 159 | #ifndef _WIN32 160 | 161 | #ifndef HAS_NO_PTHREAD 162 | 163 | pthread_mutex_unlock(&mmap_mutex); 164 | 165 | #endif 166 | 167 | #else 168 | ReleaseMutex(mmap_mutex); 169 | #endif 170 | return NULL; 171 | } 172 | for (burn = mmap; burn != NULL; last = burn, burn = burn->next) { 173 | if (burn->addr == addr) { 174 | break; 175 | } 176 | } 177 | if (burn != NULL) { 178 | if (last == NULL) { 179 | head = burn->next; 180 | burn->next = NULL; 181 | } else { 182 | if (tail != NULL && burn->next == NULL) { 183 | (*tail) = last; 184 | } 185 | last->next = burn->next; 186 | burn->next = NULL; 187 | } 188 | free(burn); 189 | } 190 | #ifndef _WIN32 191 | 192 | #ifndef HAS_NO_PTHREAD 193 | 194 | pthread_mutex_unlock(&mmap_mutex); 195 | 196 | #endif 197 | 198 | #else 199 | ReleaseMutex(mmap_mutex); 200 | #endif 201 | return head; 202 | } 203 | 204 | void del_cute_mmap_ctx(struct cute_mmap_ctx *mmap) { 205 | struct cute_mmap_ctx *p = NULL, *t = NULL; 206 | int temp = 0; 207 | #ifndef _WIN32 208 | 209 | #ifndef HAS_NO_PTHREAD 210 | 211 | pthread_mutex_lock(&mmap_mutex); 212 | 213 | #endif 214 | 215 | #else 216 | WaitForSingleObject(mmap_mutex, INFINITE); 217 | #endif 218 | temp = g_cute_leak_check; 219 | g_cute_leak_check = 0; 220 | for (t = p = mmap; t != NULL; p = t) { 221 | t = p->next; 222 | tru_free(p); 223 | } 224 | g_cute_leak_check = temp; 225 | #ifndef _WIN32 226 | 227 | #ifndef HAS_NO_PTHREAD 228 | 229 | pthread_mutex_unlock(&mmap_mutex); 230 | 231 | #endif 232 | 233 | #else 234 | ReleaseMutex(mmap_mutex); 235 | #endif 236 | } 237 | 238 | #endif 239 | -------------------------------------------------------------------------------- /src/cutest_mmap.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 by Rafael Santiago 3 | * 4 | * This is a free software. You can redistribute it and/or modify under 5 | * the terms of the GNU General Public License version 2. 6 | * 7 | */ 8 | #ifndef CUTEST_MMAP_H 9 | #define CUTEST_MMAP_H 1 10 | 11 | #if !(defined(_KERNEL) && defined(_LKM) && defined(__NetBSD__)) 12 | 13 | #include 14 | 15 | #ifdef __cplusplus 16 | extern "C" { 17 | #endif 18 | 19 | static long long g_cute_mmap_id = 0; 20 | 21 | struct cute_mmap_ctx { 22 | long long id; 23 | size_t size; 24 | void *addr; 25 | char file_path[8192]; 26 | int line_nr; 27 | struct cute_mmap_ctx *next; 28 | }; 29 | 30 | struct cute_mmap_ctx *add_allocation_to_cute_mmap_ctx(struct cute_mmap_ctx *mmap, 31 | size_t size, void *addr, struct cute_mmap_ctx **tail); 32 | 33 | struct cute_mmap_ctx *rm_allocation_from_cute_mmap_ctx(struct cute_mmap_ctx *mmap, 34 | void *addr, struct cute_mmap_ctx **tail); 35 | 36 | void del_cute_mmap_ctx(struct cute_mmap_ctx *mmap); 37 | 38 | void init_mmap_mutex(); 39 | 40 | void deinit_mmap_mutex(); 41 | 42 | #ifdef __cplusplus 43 | } 44 | #endif 45 | 46 | #endif 47 | 48 | #endif 49 | -------------------------------------------------------------------------------- /src/kutest.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 by Rafael Santiago 3 | * 4 | * This is a free software. You can redistribute it and/or modify under 5 | * the terms of the GNU General Public License version 2. 6 | * 7 | */ 8 | #ifndef KUTEST_H 9 | #define KUTEST_H 1 10 | 11 | #if defined(__FreeBSD__) 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | static int g_kutest_ran_tests = 0; 22 | 23 | #define KUTE_ASSERT_CHECK(msg, chk) do {\ 24 | if ((chk) == 0) {\ 25 | uprintf("hmm bad, bad bug in %s at line %d: %s is false.\n", __FILE__, __LINE__, msg);\ 26 | return 1;\ 27 | }\ 28 | } while (0) 29 | 30 | #define KUTE_RUN_TEST(test) do {\ 31 | uprintf("-- running %s...\n", #test);\ 32 | g_kutest_ran_tests++;\ 33 | if (test() != 0) {\ 34 | return 1;\ 35 | }\ 36 | uprintf("-- passed.\n");\ 37 | } while (0) 38 | 39 | #elif defined(__NetBSD__) 40 | 41 | #include 42 | 43 | static int g_kutest_ran_tests = 0; 44 | 45 | #define KUTE_ASSERT_CHECK(msg, chk) do {\ 46 | if ((chk) == 0) {\ 47 | uprintf("hmm bad, bad bug in %s at line %d: %s is false.\n", __FILE__, __LINE__, msg);\ 48 | return 1;\ 49 | }\ 50 | } while (0) 51 | 52 | #define KUTE_RUN_TEST(test) do {\ 53 | uprintf("-- running %s...\n", #test);\ 54 | g_kutest_ran_tests++;\ 55 | if (test() != 0) {\ 56 | return 1;\ 57 | }\ 58 | uprintf("-- passed.\n");\ 59 | } while (0) 60 | 61 | #elif defined(__linux__) 62 | 63 | #include 64 | #include 65 | 66 | #if !defined(__GNUC__) 67 | 68 | static int g_kutest_ran_tests = 0; 69 | 70 | #else 71 | 72 | __attribute__((__unused__)) static int g_kutest_ran_tests = 0; 73 | 74 | #endif 75 | 76 | #define KUTE_ASSERT_CHECK(msg, chk) do {\ 77 | if ((chk) == 0) {\ 78 | printk(KERN_ERR "hmm bad, bad bug in %s at line %d: %s is false.\n", __FILE__, __LINE__, msg);\ 79 | return -EAGAIN;\ 80 | }\ 81 | } while (0) 82 | 83 | #define KUTE_RUN_TEST(test) do {\ 84 | printk(KERN_ERR "-- running " #test "...\n");\ 85 | g_kutest_ran_tests++;\ 86 | if (test() != 0) {\ 87 | return -EAGAIN;\ 88 | }\ 89 | printk(KERN_ERR "-- passed.\n");\ 90 | } while (0) 91 | 92 | #elif defined(_WIN32) 93 | 94 | #include 95 | 96 | #pragma warning(disable : 4127) 97 | 98 | static int g_kutest_ran_tests = 0; 99 | 100 | #define KUTE_ASSERT_CHECK(msg, chk) do {\ 101 | if ((chk) == 0) {\ 102 | DbgPrint("hmm bad, bad bug in %s at line %d: %s is false.\n", __FILE__, __LINE__, msg);\ 103 | return STATUS_UNSUCCESSFUL;\ 104 | }\ 105 | } while(0) 106 | 107 | #define KUTE_RUN_TEST(test) do {\ 108 | DbgPrint("-- running " #test "...\n");\ 109 | g_kutest_ran_tests++;\ 110 | if (test() != 0) {\ 111 | return STATUS_UNSUCCESSFUL;\ 112 | }\ 113 | DbgPrint("-- passed.\n");\ 114 | } while(0) 115 | 116 | #endif 117 | 118 | #define KUTE_CHECK_EQ(msg, a, b) KUTE_ASSERT_CHECK(msg, (a) == (b)) 119 | 120 | #define KUTE_CHECK_NEQ(msg, a, b) KUTE_ASSERT_CHECK(msg, (a) != (b)) 121 | 122 | #define KUTE_CHECK_LE(msg, a, b) KUTE_ASSERT_CHECK(msg, (a) < (b)) 123 | 124 | #define KUTE_CHECK_GR(msg, a, b) KUTE_ASSERT_CHECK(msg, (a) > (b)) 125 | 126 | #define KUTE_CHECK_LEQ(msg, a, b) KUTE_ASSERT_CHECK(msg, (a) <= (b)) 127 | 128 | #define KUTE_CHECK_GEQ(msg, a, b) KUTE_ASSERT_CHECK(msg, (a) >= (b)) 129 | 130 | #define KUTE_ASSERT(chk) KUTE_ASSERT_CHECK(#chk, chk) 131 | 132 | #define KUTE_TEST_CASE(test) int test(void) { 133 | 134 | #define KUTE_TEST_CASE_END return 0;\ 135 | } 136 | 137 | #define KUTE_DECLARE_TEST_CASE(test) int test(void) 138 | 139 | #if defined(__FreeBSD__) 140 | 141 | #define KUTE_MAIN(test)\ 142 | static int modld(struct module *module, int cmd, void *arg) {\ 143 | int exit_code = 0;\ 144 | switch (cmd) {\ 145 | case MOD_LOAD:\ 146 | uprintf("*** " #test " loaded...\n");\ 147 | if ((exit_code = test()) == 0) {\ 148 | uprintf("*** all tests passed. [%d test(s) ran]\n", g_kutest_ran_tests);\ 149 | } else {\ 150 | uprintf("fail: [%d test(s) ran]\n", g_kutest_ran_tests);\ 151 | }\ 152 | break;\ 153 | case MOD_UNLOAD:\ 154 | uprintf("*** " #test " unloaded.\n");\ 155 | break;\ 156 | default:\ 157 | exit_code = EOPNOTSUPP;\ 158 | break;\ 159 | }\ 160 | return exit_code;\ 161 | }\ 162 | static moduledata_t test ## _mod = {\ 163 | #test,\ 164 | modld,\ 165 | NULL\ 166 | };\ 167 | DECLARE_MODULE(test, test ## _mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE); 168 | 169 | #elif defined(__NetBSD__) 170 | 171 | #define KUTE_MAIN(test)\ 172 | MODULE(MODULE_CLASS_DRIVER, test, NULL);\ 173 | static int test ##_modcmd(modcmd_t cmd, void *args) {\ 174 | int exit_code = 0;\ 175 | switch (cmd) {\ 176 | case MODULE_CMD_INIT:\ 177 | uprintf("***" #test " loaded...\n");\ 178 | if ((exit_code = test()) == 0) {\ 179 | uprintf("*** all tests passed. [%d test(s) ran]\n", g_kutest_ran_tests);\ 180 | } else {\ 181 | uprintf("fail: [%d test(s) ran]\n", g_kutest_ran_tests);\ 182 | }\ 183 | break;\ 184 | case MODULE_CMD_FINI:\ 185 | uprintf("*** " #test " unloaded.\n");\ 186 | break;\ 187 | default:\ 188 | exit_code = EOPNOTSUPP;\ 189 | break;\ 190 | }\ 191 | return exit_code;\ 192 | } 193 | 194 | #elif defined(__linux__) 195 | 196 | #define KUTE_MAIN(test)\ 197 | MODULE_LICENSE("GPL");\ 198 | static int mod_init(void) {\ 199 | int exit_code = 1;\ 200 | printk(KERN_ERR "*** " #test " loaded...\n");\ 201 | if ((exit_code = test()) == 0) {\ 202 | printk(KERN_ERR "*** all tests passed. [%d test(s) ran]\n", g_kutest_ran_tests);\ 203 | } else {\ 204 | printk(KERN_ERR "fail: [%d test(s) ran]\n", g_kutest_ran_tests);\ 205 | }\ 206 | return exit_code;\ 207 | }\ 208 | static void mod_fini(void) {\ 209 | printk(KERN_ERR "*** " #test " unloaded.\n");\ 210 | }\ 211 | module_init(mod_init);\ 212 | module_exit(mod_fini); 213 | 214 | #elif defined(_WIN32) 215 | 216 | #define KUTE_MAIN(test)\ 217 | VOID DriverUnload(_In_ PDRIVER_OBJECT driver_object) {\ 218 | UNREFERENCED_PARAMETER(driver_object);\ 219 | DbgPrint("***" #test " unloaded.\n");\ 220 | }\ 221 | NTSTATUS DriverEntry(_In_ PDRIVER_OBJECT driver_object, _In_ PUNICODE_STRING reg_path) {\ 222 | UNREFERENCED_PARAMETER(driver_object);\ 223 | UNREFERENCED_PARAMETER(reg_path);\ 224 | UNREFERENCED_PARAMETER(g_kutest_ran_tests);\ 225 | int exit_code;\ 226 | driver_object->DriverUnload = DriverUnload;\ 227 | DbgPrint("*** " #test " loaded...\n");\ 228 | if ((exit_code = test()) == 0) {\ 229 | DbgPrint("*** all tests passed. [%d test(s) ran]\n", g_kutest_ran_tests);\ 230 | } else {\ 231 | DbgPrint("fail: [%d test(s) ran]\n", g_kutest_ran_tests);\ 232 | }\ 233 | return (exit_code == 0) ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL;\ 234 | } 235 | #endif 236 | 237 | #endif 238 | -------------------------------------------------------------------------------- /src/test/.ivk: -------------------------------------------------------------------------------- 1 | --forgefiles=Forgefile.hsl --Forgefile-projects=cutest-test --obj-output-dir=obj --bin-output-dir=bin 2 | -------------------------------------------------------------------------------- /src/test/Forgefile.hsl: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (C) 2015 by Rafael Santiago 3 | # 4 | # This is a free software. You can redistribute it and/or modify under 5 | # the terms of the GNU General Public License version 2. 6 | # 7 | # 8 | include Toolsets.hsl 9 | include ../Forgeutils.hsl 10 | include ~/fsutil.hsl 11 | 12 | local var sources type list; 13 | local var includes type list; 14 | local var cflags type list; 15 | local var libraries type list; 16 | local var ldflags type list; 17 | 18 | local var toolset_name type string; 19 | 20 | cutest-test.preloading() { 21 | $toolset_name = get_toolset_basename() + "app"; 22 | } 23 | 24 | project cutest-test : toolset $toolset_name : $sources, $includes, $cflags, $libraries, $ldflags, "cutest"; 25 | 26 | cutest-test.prologue() { 27 | var option type list; 28 | 29 | $option = hefesto.sys.get_option("build-skip"); 30 | 31 | if ($option.index_of("cutest") > -1) { 32 | hefesto.sys.echo("BUILD INFO: cutest build was skipped.\n"); 33 | hefesto.project.abort(0); 34 | } 35 | 36 | hefesto.sys.echo("*** NOW RUNNING cutest's UNIT TESTS... wait...\n\n"); 37 | 38 | if ($toolset_name != "msvc-c-app") { 39 | $ldflags.add_item("../lib/libcutest.a"); 40 | } else { 41 | $ldflags.add_item("..\\lib\\libcutest" + get_msvc_artifact_postfix() + ".lib"); 42 | # INFO(Rafael): Rather important. 43 | $ldflags.add_item("/NODEFAULTLIB:MSVCRT,MSVCRTD"); 44 | } 45 | 46 | # Let's explicitly configure the needed flags by platform. 47 | 48 | if (hefesto.sys.os_name() == "linux") { 49 | $ldflags.add_item("-ldl"); 50 | $ldflags.add_item("-lpthread"); 51 | } else if (hefesto.sys.os_name() == "freebsd") { 52 | $ldflags.add_item("-lexecinfo"); 53 | $ldflags.add_item("-lpthread"); 54 | } else if (hefesto.sys.os_name() == "minix") { 55 | $ldflags.add_item("-lexecinfo"); 56 | } else if (hefesto.sys.os_name() == "sunos") { 57 | $cflags.add_item("-DNO_CUTEST_BACKTRACING"); 58 | $ldflags.add_item("-lpthread"); 59 | } else if (hefesto.sys.os_name() == "netbsd") { 60 | $ldflags.add_item("-lexecinfo"); 61 | $ldflags.add_item("-lpthread"); 62 | } else if (hefesto.sys.os_name() == "openbsd") { 63 | $ldflags.add_item("-lexecinfo"); 64 | $ldflags.add_item("-lpthread"); 65 | } 66 | 67 | if (hefesto.sys.os_name() != "windows") { 68 | if (isdir("/usr/local/include")) { 69 | $includes.add_item("/usr/local/include"); 70 | } 71 | 72 | if (isdir("/usr/local/lib")) { 73 | $libraries.add_item("/usr/local/lib"); 74 | } 75 | } 76 | 77 | $sources.ls(".*\\.c$"); 78 | } 79 | 80 | cutest-test.epilogue() { 81 | if (hefesto.sys.last_forge_result() == 0) { 82 | var exit_code type int; 83 | if (hefesto.sys.os_name() != "windows") { 84 | $exit_code = hefesto.sys.run("bin/cutest --foo=bar --cutest-run-suite=suite_c,suite_a,suite_d"); 85 | } else { 86 | $exit_code = hefesto.sys.run("bin\\cutest.exe --foo=bar --cutest-run-suite=suite_c,suite_a,suite_d"); 87 | } 88 | if ($exit_code != 0) { 89 | hefesto.sys.echo("\n~~~~ UNIT TEST ERROR.\n\n"); 90 | hefesto.project.abort($exit_code); 91 | } 92 | hefesto.sys.echo("\n*** BUILD SUCCESS.\n\n"); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/test/Toolsets.hsl: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (C) 2016 by Rafael Santiago 3 | # 4 | # This is a free software. You can redistribute it and/or modify under 5 | # the terms of the GNU General Public License version 2. 6 | # 7 | # 8 | include ~/toolsets/gcc/gcc-app.hsl 9 | include ~/toolsets/clang/clang-app.hsl 10 | include on windows ~/toolsets/msvc/msvc.hsl 11 | 12 | -------------------------------------------------------------------------------- /src/test/alien_test_case.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 by Rafael Santiago 3 | * 4 | * This is a free software. You can redistribute it and/or modify under 5 | * the terms of the GNU General Public License version 2. 6 | * 7 | */ 8 | #include "alien_test_case.h" 9 | 10 | CUTE_TEST_CASE(alien_test_case) 11 | CUTE_CHECK("0 == 1", 0 != 1); 12 | CUTE_TEST_CASE_END 13 | 14 | CUTE_FIXTURE_SETUP(alien_fixture_test_case) 15 | CUTE_FIXTURE_END 16 | 17 | CUTE_TEST_CASE(alien_fixture_test_case) 18 | CUTE_CHECK("0 == 1", 0 != 1); 19 | CUTE_TEST_CASE_END 20 | 21 | CUTE_FIXTURE_TEARDOWN(alien_fixture_test_case) 22 | CUTE_FIXTURE_END 23 | -------------------------------------------------------------------------------- /src/test/alien_test_case.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 by Rafael Santiago 3 | * 4 | * This is a free software. You can redistribute it and/or modify under 5 | * the terms of the GNU General Public License version 2. 6 | * 7 | */ 8 | #ifndef ALIEN_TEST_CASE_H 9 | #define ALIEN_TEST_CASE_H 1 10 | 11 | #include "../cutest.h" 12 | 13 | CUTE_DECLARE_TEST_CASE(alien_test_case); 14 | 15 | CUTE_DECLARE_TEST_CASE_WITH_FIXTURE(alien_fixture_test_case); 16 | 17 | #endif 18 | -------------------------------------------------------------------------------- /src/test/main.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 by Rafael Santiago 3 | * 4 | * This is a free software. You can redistribute it and/or modify under 5 | * the terms of the GNU General Public License version 2. 6 | * 7 | */ 8 | #include "../cutest.h" 9 | #include "../cutest_mmap.h" 10 | #include "alien_test_case.h" 11 | #include 12 | 13 | static int g_counter = 0; 14 | 15 | static int g_suite_a = 0; 16 | 17 | // INFO(Santiago): nothing to do here but it breaks the compilation 18 | // if there is something wrong with the macro definition. 19 | CUTE_TEST_CASE(test_decl) 20 | CUTE_TEST_CASE_END 21 | 22 | char *get_test_decl_return() { 23 | CUTE_RUN_TEST(test_decl); 24 | return NULL; 25 | } 26 | 27 | CUTE_TEST_CASE(assertion_tests) 28 | unsigned int foo = 0xdeadbeef; 29 | CUTE_CHECK("foo != 0xdeadbeef", foo == 0xdeadbeef); 30 | CUTE_CHECK_EQ("foo != 0xdeadbeef", foo, 0xdeadbeef); 31 | CUTE_CHECK_NEQ("foo == 0xf00b45", foo, 0xf00b45); 32 | CUTE_ASSERT(foo == 0xdeadbeef); 33 | CUTE_ASSERT_EQ(foo, 0xdeadbeef); 34 | CUTE_ASSERT_NEQ(foo, 0xf00b45); 35 | foo = 10; 36 | CUTE_CHECK_LE("foo > 11", foo, 11); 37 | CUTE_CHECK_GR("foo > 9", foo, 9); 38 | CUTE_CHECK_LEQ("foo > 10", foo, 10); 39 | CUTE_CHECK_GEQ("foo < 10", foo, 10); 40 | CUTE_ASSERT_LE(foo, 11); 41 | CUTE_ASSERT_GR(foo, 9); 42 | CUTE_ASSERT_LEQ(foo, 10); 43 | CUTE_ASSERT_GEQ(foo, 10); 44 | CUTE_TEST_CASE_END 45 | 46 | CUTE_TEST_CASE(get_option_tests) 47 | char *argv[] = { 48 | "--cute-log-path=/usr/boo", 49 | "--passed" 50 | }; 51 | CUTE_CHECK_EQ("cute_get_option() != NULL", NULL, cute_get_option("none", 2, argv, NULL)); 52 | CUTE_CHECK_EQ("cute_get_option() != NULL", NULL, cute_get_option(NULL, 2, argv, NULL)); 53 | CUTE_CHECK_EQ("cute_get_option() != \"/usr/boo\"", 0, strcmp(cute_get_option("cute-log-path", 2, argv, "(null)"), "/usr/boo")); 54 | CUTE_CHECK_EQ("cute_get_option() != \"1\"", 0, strcmp(cute_get_option("passed", 2, argv, "0"), "1")); 55 | CUTE_TEST_CASE_END 56 | 57 | CUTE_FIXTURE_SETUP(fixture_test) 58 | g_counter = 1; 59 | CUTE_FIXTURE_END 60 | 61 | CUTE_FIXTURE_TEARDOWN(fixture_test) 62 | g_counter++; 63 | CUTE_FIXTURE_END 64 | 65 | CUTE_TEST_CASE(fixture_test) 66 | CUTE_CHECK_EQ("g_counter != 1", g_counter, 1); 67 | CUTE_TEST_CASE_END 68 | 69 | CUTE_TEST_CASE(CUTE_GET_OPTION_MACRO_test) 70 | char *value = NULL; 71 | CUTE_CHECK_EQ("CUTE_GET_OPTION(\"superunknown\") != NULL", CUTE_GET_OPTION("superunknown"), NULL); 72 | value = CUTE_GET_OPTION("foo"); 73 | CUTE_CHECK_NEQ("CUTE_GET_OPTION(\"foo\") == NULL", value, NULL); 74 | CUTE_CHECK("CUTE_GET_OPTION(\"foo\") != \"bar\"", strcmp(value, "bar") == 0); 75 | CUTE_TEST_CASE_END 76 | 77 | CUTE_TEST_CASE(cute_mmap_ctx_general_tests) 78 | struct cute_mmap_ctx *mmap = NULL, *tail = NULL; 79 | struct cute_mmap_ctx *mp = NULL; 80 | char byte = 0; 81 | int integer = 0; 82 | float real = 0; 83 | size_t mmap_nr = 0; 84 | size_t m = 0; 85 | struct expected_mmap_state { 86 | size_t size; 87 | void *ptr; 88 | }; 89 | struct expected_mmap_state ems[] = { 90 | { sizeof(byte), &byte }, 91 | { sizeof(integer), &integer }, 92 | { sizeof(real), &real } 93 | }; 94 | mmap = add_allocation_to_cute_mmap_ctx(mmap, 0, NULL, &tail); 95 | CUTE_CHECK("mmap != NULL", mmap == NULL); 96 | mmap = add_allocation_to_cute_mmap_ctx(mmap, sizeof(byte), &byte, &tail); 97 | mmap = add_allocation_to_cute_mmap_ctx(mmap, sizeof(integer), &integer, &tail); 98 | mmap = add_allocation_to_cute_mmap_ctx(mmap, sizeof(real), &real, &tail); 99 | CUTE_CHECK("mmap == NULL", mmap != NULL); 100 | mmap_nr = 0; 101 | for (mp = mmap; mp != NULL; mp = mp->next, mmap_nr++); 102 | CUTE_CHECK("mmap_nr != 3", mmap_nr == 3); 103 | mmap_nr = sizeof(ems) / sizeof(ems[0]); 104 | for (m = 0, mp = mmap; m < mmap_nr; m++, mp = mp->next) { 105 | CUTE_CHECK_EQ("ems[m].size != mp->size", ems[m].size, mp->size); 106 | CUTE_CHECK_EQ("ems[m].ptr != mp->addr", ems[m].ptr, mp->addr); 107 | } 108 | mmap = rm_allocation_from_cute_mmap_ctx(mmap, &byte, &tail); 109 | mmap_nr = 0; 110 | for (mp = mmap; mp != NULL; mp = mp->next, mmap_nr++); 111 | CUTE_CHECK("mmap_nr != 2", mmap_nr == 2); 112 | 113 | mmap = rm_allocation_from_cute_mmap_ctx(mmap, &real, &tail); 114 | mmap_nr = 0; 115 | for (mp = mmap; mp != NULL; mp = mp->next, mmap_nr++); 116 | CUTE_CHECK("mmap_nr != 1", mmap_nr == 1); 117 | 118 | mmap = rm_allocation_from_cute_mmap_ctx(mmap, NULL, &tail); 119 | mmap_nr = 0; 120 | for (mp = mmap; mp != NULL; mp = mp->next, mmap_nr++); 121 | CUTE_CHECK("mmap_nr != 1", mmap_nr == 1); 122 | 123 | mmap = rm_allocation_from_cute_mmap_ctx(mmap, &integer, &tail); 124 | mmap_nr = 0; 125 | for (mp = mmap; mp != NULL; mp = mp->next, mmap_nr++); 126 | CUTE_CHECK("mmap_nr != 1", mmap_nr == 0); 127 | 128 | del_cute_mmap_ctx(mmap); 129 | CUTE_TEST_CASE_END 130 | 131 | CUTE_TEST_CASE(leak_check_tests) 132 | char *my_sloppy_allocation = NULL; 133 | int *my_sloppy_int = NULL; 134 | if (g_cute_leak_check == 0) { 135 | g_cute_leak_check = 1; 136 | my_sloppy_allocation = (char *) malloc(8192); 137 | my_sloppy_int = (int *) malloc(sizeof(int)); 138 | CUTE_CHECK("g_cute_mmap == NULL", g_cute_mmap != NULL); 139 | CUTE_CHECK("g_cute_mmap->addr != my_sloppy_allocation", g_cute_mmap->addr == my_sloppy_allocation); 140 | CUTE_CHECK("g_cute_mmap->size != 8192", g_cute_mmap->size == 8192); 141 | CUTE_CHECK("g_cute_mmap->next == NULL", g_cute_mmap->next != NULL); 142 | CUTE_CHECK("g_cute_mmap->next->addr != my_sloopy_int", g_cute_mmap->next->addr == my_sloppy_int); 143 | CUTE_CHECK("g_cute_mmap->next->size != sizeof(int)", g_cute_mmap->next->size == sizeof(int)); 144 | CUTE_CHECK("g_cute_mmap->next->next != NULL", g_cute_mmap->next->next == NULL); 145 | free(my_sloppy_allocation); 146 | free(my_sloppy_int); 147 | CUTE_CHECK("g_cute_mmap != NULL", g_cute_mmap == NULL); 148 | g_cute_leak_check = 0; 149 | } else { 150 | cute_log("WARNING: Skipped test. Unable to run \"$CASE_NAME\". You need to disable cutest's memory leak check system in order to run this.\n"); 151 | } 152 | CUTE_TEST_CASE_END 153 | 154 | CUTE_TEST_CASE(test_set_suite_a_flag) 155 | g_suite_a = 1; 156 | CUTE_TEST_CASE_END 157 | 158 | CUTE_TEST_CASE_SUITE(suite_a) 159 | CUTE_RUN_TEST(test_set_suite_a_flag); 160 | CUTE_TEST_CASE_SUITE_END 161 | 162 | CUTE_TEST_CASE_SUITE(suite_b) // WARN(Santiago): it should never run on normal conditions otherwise it will break things. 163 | g_suite_a = 0; 164 | CUTE_TEST_CASE_SUITE_END 165 | 166 | CUTE_TEST_CASE(entry) 167 | char *retval = get_test_decl_return(); 168 | CUTE_CHECK_EQ("retval != NULL", retval, NULL); 169 | CUTE_RUN_TEST(assertion_tests); 170 | CUTE_RUN_TEST(get_option_tests); 171 | CUTE_RUN_TEST_WITH_FIXTURE(fixture_test); 172 | CUTE_CHECK_EQ("g_counter != 2", g_counter, 2); 173 | CUTE_RUN_TEST(CUTE_GET_OPTION_MACRO_test); 174 | CUTE_RUN_TEST(cute_mmap_ctx_general_tests); 175 | CUTE_RUN_TEST(leak_check_tests); 176 | CUTE_RUN_TEST(alien_test_case); 177 | CUTE_RUN_TEST_WITH_FIXTURE(alien_fixture_test_case); 178 | CUTE_RUN_TEST_SUITE(suite_a); 179 | CUTE_RUN_TEST_SUITE(suite_b); 180 | CUTE_CHECK_EQ("g_suite_a != 1", g_suite_a, 1); 181 | CUTE_TEST_CASE_END 182 | 183 | CUTE_MAIN(entry) 184 | --------------------------------------------------------------------------------