From f3f59eef001fcf282ed3f4e2159a939ea221f48c Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Mon, 25 Sep 2023 21:50:16 +0000 Subject: [PATCH] Build .asciidoc Files for GitHub Pages from c9343fe59a41c531da9092471b41af55ede37212 --- .gitattributes | 17 + .gitignore | 18 + CMakeLists.txt | 56 + README | 39 + README.html | 458 ++ avtar.png | Bin 0 -> 90220 bytes cgreen-guide-en-docinfo.html | 44 + cgreen-guide-en-docinfo.xml | 47 + cgreen-guide-en.html | 6612 +++++++++++++++++++++++++++++ cgreen_asciidoc.conf | 3 + cheat-sheet.md | 98 + doxy.config.in | 1539 +++++++ index.html | 1 + logo.odt | Bin 0 -> 18840 bytes logo.pdf | Bin 0 -> 22485 bytes logo.png | Bin 0 -> 110746 bytes man/man1/cgreen-debug.1 | 44 + man/man1/cgreen-runner.1 | 89 + man/man5/cgreen.5 | 44 + tutorial_src/.gitignore | 48 + tutorial_src/Makefile | 311 ++ tutorial_src/README | 12 + tutorial_src/all_tests.c | 12 + tutorial_src/crash_tests.c | 19 + tutorial_src/crash_tests1.c | 17 + tutorial_src/crash_tests2.c | 17 + tutorial_src/crash_tests3.c | 23 + tutorial_src/custom_constraint1.c | 37 + tutorial_src/custom_constraint2.c | 39 + tutorial_src/custom_constraint3.c | 64 + tutorial_src/double_tests1.c | 33 + tutorial_src/first_tests0.c | 10 + tutorial_src/first_tests1.c | 20 + tutorial_src/first_tests2.c | 20 + tutorial_src/formatter_tests0.c | 22 + tutorial_src/formatter_tests1.c | 23 + tutorial_src/formatter_tests2.c | 34 + tutorial_src/formatter_tests3.c | 47 + tutorial_src/formatter_tests4.c | 55 + tutorial_src/formatter_tests5.c | 61 + tutorial_src/learning_mocks.c | 24 + tutorial_src/multiple_streams1.c | 17 + tutorial_src/multiple_streams2.c | 22 + tutorial_src/person.h | 11 + tutorial_src/person_tests.c | 58 + tutorial_src/read_paragraph1.c | 19 + tutorial_src/read_paragraph2.c | 19 + tutorial_src/read_paragraph3.c | 20 + tutorial_src/roman_tests.c | 13 + tutorial_src/schema_tests1.c | 54 + tutorial_src/schema_tests2.c | 50 + tutorial_src/set_contents.c | 14 + tutorial_src/set_field.c | 18 + tutorial_src/side_effect.c | 44 + tutorial_src/stream.c | 32 + tutorial_src/stream.h | 2 + tutorial_src/stream1.c | 32 + tutorial_src/stream2.c | 32 + tutorial_src/stream_tests0.c | 16 + tutorial_src/stream_tests1.c | 17 + tutorial_src/stream_tests2.c | 25 + tutorial_src/stream_tests3.c | 33 + tutorial_src/stream_tests4.c | 46 + tutorial_src/strlen_tests1.c | 20 + tutorial_src/strlen_tests2.c | 20 + tutorial_src/strlen_tests3.c | 16 + tutorial_src/strlen_tests4.c | 18 + tutorial_src/strlen_tests5.c | 20 + tutorial_src/strlen_tests6.c | 20 + tutorial_src/strlen_tests7.c | 25 + tutorial_src/struct_parameters.c | 55 + tutorial_src/suite1.c | 14 + tutorial_src/suite_person_tests.c | 46 + tutorial_src/suite_strlen_tests.c | 22 + tutorial_src/test_as_xml0.c | 26 + tutorial_src/test_as_xml1.c | 29 + tutorial_src/words.c | 23 + tutorial_src/words.h | 2 + tutorial_src/words1.c | 5 + tutorial_src/words2.c | 11 + tutorial_src/words3.c | 12 + tutorial_src/words4.c | 12 + tutorial_src/words5.c | 15 + tutorial_src/words6.c | 16 + tutorial_src/words7.c | 24 + tutorial_src/words_tests0.c | 14 + tutorial_src/words_tests1.c | 21 + tutorial_src/words_tests2.c | 30 + tutorial_src/words_tests3.c | 44 + tutorial_src/words_tests4.c | 52 + tutorial_src/xml_reporter.c | 77 + tutorial_src/xml_reporter.h | 8 + tutorial_src/xml_reporter0.c | 8 + tutorial_src/xml_reporter1.c | 35 + tutorial_src/xml_reporter2.c | 46 + tutorial_src/xml_reporter3.c | 53 + tutorial_src/xml_reporter4.c | 73 + 97 files changed, 11563 insertions(+) create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 README create mode 100644 README.html create mode 100644 avtar.png create mode 100644 cgreen-guide-en-docinfo.html create mode 100644 cgreen-guide-en-docinfo.xml create mode 100644 cgreen-guide-en.html create mode 100644 cgreen_asciidoc.conf create mode 100644 cheat-sheet.md create mode 100644 doxy.config.in create mode 120000 index.html create mode 100644 logo.odt create mode 100644 logo.pdf create mode 100644 logo.png create mode 100644 man/man1/cgreen-debug.1 create mode 100644 man/man1/cgreen-runner.1 create mode 100644 man/man5/cgreen.5 create mode 100644 tutorial_src/.gitignore create mode 100644 tutorial_src/Makefile create mode 100644 tutorial_src/README create mode 100644 tutorial_src/all_tests.c create mode 100644 tutorial_src/crash_tests.c create mode 100644 tutorial_src/crash_tests1.c create mode 100644 tutorial_src/crash_tests2.c create mode 100644 tutorial_src/crash_tests3.c create mode 100644 tutorial_src/custom_constraint1.c create mode 100644 tutorial_src/custom_constraint2.c create mode 100644 tutorial_src/custom_constraint3.c create mode 100644 tutorial_src/double_tests1.c create mode 100644 tutorial_src/first_tests0.c create mode 100644 tutorial_src/first_tests1.c create mode 100644 tutorial_src/first_tests2.c create mode 100644 tutorial_src/formatter_tests0.c create mode 100644 tutorial_src/formatter_tests1.c create mode 100644 tutorial_src/formatter_tests2.c create mode 100644 tutorial_src/formatter_tests3.c create mode 100644 tutorial_src/formatter_tests4.c create mode 100644 tutorial_src/formatter_tests5.c create mode 100644 tutorial_src/learning_mocks.c create mode 100644 tutorial_src/multiple_streams1.c create mode 100644 tutorial_src/multiple_streams2.c create mode 100644 tutorial_src/person.h create mode 100644 tutorial_src/person_tests.c create mode 100644 tutorial_src/read_paragraph1.c create mode 100644 tutorial_src/read_paragraph2.c create mode 100644 tutorial_src/read_paragraph3.c create mode 100644 tutorial_src/roman_tests.c create mode 100644 tutorial_src/schema_tests1.c create mode 100644 tutorial_src/schema_tests2.c create mode 100644 tutorial_src/set_contents.c create mode 100644 tutorial_src/set_field.c create mode 100644 tutorial_src/side_effect.c create mode 100644 tutorial_src/stream.c create mode 100644 tutorial_src/stream.h create mode 100644 tutorial_src/stream1.c create mode 100644 tutorial_src/stream2.c create mode 100644 tutorial_src/stream_tests0.c create mode 100644 tutorial_src/stream_tests1.c create mode 100644 tutorial_src/stream_tests2.c create mode 100644 tutorial_src/stream_tests3.c create mode 100644 tutorial_src/stream_tests4.c create mode 100644 tutorial_src/strlen_tests1.c create mode 100644 tutorial_src/strlen_tests2.c create mode 100644 tutorial_src/strlen_tests3.c create mode 100644 tutorial_src/strlen_tests4.c create mode 100644 tutorial_src/strlen_tests5.c create mode 100644 tutorial_src/strlen_tests6.c create mode 100644 tutorial_src/strlen_tests7.c create mode 100644 tutorial_src/struct_parameters.c create mode 100644 tutorial_src/suite1.c create mode 100644 tutorial_src/suite_person_tests.c create mode 100644 tutorial_src/suite_strlen_tests.c create mode 100644 tutorial_src/test_as_xml0.c create mode 100644 tutorial_src/test_as_xml1.c create mode 100644 tutorial_src/words.c create mode 100644 tutorial_src/words.h create mode 100644 tutorial_src/words1.c create mode 100644 tutorial_src/words2.c create mode 100644 tutorial_src/words3.c create mode 100644 tutorial_src/words4.c create mode 100644 tutorial_src/words5.c create mode 100644 tutorial_src/words6.c create mode 100644 tutorial_src/words7.c create mode 100644 tutorial_src/words_tests0.c create mode 100644 tutorial_src/words_tests1.c create mode 100644 tutorial_src/words_tests2.c create mode 100644 tutorial_src/words_tests3.c create mode 100644 tutorial_src/words_tests4.c create mode 100644 tutorial_src/xml_reporter.c create mode 100644 tutorial_src/xml_reporter.h create mode 100644 tutorial_src/xml_reporter0.c create mode 100644 tutorial_src/xml_reporter1.c create mode 100644 tutorial_src/xml_reporter2.c create mode 100644 tutorial_src/xml_reporter3.c create mode 100644 tutorial_src/xml_reporter4.c diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..bdb0cabc --- /dev/null +++ b/.gitattributes @@ -0,0 +1,17 @@ +# Auto detect text files and perform LF normalization +* text=auto + +# Custom for Visual Studio +*.cs diff=csharp + +# Standard to msysgit +*.doc diff=astextplain +*.DOC diff=astextplain +*.docx diff=astextplain +*.DOCX diff=astextplain +*.dot diff=astextplain +*.DOT diff=astextplain +*.pdf diff=astextplain +*.PDF diff=astextplain +*.rtf diff=astextplain +*.RTF diff=astextplain diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..dfea21f4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,18 @@ +build +.idea +CMakeCache.txt +CMakeFiles +Testing +*.exe +*.stackdump +*.so +build-stamp +configure-stamp +*~ +#*# +.#* +bin +lib +valgrind.log +gitrevision.h +.cgreen-debug-commands \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 00000000..b5837df8 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,56 @@ +FIND_PACKAGE(Asciidoctor) + +SET(ASCIIDOC_CONFFILE "${PROJECT_SOURCE_DIR}/doc/cgreen_asciidoc.conf") + +OPTION(CGREEN_WITH_HTML_DOCS "with HTML output" FALSE) +OPTION(CGREEN_WITH_PDF_DOCS "with PDF output" FALSE) + +IF (CGREEN_WITH_HTML_DOCS AND NOT ASCIIDOCTOR_FOUND) + MESSAGE(FATAL_ERROR "Can't produce HTML without 'asciidoctor'") +ENDIF (CGREEN_WITH_HTML_DOCS AND NOT ASCIIDOCTOR_FOUND) + +IF (CGREEN_WITH_PDF_DOCS AND NOT ASCIIDOCTORPDF_FOUND) + MESSAGE(FATAL_ERROR "Can't produce PDF without 'asciidoctor-pdf'") +ENDIF (CGREEN_WITH_PDF_DOCS AND NOT ASCIIDOCTORPDF_FOUND) + +IF(CGREEN_WITH_HTML_DOCS OR CGREEN_WITH_PDF_DOCS) + FILE(GLOB _docfiles *.asciidoc) + FOREACH(_file ${_docfiles}) + GET_FILENAME_COMPONENT(_file_we ${_file} NAME_WE) + SET(_in "${_file_we}") + IF (CGREEN_WITH_HTML_DOCS) + SET(_out "${_file_we}.html") + ADD_CUSTOM_COMMAND( + OUTPUT "${_out}-html" + COMMAND ${ASCIIDOCTOR_EXECUTABLE} + -a VERSION=${APPLICATION_VERSION} + -o ${_out} ${_file} + DEPENDS ${_file} + COMMENT "asciidoctor ${_in}" + ) + ADD_CUSTOM_TARGET(${_in}-html ALL echo + DEPENDS "${_out}-html" + ) + ENDIF (CGREEN_WITH_HTML_DOCS) + IF (CGREEN_WITH_PDF_DOCS) + SET(_out "${_file_we}.pdf") + ADD_CUSTOM_COMMAND( + OUTPUT "${_out}-pdf" + COMMAND ${ASCIIDOCTORPDF_EXECUTABLE} + -a toc + -a VERSION=${APPLICATION_VERSION} + -a docinfo -o ${_out} ${_file} + DEPENDS ${_file} + COMMENT "asciidoctor-pdf ${_in}" + ) + ADD_CUSTOM_TARGET(${_in}-pdf ALL echo + DEPENDS "${_out}-pdf" + ) + ENDIF (CGREEN_WITH_PDF_DOCS) + ENDFOREACH(_file) +ENDIF(CGREEN_WITH_HTML_DOCS OR CGREEN_WITH_PDF_DOCS) + +IF(UNIX) + set(MANPAGES man) + INSTALL(DIRECTORY ${MANPAGES} DESTINATION share) +ENDIF(UNIX) diff --git a/README b/README new file mode 100644 index 00000000..ca8f157d --- /dev/null +++ b/README @@ -0,0 +1,39 @@ +HOW-TO: Compiling the Cgreen Guide +=================================== + +The Cgreen Guide Book is written using Asciidoctor. Asciidoctor builds +upon the asciidoc format, is 100 times faster than the Python asciidoc +toolchain and can generate PDF in the same step. Writing in +asciidoc(tor) is a wonderful way to write textual docs. Please visit +http://www.asciidoctor.org for more information. + +You also need a source-highlighter as described in +(http://asciidoctor.org/docs/asciidoc-syntax-quick-reference/#source-code) + +There are a number of source examples in the documentation that are +automatically included from the `tutorial_src` subdirectory. They are +kept separate so that they can be compiled to ensure that they +actually are correct. Then all, or a part of, the file can be +included. To do that, go to the subdirectory, ensure you have the +correct version of cgreen in your path and do `make`. + +The one drawback of this strategy is that you cannot read the source +of the examples in a raw asciidoc-conversion, like on github, since it +requires including other files, which is not allowed for security +reasons. So we need to generate the documentation and upload it to +`cgreen-devs.github.io`. Ensure you have *asciidoctor* installed +(`sudo apt install asciidoctor` or similar). + +It is preferred to generate multi-page HTML, which currently +asciidoctor does not do natively yet. There is an extension in the +asciidoctor-extension-lab, which I have not +tried. [asciidoctor-chunker](https://github.com/wshito/asciidoctor-chunker) +works well, but requires installing Roswell (a Common Lisp +environment). If you use asciidoctor-chunker, first generate the HTML with asciidoctor + + asciidoctor cgreen-guide-en.asciidoc -o html/cgreen-guide-en.html + +then chunk it using asciidoctor-chunker (assuming some convenience +links in your path...) + + asciidoctor-chunker html/cgreen-guide-en.html diff --git a/README.html b/README.html new file mode 100644 index 00000000..cff3febd --- /dev/null +++ b/README.html @@ -0,0 +1,458 @@ + + + + + + + +Cgreen documentation + + + + + +
+
+ +
+
+ + + \ No newline at end of file diff --git a/avtar.png b/avtar.png new file mode 100644 index 0000000000000000000000000000000000000000..ba4081c8041f5dfe517e458c6f2e77e63addc0c1 GIT binary patch literal 90220 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∈Y zc<%3@JM7YWfjym6e5WAP;KgzIdAJWtx-=8f-h}LOC#CZd7hr&*E2hf8D2c=E6wAhC z6KDS$O+JV^vr@;EbxHHzG>y7iH0GOqbTQkQdU`P;9n^i(gSD3ep4p`;;Fs@DVqvhk zFp>0c2;ni7NwZ$8#S+k-jQF)CeI2@p4L8GY=d4UM1N&n}3|N39Xfn$#K z-{=CEa1BRwM}|R2A&jbI1gY}VthSwx@j9D6-kAjGd0ML~s;=CR+7Dbm2R+0caj^oY zGVb@r9w^N$R7hy|DN=r9&1E!IaAXouJ!!cIzu;7Z^cgHX`)bPgYBJR}hJ>S$%mC5G z8Gb5m)CTAZ#U~rY8NJ66p!yo@`QtIgM0w5T^4)EvuI}2+Y*StZ(m^MPU|e6G=Imkl z!Z(Ng5=piZR-}vtxs~rX(+9E{h4bht)3VsNGf$S}h!a3T% zAU&t;=6#sjRYB})>-16}QA^IIJV?czzBOmjpO@}#``v#z&FZ@=CYtG&fc0)>11@oL z`a%um}>+HaF<$a+KBvCQ%G>7^SEx7!75KK#_W(I z@lz#PRypy>LxOxQLVwtU8>YN2%8f*ToQE!RFU?)27S`&hi5_U`ASFL#rM*_>x)D?( zc3diVkP&)x875ZIWzJoRI(E@ad7xC-J-WIoT+@T(GIN=Xy>OO8? zQqh|Q!ba&hU{cph#%%Rz!dr_7VnksV|KqVyPbZOUWU3Wi zL{XaK$hd9aTOR`kJBPRKcY#;#OaBKGLj0wy)kraTWxXnuHYu0&xpcJbSSne zyO$^})WOU2>5>+zavkP4tz^%SBzicGnAT@|TuyP;gs~P2!PAE?iaMRsrQlk8zCy*( zUDozuT94ObS^pPrBCa#lVp(x^!T^eT|DP18 zO+fSpg>Q(_00JUnBoES|{rU_fY+*02n8hAH-2ho-`oyc};P#n4KFltCu!HgLQ|e&i zSf~;48Wnx^2g+rV1@Ask({z9c+)tefUX)6HP*-F1P0hzQVS*D1Ni$-%bkgroYv<|U;hYKyAztTX-_DCmJ1h$4xmr@<4Y zV?p{XLPQp}FSU2jV2Ew7t?{+8Vzhg7#MRhfdb^@e8QJ9iH*=@~yyAKF-h9J&8(7-T z^v_XNsEYJev764H#K?!y81L2b?@1Ckq^TkvQ&X2`74XnE$XW{}ob1zMqlz)p00tea^)ODe0P$q9?OXa{jNtL-t}A zRRbGLnnsT)`zMU#{}s=_HCo>he`@6*V$ZW4f&FpU0t<829}$JN_TEK2_v(JDZAQH* zd$%k0AEm~A)!@W}x7~{=#adagF15qKhrg4<0}|&1uxIy1L&EkcudK&@%m`ppjoToxaGrNL zw7)h8RLje3@NUy~eSHjvm5@@&WjoC7kje@{s4KbWocT|w48TlDX;CTr0`R9cLmQgQ# z*)5uE3;$V8ZENJ=WH)W6LBbwRKL;wqD?2+c8BxQ$DZTAH%hzu|+2aO8xJt8s4^LK_ zOf~rP8%1T-ixCCUBpsoF4ZZUcug!NaIUa@*k^|T8f&QU20@$lZ{Piz3=4EcH7`Na1 zA5Qy^ysy2Xs|zCFXQ)=8UX&S*qDaE_y}-ktmhndWyOoc1B3I77FR-cF+0KZCa)Z?V zJ#3BGy%v1ekub)ndMZ-b%kHW7{raG93QDq-D5rKiEkrI&&S*q>Wc|BxyHfD)AzSVf zOc(<(EX5zbE8rs$?WYmgS!GFXwyKy|k($oq|eI(LZHi9@q)No5f11 z5fyAl^GZ^pWEA0q9^vW98dbI7ChIjHPiCXbax~-~4;YYxA0`+{_GH-3oo^xU@ph=lTovD`7j4YaNO7`@B;sHoYQMJ-=N>LH5vzLHG28o$>oM=&#~W3gtU!d`{K;M`6;a;`C$v7Nc# z$f^>%fVe8Jo$1$sd_?0Fhp0vb+q+?BK%=CQChE$%90gm^rxsB~GT$A z1>mlzujdq+l;cl0X{Y>G*;iN{1BAKXIp@YxTc7c&dRR4{p-PU5=ng~3CcDkF6M9|H z9x%)<3iLSlqIb)qfX8HBFvBZ}i|MMmz{`gS?eH?VG5)r{PhGV6Wh?nXrJ4HyA+s88(zOF~T+Ave zHA{BreNmPH7ba&nd6fkPh1nNB#~bNV%cw{?wUPG0;7SW)G148+t@u})m+G`=yQ8W$ z=U};;WQx^3tSm4ey+>xmdcE+jaMVWbxhe*o($oH(Y(F9hwOWn{pO3n{=kT)4ATYoJ z4)SbGJo&>L_~Q6nW6#I9n+*(%nE2kBrxwd%Q#izv&NlR!trrv_+c-=vNg=n{qu4w^ z{{mI^8&`W|2N!LrAyx8MzU?5MfPU|a>g`pwJrC8wKr|{(b=K1L-wI`FnJLdFO+H&T zC45(7Fj}|u7OyrPkcO)EsY`zECXs^(T%M&-06LY5Rolo?I16R7yJFjSn|&D{Y#4z3 zrRM+0H&i2kp@K7JP*MmfPQ#Ivk>S`Bqxt;O*2ge`vprlkEk11Oeospv1P2BG)8k`H z#CwbcsX?i#(nG3P_GzchEHA^sT*0rk=c*3WO2}kf0CYX~E}QTRrZXV{g_D10fKQjzbtfJ01*~K}eXUERP7C zJE5yF115r5QA8*2ZJ6kRby8iG^J#H<_5+xBMn^x~o1}_?oz@OZ76d~d9UyY+X}Cd7 z@?YuhL>*p1HaIoEBCJU>f^>~05>=FqLvwRa-JL;Qrri#|XgG9!FYi$avyOqiZfz$v zhKI(O$Gm)QMQJm0C!BX!qV!;;)E5k(yRzHsvG-11#iIr2TF?A;`brrPC8sXJjWukPB7p7T+ZJOttWS<-RpWAjEBnb=D(!Oz9xj3$JQi)fR79u+2lzs%9#vmZ z-+!ue3hwo;XC#wcHNpGF?-fY+}l1O?whw1S24JX+31Zc^4mD zVKSV}N8-t?t|KZWkZ)I7o{P5!`);dz{dvEq&8 zq%`oEax-_7$(U4#>Vz!T5Xy)vIlByO zn(P4}TJhjU)Af{C@=-)@%VTJzD%k2r_BfJ^PdiQ+mjMt@PrJMfuejZuO@s;T+tHk< zKp(dwzi*Elmb2X|C(PZ|RI6N!Q8(3_OZy;9l@UXauLU+ z1**kgas?|u>m*hzYrUx7XT>B^6FF#M67m)(cEI$$~QLW8k=jXL`| zfmhRy%%;6Qy$fM5HQ$!AI@mdBhq(6>R)9fve3lmVPHPS)se3U|j#l@mleN?*|K#WN zMWf@e7C>nt-MBi8Stz{bl3WH}-Z5De&u0;Iz9=8=aOt7gi{qo){o`YasWa^lfncA( z^gg&26Lv#zJaP=m9G$PuPhFWuM+(ts`{;i{rrzVWp=6j z2{TIYwno7TBC)(0VDFSrcD zw90GXUh^P9+;4=x%nA%{<8T(DM)lncjD{=>RU93Ts3ZK*qN80Uei{kbUP z=0jQ(N9KVEb=crVSNy%MgdSNt<&CH*ZI}+B(L+Pw zFoWz8b;fKLzsJJZOJyuM?p&cD89M@x10`=i0p{iDQ0z+|6zyg=0UlPW$3Mr;gWs5M zlpHee<3Cv&U0aWgFnxY6AZ>N+@*6<<*UO1AgAZ~N?c)a3c99DdRMDtSF27g%PqTBE z4O9lpT&}BfdG>Eq1>C3(=b*&^YSIr*_q4OOYIuxnAEWYMQP$m`1x=W?A=tY=1CuCt zDbxmFvV;U5O@GeO)JrO-Vkuzw&ZMFio9K7TX2LTM2A6;vF_0Jc0q-+zFzRP1#3{{4 z-%-%EU?Kq_3-6V#BXFjhw_#t*y9v?uBh4|l>85HS37+lBM{ez z#n`r0bh_g2x06Ns+wOzmW7ufl|2D8NJ6$ejR3kW+GzBmxBN_4ErA+)L1@)4$#y=5K zLSPPD54;J%XmnqkPXl?FeI*9171A@R&oCHgJaM`l&n~__gLGQK5xBUve<=u9E*RNu z2$anZGcoH~rtaMNvE@Nkw=bWWl-D7|0dQ1Wt9Rta{O8P7nZIiQ(rSkNa&+764x`-gWObPD2}7^m%UQ@ z%vJYzAOckc_ZuJuv+{>X+za#KMz;SJq7w55{fMeZyv9gHsjN{ETf?bHHNi2ffY@aT ziNIRlMEFVB+2haSPRyUA5$TEd3=wH3v#y)lLr=r_M>lwe^m2?+R$F4wUma(n<;%CR z^NCyY(q?1DzBd9a3$7CwkY65&<%k(A6w3-U=hn3UipDHn*IPZOOmE3|T-6QXvokj< zGs%(1;LYV0!bqEMK&~A!b4ilmdh+V)GJUXh1D@3}RLHa57fQi$82jm3=g`P$%gj>m zV(ZeGsV6Y@>B{plP{C2yB*U{D9&&`IU?w-hIFYdvq(iLFXCQA;l!?SwlV{_Ah`vk; z1rAX^cVCj+dL3N=HjBpkD6g0X0?*0eT{sHLGuNC2fiCK`I5LQG5M_Em5H=L~RY^45 zbTgR+aWGoRpPAx!g`J#%=-xR$xp0gz9jdm};8Orm+vB#3+P&`%?a6i?8{e> zg!9W@QFWXb3IB2B1;bM$sFN!i@}+Y~nvN3k-?#XE&~`%##J)r%mEAJB;w>Va9uxWY^|$wVP;VuF zy~R20Jz(Kk`e}oDhKFy&%9uIqfvXLZ!B_dS)cx>;>1(gFS%)C(2F&ly-0qlX8wcy} zA>c5<@9fQo44BUq1D_qjn0A@-y8MGK!f|XQp7LV71l2NFiBH~fbRQi{ZQ=PKi&QkvXo1#;JH~iR;AOUHpDlfS`m{*j zSQDwtHC4BC8TE8*To$#(*f=8KI>3vf?JTSE>Wm&e$&lvdTK==;WgfO!b3_(FP{bUS zRG%idpVOI9js2|iE}cI>YVK&ZS%z}MJ5TTI!UIgr+;H*TVd6lGP$|B=h0Cb&_dDQ zms}L1^g-qdrM1UJXnQyokx0Ldw`;p!0s&#T=vNdG{G)3tba`a5mh=}g?IYMZ zCd%kE`wE_s!!!>&Ey$OXwD~r{b5Z7JDg;!&W=iC#zoJGfVKBSs3N0nX4e@}Cu*o*;{H)tteQ4Hd1bHEm~|_AH5*(@{jOsB;$jCd4Q>TAq_iNk{YGo_TGP z)p_aGneQe2uO*8Y3X=P4owksbG-mAf`m3u?|LO8J^yH3ZQ)1;B?@~4i-m#t$zodh?}4qsM&6&k$>{`=h@9r{P8M>}62(FXrr zNrcp?AZMZ?#k3Dvo0LKa`?=j4mew{^-#x)|aG(Eu#l)B)RTuoI*jkE|m2H<+b?2JD zQ?ff1MN=us1rC$c{wW(RA)45la5;N5h43FUo$?O)4NKDz6FA&D7&qk^?Zwn!cLfmF z{K~zWc!d#F=0t6`Xz`>jP@7GY8>~>ug}ghCFn4YX#+tm}UhR~W@Z%$v?yve?EMO?H zW@`AxQN(ff@*Uz@$0HxxOx{B!j8-q4e|*%}Ty~DYu59c4vVynw5xXv9(^eAI3r<<* z)Mm{v%w<>H_f%;P%+hm;D9=U(gygsgmNWJaiFyMJ>##W?N)>mw_WGEq&1*SlsR6$i z9N=B4ptk1PdM4)iXx(7aM(&M!`W5jX&9!9f%9z=`Yemj4PsmonXxok`s`|L!G@rL; zc}X9C^*vX=usgJfvls0rJe67KORW)9bctLIiDOEAS+eq6pZ^;LY{ybJH*u%vvF)>& zF|&f#SQ}on03V4ErYU527O)+WW_rZ-fIGQFE_FUL)+2nm>kxXDh#!M>G6|z#pl%XB zY}6yDzQ={P-{*Y2*pH7lr@nTPHV@@L75h{#GtZlnV+u!p_ReQuAFAs`P*DC0VXpLa zdom($DhjPR5YfVltu+qmskL_xvEj8QRc$T)|5&KljQyf-}`k-jGt}80%#z5@7P2_79@`FVf zG<_fDR1_NTEC*3J*V~Jqzyo;JqA7b>b5*0aPW^0&l?)A=&R=AYnL!mnng1r{Hfq4V z@G!kU3-YuUlx7{)(;~aN9n=gjoprg!ERuN0$aJny&Y*6p31& z<(V1DbNIFd0B#7`yGZuU#YrE-)ASg<`8D?XF5;yCISygZOZNRVfvQF*)8p^KHxVI; zorK5G=;Zpj!Sa8^U;R0!%~gxkbbX7t5Nq=>;kgoeDP>c6muzINg@8RW#euZo_~ojk zVQA8~xflh|(q#s=l)dYNhoF7K z&Jina%Dt^GoM-E!-{!kP`jnj_jc%T*KayO9RaX68^sXLxu~ZNp z14<=X{Cw!39{GgQVW26=&{9c@zejdBfm>%mVl)(bVyHVon~g7!ce5}7n+WdWx1gwI zb=Xn~BP4@lxgyKbF^o(4uzy(rG;QKMw5A9L>G45C5~}apwB>WoH9w8r^I2q<`8&bF zC};Opo*22_OMd651E-LXBbOn*ReOY$3{sPMb8e<8WqNLau%I^p}<_F3Tl_szbMwn1jQ2HPTH3q*&IW5N`l>6dKz86`w#k zmGfw%alNA&di5MJy#SSO)G|_Y>g=t&g_lq+6ok?MiYm-m*VunNZTo$n-*PSbX~ruW z6)8XG)@;a?i>~va1&L`X9@1eCgI*GvWzQ-l|Nl&L$Uy;c%xYf1pf6fVlY7t0_YZk` z_i1m6n#x@D*mJ1`o0fLC3`-5?!E!~lgh4go9j)f$%qs~p0M(Dp8b!Qt%v6|BU6QEo z(^Xt6pNxX=I!WTj(_zza2V-Qh+SngOZ;ej%-^+J3$qAj;$+6G2?&1W#K@fc|hojk9 znVC-d{9j0@zTG|>IThKM0OM4tx@K4Kt?_(Xsj|!!P0!f*>XV)FacccJ5{>FM-H8W# zK0v7Jg;nptnwx26N9=FRu3c(Y|FO+uRUguE@n&T$-)JEYe{DqY(1HVoOY@9gLI#r) z1oN!eU!W0CE$>1Ho);afFnXc|I&|k*Uv5{1&n?p1sIl{`>w2k9h%xw1NSE#k8zm|* z-DK#2y6(77M$oPfVlwij{=m**EYu(*I`*4zR}H?w0Bi~n#$TU`^Z+4Go#-_*0zyaC z$Of%kxh1b8IH(#gS|dJf%btzqls$>Rx1Vt-;v%@@8+-DE|5~&bGE4)-RvbDwP~__kNHV{flz5aJN!*?FuRkIgp}kYH^*l&C-bPLly0)WDt4aiP*@YT214`BnLWg!PfehJp#YOFb62^r^C1S@?=MI?)3d%Mc zLQO3gg~UGWMGu4>{jH9^+#ZAg*XU@{r~Y|0vxqtGpy%l-3D8O0aRX%tj~Xxnr-soX z^U|*-a(r~#B&Z^Ep8dnmG_PHBk1TB^pFCA*J?tYq`Q9SbC39X(gv*(+*EAALV8B9#5hCR(k$=WfZQq zKH^+idS3oIm|Clo)6QkYdI-5hr=-X0vaI`GHnY@0s_x6>o8nqu-#Os!dkX5mbc1C? zFy7d&L2>?WQ+iE2v*kqPVP3QptRZ@iJ{F}~cB*Jx=cXJ~G<7UU*mV{9rNfTDNc({f zNw#>lc0R5__zt1vSypLjTFnm2Y&6E-WRRE_g*o3XjXq~6 zN#e914u@yk?Q{OSIssB&Kj*jmtE%HZ`l~vhOY2xHC8sd!vXEgjXf5l|IrCp63E_T9SYwz9%Df?`smi-?zebpCp{W*dkHHSn(ra z^j?+kF-N6$T4Bae3@_jUb$s!2<4#t^hn(q9MVrlhc8rPiS#NQ=q7{0EtGdy?=e4cn zuo+Yo_TsVzs2=ocX2RkA_=y2r&SsTOrA$of?^YB~-BIMImRwzH3`q3_|GrQrMBYgx zd95Hv**hntFZ$h1pl$2HV}Hu%tl|xMt8IFv&Zm0ZcQxB5+2@L|Z`S0B+5LR5RV_TJ zmH+UFOD`*{s~srv1|C{Vw6G^BOk%JXH5~;jzK&qEQ9h}y(rQulP}ajlUh7R3(@C$d zhVU7?iuZ4ZpAjj{CoLBL=pfq4DA5DSw}yF!`}JXKw5i@bvdQTy(6h1Bfa4HdY1hep zo?AzV(MRcIuWSh&KlMJXC^Jelf!J1aNmn+r)c&)Aj3MZ!9$CgT<^IWE*15i$HxV^J z=#WPJj|h&vFtM|rzUneLLgar=F^;AuTh_CYp!_y zLRx*z>0VNVt)ILNBUPrgi7Fbtjca#AJ*IkuKDItB*cXlO#)-{4V1YD{({M&i48vb0 z@1k#oIoy9uKC{iqi=j~;Ve!`Lt;JNyE1!vd@X%zrFsE2$fs9uQWMD2= z`TDhz^E34ZQEsT|)0g*r70%X0Lvnr8&X;iPD!`hs?wG(LIEbvR^BZ7i@507|7<)kM z-e)GN_x|VFnfVIA3&d(E;992Nfo481_HR9cFQv%*BuZ}eB0=_uWXEzgvz1QI8V!ph z1opW5J8i(26G6^v1y6{q%ob<2K}cBzCT2QybA7xB7GEjn)W2x^-I3JX)U(N=hzJg) zPwmm|Bf^c(co80lBMuZ-{0zRMT(47sNd25)bQ+BQ9dKOHJ@_cgsVIpB^2})BJ8*RP zVuepC%by#YMv2EpisR+%c(ju1F(jtCP~^1?h&r5brXyloQBca|!UeTYw&aa|GMKCZ zT%$neIhUDNI>G5Uk%XMjqP*QsOH8H~OVpl)-aYCWLsrPPs>|a0rm_td`}CWx?blp7 z%qY8Nw!No~X6=P&GqFuBOol;Z`k1x`7nFks+--$%4ju`?Q;{n6h(qm?^mx0__s=~^ z!4|@q-w;nN5t1`;_HR%A%}hE!_8N%z0;EknV<}2)-agF;kEUB5GGQjLJWy6Pb8CYn zQegWOGRH(m)f+3Pzg||yHl)k+65GX(_1#+32&CSLtsebT=mwlv?R}}hZH05)Kx2k& z0WlO-`T|zv%?Nyc2J4YbQTxv1WWpYpOSz7JI1vkflaK~yi~1e53V@O(wG80 zu@w7yWK*Z-B7|LoSv4h-hqJ&lmWt!ED7xRf$c6K~?a_BKFEf4ZvfZW)coUDscQl_p zgUH)Bb83@j(b%NZIWBO_c@}@ZG}64%lf6(5D`jQUp}4{JL5de0==%f5NH`W3ML)m% zuv@j9Vos>Fc5sbcscL#!tR&n{D56QAqF}Cl?#%wYI_smhx9NN02Dr|7j`Z?2m_A(G z!3_u*eD8<0bZA&$s|U{yu^B*$QW#Clvdf`h;ivkH70;L$ zqpl0t5ZM)jy+_o6^VBfI0u!)6^S!fHnlC9WjZ4%3Z3p%!&#O(x%rxgU54#;pF2PF32-sfR za;}bVsUbw%#5yq9=s9yx!p2rpqwzx>0rZ#3O2@Oe&1$XQ|?R7n4Y+ z(l%IFw`qDxpiand#P9Z-Of5DTB(pb5_N^-76$qlVP4@?|QsfdQB`Kf;$quX?wXRD{ z`0bQ-4@p=95(0vw6IJR?gvTObH8rH6!6!3sDI-bojf)Z7J2IHw8u6Kx<$Ci6e<=XB zg?g_EN4tFT%-V#u_y`9u!a`eU7L6O)1*nZ3gu@vKnoU)r=;AOwqj0?>{}DUm2gHKj z0*r$AXW#222lxmN-Yjw7U99m`& z5adxNN8-tv7A1sRz#IqN`n;2+G%t%d&NjY5c+aGj$Bs5W`RlkK$ipuLgUAZFfY9(2 z;dg>!zM@PRC8TH3j=$-JhL-uW&aM2}%bN06VA~YjKVd9$6j3!PXP{c`94jbfca6-X zimmg~#8d4Tij$MlYmM2FF_5Lr?Q`vi<9ZPD@OA5u6pe$A-j(1|b_a zwGv{zAO=ir8@()=uxuW2LvXV z?o!;qlvyNdK%~@Jy@n{U;SilE^vDKtqVW$D(MfRUnTzhKLzMzw*nchY@!p62b{}mdM@jrttM5dvUxQ4nrpHS>S(;T zkFFTRn$l*UQHsK!pEEHZ3-KO0BD|bsTwgoZ{3lOH*MK0fpeqJ2`&0*S?%z4S_D?WP z>z5{G*mzDM@y`b8C|Wep=G&GWbvFvXDP0<6jcaEYVf8TE%3hA@8UOf_=Q)}0HYH`uDKTuSB#1#k&@$#RPw zDwq8#p{V@ zvQL|`C^Q<@iL-o+^tZt7&JIHCkD(#tqiV%xp?3Ii1H4;@jit_+Pej1K(jQaC7i<~B zPBUli_-4;X&?w)I-@wl&syqzRjKX zrq<<6V{kA(Ea{-LQ00(NCfnQ0m!pIUKSqn}+SxB!cWE=at`!eI;GxR{nsXi4a!}3^ z4uE`=B*}D*8Vt3e=aSfljvNq%YC*0B4NJeJ@dZE?cyG%d5k$Sj{`B1J!4bnBZzimg zT}`jDCR%yhY{bM=mfvhlt4fZ2Hdkr8$InC)XBzR}TJ2NY20^t%vd*HZ)Xd_C=6MT_ z$<>6?@mQ?|?vg${b9n`g=RRk@-QGI3ZEugc9}@sN!H`Q(%IidTkarX`5Htj8DC1H} zHzNUgHEE*fB@WUfkKn1J{<3M(V=_iMtdL@Q&z;tCS>!VRg=QM0Rri;Q%!*~z*PR1o^Y~ImsSD` z0PUobO77q$&vQ8rb!cj()yS&bwamTokN~XcpVY4U=fbXc1i;+X$042HVQx=htCdXh zGUHzYmrjUGu0#e8;(cH?6O>I*bYCGcf~c8=m2@^GJsDbhDa?pAD23{QiNgGN=j_5%kCPwhMCCz3(l&{Op8#1UDB-8}>csVg6Ie0tqUxjT?#q)nRtpIHP zIR&~|9BRk7SWCS!Mxvlbw`GhYxU1*h{K+x49Y0fX4qwjvDq&HXcgI_&1G$psAb!Pni}_*gh*@icl&%$(kHKhPtIL|i%}Lb zwHbhC^{ccq0j{Tfuc@BzYNB!se*)ju`35XI42ay1b&i`h4e9W#NgNmLC+XZlQiWdCoz9p&jX)J z7LikKu(63volx{#e{;hGj4k^>;?$WZjLs9t1~}vnCx#@3P_v+O+>Ozlt7pf@;&jgmrb-obLPMg$3`AO2fN!d9JH}s)3rIR=vHQ>+N7ud z{x0WD)h|aHsgqSh`RGl@k%|c6?&=xgg#X1Bn6nk)ATKUv|HVeGDMvM93S$9^ zpmp@~jcMqrl7}@4^Bct7AcR7$DI3j*r`?sek4O#Tuf2$H_RX41=O)1q{3Y+2yrFCIWLsOe8W)0jbinZ)%gg8W0AZ;n4 z*I$<}MI445J5=SjoGyb6}A@>E>c4HH1-i(NhYzjV~4L4qIzZ2Q$y7&J6mHW0> zN|Fmcq^E34&yhLkgWBm+f`cLdiis|-I8-xejUAA+;$y?G=?GbtG~ukUEHbkdC$M}H z|5*ChExkh9JOSdEO0MZiqhH!C4D(A#8%UxV#XeqS?4Sce0LPj(HJJ_@Z_~fq2iKs$ z#7^K`o*I)Zmy5=p_r)I}LcsyiC3++~M~aYaB&ne>j!OK>%xPmGW57uLCs_-=(R&Yu z0S}Y`<+zw@5s=rYIk|H=-s;k{qvw!lwvr)U$S>kc|vZUKtv)^b}eBATZd^0n2Nl%A> z46d&km}!@>>gAnYe_qe3yOfAY_FtZno**3`*lH@zOD$_rRWxOg&|E*lXJ#~|&A3BL z!tf0VR%ZZYbo*7L(cne|s!^hlHZ8c>kR|gjYu}c7SdWBj*hj>Y_o>WZT_vxlu0;Ge z&%b4CA?^w1$iZqjSaW1^dqPWyl?cufkc+o;GV&e9&Y@vrk(^iLn@1t@bs15yIfT<#Qg)yP3-MOC%p zQpaB)HkOz0$Z^kmMWZ3FHI|f(Cm=i&n2m}ZyqRz~ST$_VBDRadnpz*rq4`G@wzPh5 zZ)JgeQN^IeVi8pYGA;bXw3Qz0=dH24S0Q$->n;26t)@Fu2{|4lvpk5dAdvFfsCFAt zuNv}1d;LTu?EI??*iu)Xqs4xnF5wT&?)&?igT5dVOVVm`HOv-Qo5}YH?{lZu?S}Vc zL)@$ih%#bC5Zwh84)_H40o%zL4rTs~(A&E}wIxUqb0i5wQc_S0F4 z0VMO;NZYZoA}?4pW*ZL41z24bCO9_h?uk^p3EOi6WgFen?>=UF0Pb4PEkdEXk^?Z8 zY$o~SQp2Z#FRZfxkY3YRg?qARda>%Vr93gN$le52YgFszN}m5rdU*?@kfe^%`U;T- z&E45MOVdN_JRDg{n1=5kW50L|t3;An3b{2j_@yQL1;x|kphbg1fx)CAUn56+?jQTwY0UkrXtY(Ksf2y8hw4oX(M} zm##7+jDJY}sT#NB!pH2K_4w{|5=(2Oh*Ud)z!*Ru1QCu!sh*_J z#>61I2vf^fdlO@X;>ABmVeZC;K(zZY$Vi_9B|)tCF5WZN#8-5rzeRbzO|lZNn9}kd z=A?V`Zx)9q{8pw}>}IZIn>u!+J?CuJ_#?>`vx`Zg-^T}&TJ)mmi^7R?=AQF)Z-br8 z2)vlPmB{w_VfM>>8=l-LEa^AxG<;jQBWLk?ZRex3!H&i6g5l3j8)ROeUYAGc>AgK9 zUG8G@(tEf1KhDb?KsY2gi2Q?=!s3b$c@CyH@lBsTnQ>8XgDjpr>!E4=xq>n4C4e?mCpFP*;usq|z;uAKggF_N$bBQOAE*qQh0HOelJlVP|;q0HwcHZU;2i~CSLt*qa z>UK%>wKS~fK(cD7e6Dk0{>cSNV3HctH|T3Ff!)p7u6NE4qD}7?8&|s>e*60%B-xhw ztq^3RrL{-NswLhrE;?|5I4tg}@rMV-DK4QvHq~QVDP23=k9~BKH>%;pT}X4$<+Jtp zDl*hEFg0H+GSY@1-SVRSW~Qoe#na4Rwm_7A3kpp9L!~SWU{y3wXB!Y3GPjKSE+X~~ zG^#TMGW-(X^A|98yjhr2=x96MCs0vLlWCpWcry!_s@%5Ms$)G|cQ zJtelJ7Z)kdEM0;ue4N|8BFm98;8=fzLURv;3j8n*dFtg<2Or%Gyi=tQhj1TatCEIo zF#IQw^fEk!hC%|)97sJZ1Nk$`3brxN5F*BdR&ERCT0wuQWw9)+BM%b)0P|~19;dL6 zEF=IbxVm}at^XOWv9vDq+hQuC)9bY%NiDmqEor52u2MPAHJC{~w$rZJYV!Av2?PZI zWG@U`8ut`7Hg+L7uhwVGO`v!!Xxf?abl2?bWVQ?o4+DZ?F+Q0p<26a1)I2>lT6!%_ zEmup>lGE@76KRw<-Q%pB#+mxtLmL_^a9yqTKWnlY2HeO4$ucQXIm=s5X zZC8`mqbiGzuy^jwm490n@uLd#YxVVFY}v&Z(8jBXLt*cU5iY*h=Yn|qT#w^R)?ys6 za>30Ujj}F)lh^+m=ga`Pzj(9^{qe<8W?=EwBAR_DEqPW>omcY(0#(0SLn|@yLXgS~ zyyMWv*L5fitgsTP+;8%L??Kwmh_LxW{O1RXSX z0@V@CiV8wqJ8@8@Uv8gz_2C2ghOv%Xt!sb)xV&V%7vJVie^9z?Sj6ON1-7>zr{~!v z{C15o*LgH^G~Z6s`TO-QHw>>@D6=~ARAG(Tt{Osr`^VWSAbI9wUvgqzX%$sI3zf3| zksj1s*lDiFsj6|;g6r!EyhE^jkBLQPtkqg1Y4;eRAXs2dD{PY-Xg`1(6OHp#%~&j= zjntu@IWx7LE6KsNtoepg<}PlQb1B0<*XuBrc!{yqYh{boH@A&f>kW)GZeG#d#aO(B z`&U*aBy<&IxTxhwb<&ff-6OOB^z;Pv8Vg=)I@Z8Ad$gq%Ktqw%U0t_OL%5T#0VrJ& zYA*LZyK9TXw6v^jr!8Dp!68B(H%|@g?xh&j+)MMX%5r5e{h*Hb`ke|Y zdS(ARyNH%30ls15+UuGFXg1=U(2DMC-)7KvKKW>msKc_8@F9_&J$rP@N|v$2KsNm_ z4i!p`SSUM|&>K;@IWw40B+RZ3@ZTej!^mO(etV2pZa5p~VpT;!=`*pGN;KB?zCT`p zT)$9=_nI0YT;!mG)o!fo6k}f|_6Mj|$NRq^8ey}G_}tu122wallDhp)Cax=pLxhgS zaN!FV5bk;GeiSBKo~NbM#Cu;A-BRJ;=(&F8onvi@Ks)oV4!RCrZ&Uf|)*aF~%=-8L zhW((x2?1`eydQfh3N0no1$90WMeW|VjO-P?4K^oR)3wyzNS;uzM)r7Wz(1&#Jxzp8 zYtEK-Xj}!Lx;=PedhTQ9H7U?ABjbN(c3}0?*s3T`W_9)_)J1(CSyFd}@@OCbIcdt4bDUg-VsSz&%UvCzUEtwtcCSBOe<$&3gvPF96Nu zblukOaueTFebx?EU=&hl55OXK6HF~~u_}HFE5MaJCdLV)CXC-5Rt;G6TL=B2?pSur zN$otT<`{iQKP~z)41@{I4O94vUz96eEnb-a1KPsq6agb7n^;&3kK&t;cz8|>f{6A< zvK9dp|4UF6PRi$$0?5Nsy9CcNMZ7>t*_zb^&{<60E*ea4d0uxT`PhBL{hU59hRU5` zL9y3V;r^kbMOg_CZq{z`L{NH32K1~S+JOz9)W${voR2d2$H?136303s{r>_E7DQw`z3Y5h;nB~ik7qAd4m zkJrBBevcgcbp>2lTAg8yr{ijucTcl)vOX267CsOQsJjT*K~PWyQmGsqMbd=X9X@Gg zdbowrpe=6drikkSR~5=uu3|Ye`go#2pM=t@szczRJznb}YWsq@R-C-Tm0Y@kKvVc~ zO{AOz+2qWOzhp_qX0F#qtTw%(lV|I-^R>@ltk{c=>uhwXOIRlf+Sqt`UP1@jBFm-e zkXdE{gY}u4N|@ReJZ5nfJSMj+`cmUs5Fvw5nlrgjuDEWTLyz;3=2hklH|bU5LT%gH zI*JvFv0kWHatNbdIg@hlD(VMY9{CjBNP^Ro*qU|Qk$U|TkQj(9HJvh+IbVSyH#E@SZT)S<1>`W^U(0l*TUJ0iL znA_zsVzF50=cUFY1zK?--`G$LvWDVT9I{dJ!`lqnq29Q3dUN?A3v7Q-Pz5^U*TQsl zB|-89kK56wOF9tq*_yuJ`@}d1_Ka3ilWArsr>k?`Na_mU`_`k?_d#8bdX|(d%gWHVl$1 zIvkpqCKo3(-72Eli;1P~i)4}!MG_AuYU0K_{m<~HQz`a_z;Yw&{FFi5CA61RZ>3s; zCxB~EB->=rCYw@v;N)||mgQmq7eXwKKNx)mUttH^J|D!|2m*L>H~L?_d6{l8hd}~? zX4qideglO)3Z-RaNF%dN9+>nMO<5}b2!QcYcQu3+s1*A?HP-5a}m zWEtt^#l#HjFn?U61wYbp?A)^quqaLtKap4NSbq~p&5 zk69eCPI!*I*Y z72eo=rt*EmYmI`8{=57ir~p<}{}Os@@>RYjH_N_b9tIS0G-;4LR}9h)Pv0-HC%ycq zM}lKB;BQET&9A%-*BcZ2?y8{U@Q+i!}7( zyg~jpJ%)TRvAw$Vy(#M_H z7R99PvS|7E#?U@dQhY=nCP57%$hU-9i-+|h@dhZ?S<=3m=JOF|9wpolO1dIlKBH9VhBK}?Lb8D~^AbuK%MMFw6wv7~qmtk3-F%!`yQAU&AUIS<6 z;jbKyWq+b1sVwkRHTWnapi#U}zeC?)u%3=hWJ;QYqHyw>iu~(VUm4CNSqe_Za)EzEGF9HTXfpF368ud~?@pG610+m+~Jw#i0}Jbw>-$q0gZ-;5ql zt_55E0!;l2cx2v$x`u^N!X9C^Tb1V^`3RP*t|-ghh@9yS_MQpursrmbUQJQ>_RL7n zVeNi#UIEi+{uw=nK*p#Y$TD}sUHeu zG5)rROVkfv%-eR&gZ@M1PnIq<8&qU}pTGeJsm1^ACy_Gp%NorZEKg@HAEDAcZfZT} zY^^b~`D~?n`{zT$k>~wMX3o>zqm!GnH}_IlFNaa#R?o{>l`tOrKN0r%-n4B@d`50H zU|Mf4P)4po)%A7-=- zic1jsx3vw=_sO1>oAZ)ZiME6BrxSA|P8AUYDP6=ottTqqPQ;KTmU)ryM(0zalCw!6 zs7rUXYg&41VoEhlt>C*Ocr8$n)am_wc~$M&Bh0XxvPJ6iq3@kYAUVd6toA16KJ0$V zIx`;+<7D0S?gLGtu1a=&xK7CuY4aJ#>+}l6eNkT{ra3zKV?J!qs*WF*$zfq-<`(pk;C z!d%pwojANO(+p}?`ubhj$~m2qnlO9wE}#PuB;oKZLW95hjgvAu8dE=QkX~T_Pb_P# zErDEDn^oALdDcz?qn7rNVeeyR|FC`5@ zlucfY?o3Gt`e7tfvoB!uyu-zqKXO?+o5_>FayVbHPl!=1l6r$mE~fafjeiwR?gDrF1Z;01~8~CJiYJT^Gy9z{Hcq2F$@T z=Dpp55BTT6EOl#l+^KQSsBsr8xF=1ZwMhmzx1o3otN;T(K~PYLLUIVE?GlcI&ra(M zdwP7l`)C#+!%WY!kx-U>t~Vj0%SHFh_PSU{IX7<3U1$w5sR|~!T&yJN=N^cda+G%5 z>?okw%|XbJF3M%%DfmwP(drcgPP#nb!JrSyGvRLL{UB{%h@s^FV0GDA@Z^%1`(HZ4sc%ecU8%hC>` zXxSLU^$iU`-@a+1S}}_Zjbd#$&bCS7v_Ecf8LEty?8Y{xm7^1;AF{=hDc$eL{+!6T zBBohD>UH{NbiILvrwv-pc5`>UDr%xzxn(xTh+Xf7U)?(VB==SdJ2!8!ztfNnAf{?s2LdXJhbqfn(8y3|h(>Y<4%BS+5G zecU&JrTp6C>~&%EFzbw2g7L48I*k--FTeXu1`a6Z8MrI`ZNFK8DU%W&|LiP2%8d~$ zgUUtYF7xss5%~famq6BRBI^-MiK&y4B$9+&crsdZu@feEml12Je`l8ytje@<9)dszI}yeqIS)@u=El5$;F6m@scIS5|E3iiv9en zT%c9_$Fz9@h@O|#Zi_#Oa_tzA=VX^gvETjK9S%dh z2tK>6gze0{VlFk|fm4cnzKpCp)6J{Dz`C;BVRrbmRfpyKeuV!Q1(0<4s+2G6Z0U3J zmA@bWky+~J4!7wgxMJW9ZRC1-*>b+(#H^v=3!SN|(gEJb z+152jifEQx=R4!$?D&N0@O4=0rJl{}3r|_P%UO#OdKW0tnFNprOADop;|mWqEuu{N zCug99NFRcRTGx{dnA6!vn3RbNnH9G|h2XmB12KTrO21Rut1E%&W;$oFOV&FJmrjhw zPohAj_en5ol4fhP6ZvtbhIsqf#AuUJQz8*Nsu(d*4*fNkkVkl=A6;9m`AfoHsDqr_ zj4Q(!Spo1D0dw@6c~|2UE5WhRHrs_;r~s4#U;lc$+rlFQd^zlsm7VBbmb8f@&0QH& zeZd*1lQ9#{?R()x$1lsvQetmTF_pUmOP!!{aBP^UX_;kZ$z4gC z*&|ivvckJnj7A$5PlqF6)5iOcV3Zw1f|Sk%$zP2a`i??m@B)B;hKY@yioO(nEGveaUXYxO3hGt6 zkZ!1v)t(p=g__=zBu_GTRhoPm>jdyeVBpFibY71SNx5z0q%6+aQz$7ZS-jfP@McfJ zs-zF$82dvy<&jcYABAYCJ{=+z-_A;H_)qs3bIngpkhMg*x=WBO2@Ou*_btDC{+hA2 z$NuQkA`UA*(EW6MI9g3bq2K>1zb{WL5?nmk#CG~l_|k1jr}JQhe3w`Rt1z*$86Ipu z$q1jBzmjzkO#w^7)1tPo1)0Ope7NI0nzN&WqBPX%YCYe~XmMerLUb|NqILaGH%To( z8U0CHZiY5j$(84$Ghy^{PNljjjjs&)e%qFW)L5wCPoH199tm((zWpk-?Y@Xxz-C8K z&?8vcKJ+x+3csMfisUMgl_oY7HuWkl$cTQGR2gQV{rZ!JIPg+9n!;=k7<1_BEm(7d z^fmtbG2{B^Li<@n#)SFla3q3usY@RSqq1hGq{b4_K52y8&sIFi1%F_yBS4b3x9n5c z`yjoxoY!}Erl^Yn>fjn`A6@YjwHsc58@V9}ktXa)0xZSTaS6!-zt@zjSz{qf~A%CMsD_B^Mo7@*h$)EU&WeUVzv=C z$L;rz6tf>x^*>gYx)ID7y&EcxRy2u0y3zEuy3Spakw-b?e661N|9fhM$9d11Im%^HYKV2@o01LhJ=4(_82%zQ%a1Tcg8_)x@3nl# zrd7R-MV91Y1YPs;GP50ia(x*0alqhw$T%jlWWLv;rzBtlUc$BN4~9y)JGp!G#LekBD`hhJpGc7X@vo-#2JdPPJu#T+em~x`7GWids;MUKCKW?UOKM^PJ(H9MKEz(zDfWn`r!eLf2Wgo&tCqLC!A zLuOiG-lZ~?2KkLx+;(i}yoii$wrW~!9zr$T(8>>FvZlGZ)WS0X7$}Pvh_ze{qV#$B zW2zfjMY=E7T?z1zNt`S@gbqS>xIbkLq;l``ckYk7fe3$3RIxh*rmoUZHB$GwGx&N1 zT{4s}Scq_7Acq~kKc6LE7bZpz0l~5BG{8nH``$&9N;g|3j}(Lp63cCpan5eN3{r6Z zQSopIlHcmonORv%ti2y56uG%s5Yo)b*1K9Qw~Tqs@7fwc_Xp`fH=0e?*}b?j`z_(9 zAg>&&Y+yh4u3@&vx*cD6O1}GF(Pg2qG5*r2G2MCBw2<4X_$Ql-JB`%{(&&Oz2>*M| z83_93vSW$L4Q6?x$DDN@?s{}Y(2ge8gUU1XZF@En>R=4g-Aj>dsNQvRB$YC=`Vx2G z4{~%iL^NV?1voV!pxo`b~pxZrGzKI6s{TYu1+^XKnI5gQyQz9qMu1tFg z+()~~mK-YViA6dARN(ca9{Cyz%U%nuPDk%%hs7BwhS5ay5WmN)gk=p3vtumpDYwd` z#Pj)=TncS_xzm6&F`t>pNNsyWk`H@s`n$;HDBRWyVX7rw3!`)JG{;;%*WEq z#IdHqGr@&8gzQt)HFFGD`MrN<+2fqx@jym5J5iVq(0<^rxD-f(eMK!F134|0&v@53 z4?`Yay`{H`KyFOsQ`YygtPCx&pqSk{UYQ+`*9k*|`ZgYpcDL8+F)z;|Ozg_CLj!MA zj434>j?AWBPR{ruSwO7=!r!J=Nl0#n?j_#PQh)tB@!{gBq0MHWBv=}sWIP-Hi|p{_ z1;#Ji-UuZ-S+N#_av;N5cttMe2LB>9Ajo75CEAR|%^vW4ujNcpR1Fm5V%{CQLz(T6{KNmv_-kDb)D-z|D zp8J%+KL6TM!~K`;X#jrQFfzCxP&D9ar6}9f!#z!1cebJ-5c%Ec`Jm^v+k&N0@XSCL z@2Fsd#NrPqc3+RU-?q<{lt!~jlofAD1U6IlCp(+1e`h&%*^l3n`eZ#h4ex!F#>)el zrfP&3ZLx5<^$3i)kli+H#@DKDrup&N^3Es@4K`RvEU+8AdS*PWdMpp+G-J{$R!paAEo6250jl3 zlu}n)h4a|;?`vr9*Qf=ePUa~phhiYtIq{c{dJa3GE-5L;~%!2w{Vo1(-q@qZInsOQ<#v3 ztq=?g+s6bw`EA1A!8`AEgD3?q^_Mu;*kcwmc)`1^AqwyJrX+6+N)a7}7`-;CB0!m?9-Vi#&k3Ye)FA>X18TBSmb$|L^_MD{baB(7nrMX!FVM5pq6=8N|H`=OWQP`PXF{83@MP zQb&A04S2H_tl@QN$0WJ>-Y4kBAGL#2=#HtLaP6}O#7i^-Q7dR0qWQLK5jn{X{Iwio zuXle>$$-%Y){aVe+8%t3(P0)#XM_e796?RR<8>-eSnBL4k#`fl?Q!1kg`)t-!>F#O zM~jODNy4DpWx41M-}36I4#w&W!$P9&XkFiK$D36|36Z))wBB|j;HtSAUy&)^)sm)D zbNTBe1lUUOdXYf`D{G$~brFO~&>3)Euo+~=kohN79{(*%MhQ;0tjsi(dZ^F)@8|m+ zrP7q?ipVgUhgrsvXr2Lo;`z6FL;IQ}n7!*O!+Oyl12Pob;h@PuXRnq^kB1&0wsJ9AHwWt$}?0+Tc-$vk?QQECS z40&_dbPtwo#3g;FG|((a>ZT$dy#1=NNjP4>BdNVHX#i_tbp6ZO}x@L!%eo( z4q5nsE|Z;b&v-n&NKCF0AliXBAL9Mi>^bxY@iuoOUCvltas18HczJ{D+Q0viPz#i(b*_grFaewRIqIJF|aL~56-?_?;0lwQE18xwq7%arupsN{>YYh zOBZw<+xsfjw|EM-VD4i;a3>bLfM9r@UW7m!5Q;|L2l3;wlmHbEQLnWzN*DWfsz7Bb z6D?SQdQb%a75d{wpP8;`NLd@9_q&a8DqyR97y@-;xSvh!JHjhx%`~$bueh+7#=hmA zc#55O+6ALC7E%fE@P569@_G-D#F)74B3>l?!V7xHY)6W`dB$VT%orXVqDEyD1$YXi zW9KO+9U=Ya)!s$@Oq?zw={T3OcI*9YsfIx)^Y5dVx9qPhH-Tfs*-xR-hp?i|7W$e& z3K-ie8#so3o#Rq#0#~7rM=*+AJbKmWTh#{iipr&W?}DB^64yhLF(eJ#zw&9V zXbOv9P8_CrTym)Q=BVRNS1H|#g|D^o3G$Lgja;}FCy%4cXV)_-U7OxSo+rM-bAGsQ zMB?*(!4Vk-wcQ$4A^#W#k?paQ5OC*Y@K|626f56H?*+?8zxsYg?%~Q8or>;-#(D zbg7=O_yMRVI}Zz~LJz@v|im7L)o>R>`MBVs!Mw^9=`<*V8+-2$Sq~8oV+j zj?=bOGiBPM?>dh2lv#TYXX^k-?oo!-UA*R0lsJGl!5pFCXOm;{N)a7&7+e@A4CgE) z#LQ`_bTK7*ySu${D)=KRM3qRRGN>Vyt%FJ(7fR+}cAG^-NXtV-K5iDJu%%P8OVR3A zPiCxtC?W3AODs$196QjY%$H&NValFQlxjps$n(wuxXcZ6(|{R-j~_yWe3hAdk`!?d z0)AQj0jtfs8*$r|z+c;DQ>ZQfw|WHLENWaq(QN6I`qqxMJwokV?g|%`3@44ezRC+u zb=*b*1;G%gO{X(B&J{mQIE%KueVyQsuIlPPa_7Rdld9owE0y^}G(zt& z)HbPLJncNW!1J9{CGse36s!~8sgd}qAH68&wDAY_&pn}e*tJa9YMpUCl)?GGhYh$t zFbo3M?^kmv9Z&0j$8~d1|E?pSru(C(8#Ny}DCy785^8n`ihu36RXYLMcFro9(b=wd z`ANHQ&!qk_mad&Fso|bY8(Bq`Zy4@HSv2!>y@53^9+7W3Q+|$K)A_nd1 zTiJdiAtJ;JV6`jOW}o2(rI#ja281cVOe-o1%7!VhTiVHlr_OaSOA-?@eA5($zt5S% zD{=1hcyn^07%x4oU0M+458J{1)}&`8^EQpIA?&MvF1y~=PY2u94Wcv>)DO7d`6#8! zZ#35`6T$k|X66$>7n2?MM7|3sd^$PSa7_f}84*h(eE*;m<#Dx$l9#*IyV(cH+4X%{ z%rP^HiXv%_){+5PpFq%}wSM|@rN~Qd9{*tYTRKsH7rxLS+yj~pC5(JRuUsCuz*3&& zX^}E=%k#9UC3(hdey~JeHbSa9JNV}BkG=rbhmFbQLrQ;w4`34T1b@`%-$UmwWnCgx4 zRK@$f>!$e6ll6rRESHo;Q-ZzR22=-}CEk-IN7cRSy$g!nf29`q^Ck^g*Ar!{W5>WEi%z$mXu!2im^-Vw--L2JKebd=fS-mUm>r_^G zK*>oYKsm|=%38D9@taRHM=Cg5lJ~%{oXyQFH~*G>*PS<8tV|XwJjeEYjy4!~$93mT z&tM){mMvC{^tG<;X9@+QfX6*8`>g~%;9EKcY#v$Pr$+NP<_6qSR!@E+L$Ledxp+h5 z0Sj)45IL&s!Z(a8&teMzjJRdVru1%CBRbON_FW?pHa1#*CxvHo+%wdD+At-S&^xPF z7pbUefk>!|U^O~nh#PRSG~Yu~DBNI1&ropd??GqQJtmV_53!#ugzL(A}kWD5jtD167g`O0w8sk*ePq*^A6 zFR0-3g^=3d3F$?_q9+XtMp-4@FlZ>2lrT10$f~SNrHJSy1&%_xP`ZcI>ocE!mkJBP zSG(5Bx)uf!7#j9EO(K;h^C7QSVV;N*mB5!o$b1LjZ=46FkFb!W;aE+zOkGY?fQHyB z^I=7T&pqT@WblD48dxT^N~H2<5f_beSen~gj(9(65|8=S$7*;I>W@^}rQ)?&P0v*{ zbXg!xmgG?Un9~;TaoD)b6j!PzGwyM_L|w^qin!##G{|+pY4fh$WA23X_iHi(U5Asd z;&W%LuSH2W+4?733dfl8KDURQmuXvdUbd9wZ=&_Qmgd@2c3Z+SLWzQk`s=^(iXU2WDa{?UEM(j z?sOYQf%`^p!Fr?Y-p}@oGXkm~1yMBl!}lciMd0Y;u|QCv^RHE5IIy28hn) z@V3afk9P0Lj9V;GE1bLIjRT0&s_Cz2)psNTLR=(;5IzaQCa3l{01Qo z8}$E3C>I#!*_U%Zard4)-r9)=^AxH&@FQ3hPrEdPY8w)agO*+*O^qK5V{^$K<%Aoo zjEk|gTb4<&K$xNScEomx0wV|_T}dOr?(jo@Z6rrLb+Mbw1!|on%%2j>T1pe=fD^p< zcidN+k7At$+5(nNCYJ-8 z5Vb{LnMhlMVz3TpBC-5vF_}meT0qsfMoG1_Cgr{;3}BC!=Piw-q$yNRh&L&bAzZzb zh(uISr8>OEL0B5nvDL|jcWeE2z`C`_y&qI}+^|ux6R&*AX1Iu0r9~D4tX=pEKm=HZ z_EWEq&E*0R-4Q62uHrX9#`xo?WpKLB)l3LI?vqGbj23zpy`C6`t6+{Nm>5Sl+#{7(^m{E_Q6N^AVGebT-Esl5F|X}h@@ zj4$n5jVFrF9W)`Wdl)0iEWbLd5%7qIp(;6^-VL;_aEN??|A?;oF1N zIxzdVWq|LDA9ZXc31)5-7xxe+(ncj`$?|t2Jg+5tG}+;y@nhik8*cN<;U6)6gB>5{=ZW3|m!4M|ZsWKx z(Zx^nXl(9vu}z@ff*A^k?0mjajKcatRVQ%9mB>OqRdT*1Gpcp)FGPWf z6Xt_n*Ipv-#}6#m=lZyG92^BM$4_PG(Pn{`P>%|;&*JI&Jisu@!yK7n_{>)0C?tQ^ z$vg8qVtIY9Ds{{0g{{O6juTzGWN9`XV>>RMeR@qN=jLw@n7ax+61IDYe$pqXYc7^R zwi8$|FWvEJrxOqR{~P42a)FT-v%f;%nXlR1O6tn-zDzxnFQ0pH~T(d5@1it^)Oh98N=p= z=Cx1tz7eckK2_?+zp%k<$vY|@j?C8h%D*l>h?)av9dg>CgaLz%711F|?21&)Ia`Ou z2L;dxEE&kFKG2KY-``spWwx+~@1UdWvQ`;k6bb?RBowtj*%oo`3a;^JF)IpjM;WJ3 zuI891o453Ey2JppoYY>)|LM5^9*XWW{^>MgtTQc@`gwEe9WyePl2+7_F}C`%@i08g z$VHdDZ+{&Bi;T*!i7sKP;RagTo@VVakJh+EKZM|WqqE|iN4r|ck673BUI|nzZuk68 zcl)Qm4T`cG==Ie5wcL@0?fcxR)GEj;$AM}rrt2%1G`E9vb?6vfBiOPSZb^)C(l!*< z?La2H$X(L$Zb(PbC(Tc?bMBW6=+9?n*7&7#^NJmyNFWWFc-&9yF%T4;_E)RIK%IuA z?Q+bT(4HeOn;@3^DXoc}@v%!oVt^F1AglrEb+3Pn)4H1&BsT42LN7^^`9RLWc!^ns zFrVHo_fp~4xg3Rs;S1YxB9Ir<(h&JF7m&ENaI)lj;w!4+O4&EtC$hp<#lyrRXuu%x zt!nwn6YMb(<8uf=3Bn~)d0!Zme3_@N2-!<7`8K{GwYhKxqjmjuxwQiQYc$l-xH6l| zXY_q3+4uIg24J{w;JirSeImD5T4(-II6Z0OF`#X=>nY@9lN6t+v)n$dB^v{bQ}XK- zZvtd#+I}GW@oSNdx*dgltxw#3f>n_+M=9M+LxDC~k)17E>VQLD{AbM)@f6S6%wTB9 z^3xprpm=M3#t~wx_}8PcSTq-zFSzymc~(T}qMCyeQtOdv-}Jn)p!Ma48h9F^HZj$8 zrETpeT;>b~Gv!vVBh)_a=INl z*P?)*?udSAdtdKei>n&NP_H_IBL=6p7_E$qAjcz^z<51zoB)IItr{uR7UuG6XpIlP zS`pps5Lx+9+3Q6|Ms>ue7V!Ba@{N7$7s8AFN<+*qz&y zE#8sI+PHbC(qsQT*k|ayyrPkUZrcY+MsdT|7!XmQ|H^+#5g!O5pQzURGXztiSfn&A zJLqFU*0q6iMHPX3H*IYisr{9?z45bi8yieO4Cph4R)g`~Lav$2*~t*sO1{aP)&=Fn zt74SPkX(8#)_Y?Id4mQuqCLRlwI_ArRdwe3kF&n9`h|J|)V;DK%96wY;Bdew_|n`U ziFV!pf$f7jvek-HfZJDXnw*yMpHt?QVKegMduXk6=5;q6TXxyn(_t90>fY#{hxlry?M_5jhDT^OY4sj? zsoD$P(|+kXjjN>=_n*J?yf!pbS|sQJtKJD9o0f&=I?CSVe!-fBg=p-Ek?Jgh9jEYg zJMxA~L2S(MPyBLcyu@m-3R7qDixxUIfU-dDdI6q7hU`+c{Hv1KLFrBAHQ+V9l)pWzFW=Ra?2XE zHeM9<7-^j8uz9_9!DBame{ZB_t8ec{+PON80*(TX0*(S*qCg^7w` z_&=-DC(eqr;k4NTU4eQ`%PtfG&QdjmsxbVm&=Q;=LIopM81mN^N@xhlq~7Jn^Fl$p zp7u+TSj|)w18UauOO&Vzvl1g~4_|_@d!ewzs_Mv&c{Q4tk{yLN9MFydcUR|CsjSsz zo>ha^Rt{=T1fI3NsWK{yWix9gJ+}Wby2%IvRJ-v3*{-+eK?ok(KJ}y3!;-TJ>-d6o z<)@d%gAN=8hLHk%AbZ&1#za+*O>5Dj;gM>&rnMTlGNFK&QSSL}V6+B>^b6%l~; z_+3v(-o>ey%1ddOc4o3lhq{cRBL^&Lk8N6BAuo09_tQ1ycg7Z+(WU^^hK;Y7y zJ9kRnv)*&+`N9i^0Puu_R}+aNJ9s4N*o?Fkt==64IdM3lvNWf3$comiu#NFyb$M*&9xM}dt~z!~CdT9t<+sOsX69^LrO;c9M}0^O4sZPG#6 zhB1y%MRM~_l@_uElkI2Cw>?8W)PxZRWnKsKSk!9ASh}2%RtyFkgSTtKTyk2K5@U+K zi{Aap|5Nh5{-e9pbNA`D`rJrPw)K}`y!%}BDJiFPBc9UlD^_431w-~hIwX^tj#`!0 z9E!tAk;?D^WhFVDr)4=*vn7){*l!8O@9ZO9NvJ(9Tq2abZr^q4H7|dSoFB~YSED6e zcTAaeM97e`F^IQskL=k?Xvd3{a#cYPC{WG7pgMR{1h1tgd(!GT-BN!x=TM5cHJMcD z{CR)ZLwn9G?334AGw}0Y`tElJevbW^p#9EweC+J5T^;W;Y8|>A9#LDdF5!!393OQ0 zVb|(`*D4RVtdiX&EJM|8`%K~5#Ix&RSEVPDUmK;4px{D7bcBqN>13nv^dd8%kRaOp z>fAxZ68rWm#_<_eaE6S-v+`AJyBB~&Dq$oYBAkM20RByk=v|sqlURI_;e=-g9?*9k|v5O#x?k z-M!`QDq}Qq)63Mt5Hm*3kgo3j1V1Mt)JNy&WI>zH1x3* z=f(~@1Y3Qn5y=k> z+2J)-lj0auH8r3P4KRYx5*cI6i>PUmcX;BUdi^E0$a&@AWpyHdLKVG|dL*U&$504i z1raF-JcifD5x3|YRT-#dRcLYgH~>(MRuU->-5$VqIKy>Ffj-bUx&Y7Ogml_d*=$_p zyb<-^aT?X%m}Om$Aq*&aT~MKOlZu#%T3A-qdLn?=L%wUH1htd$fixh>4Ck)T1f zZ~1i%Z2oWZa*f5U+73)V?Aq^Jz+_)+W4BQ|zKqJ}&eai%@Y$fuW344{9mb4!H535SEOc7^n&uRB#flQIL zEwXuRYj>?BpdRzQgi3o!#I4&eRmQX9Dy#GkGxAiLE#gtHIAf_LN@fgM6f6bn*(@B* zsf7Np=e6-;+~pX!d(c6v&1$XkY(HB$w(HAiD`SSO&7d147tWkko4+xpIK8;~$ZRYJ~fF4{%B%U9oThZ(wH7lyT zNOyRhGyz?Oak9(6-mF;3(iI;3%*e3M~BFeVeK7>b9W3AitC@+;dNh8M~}bQJ||5 zBO*Eew%gS(I~~iPJlUzWa{Y9Z0#z#>s1HJ^LGaCi(u+zk6M1k&q=6MoS2TmE8Bbe7 zVdN#mnpdg$nVm2U&ZvdL1vT;GZPGqg#+XGtK*>9cvEcC;ZLCR68AdLZvM!e(sxn4Z zCCl0~w6^1=JOloF44#6AIWGhGsxc0iO8AK5I%)c}c|4XAR{B!Ur7e`5s?Fsbg9hou zpkT(d^?2ksR#Id0=^4BdFtow6EE!=d1c8k)P_`?=5Uy6D6vv$6#vaHs>*y7eD!rm@ zh%U<=RrAvb=o`<%my!L+t7E~jtQw6m(g>p7)Ls|lT4&aAs%NftW@UuFa=VDWtkq7- zX9vRwdbRoWB7cy*Uk|zsRWI(3wZ^c&5^L3It8Cb}?B|^>xUKuAD~(KXy5uRWp|rn?8e@QR%Xg43u+Mvj3L!8myyxlg6PN5cqVnauI=gsYZU6dCDffRo zH+xZbtnqnDGbBP0;YRWXfsB_CTbV?qta8OADX)S7vzwgA7?DAd_GWq|v@MlQXVL;e zD!^Ez!AJxlA8%%iR?w$qxNQ9trbhnF4>VHFxA=8Ql@PVw638?tqj4#r>0|pKhAC4| z)Mi$1CcDzmIl$Ppt2mfRM062AAP_l@7ADl}`CW4U^2t}KOLkuZmIyn|l%~C|tv{LD z1Vo^bOyo~%Y*j;jjZPqotIrgG7IkY}uc=yIURH}={z9PP#xIBcH`h{XP`9%?yH&fw z-8;7U=5}^4ZrLAZ7-nhNFt59tyQ9EXDX?_>_*R=ryJ7#$wZt3L?d;C6qg~w&+NCIFO%#3(aOEO}rP*dBCrpHvS znp4R{O5J$ljWU(v$DdY@KmNF!$DyB-4QuGLW>X9?NhYHMot8ujrTzUd>;-|Ip{@7k zM4~7XBjy_?&^3c)=7kx!)xK!vBvLjLShpzq<+a$QrVN8v&Z^p$msVt2JH~dYYY$v2 z=ewtNs~K+^0-%5hD5L~v_rW))l9J6XSN^_#*=&x_}xvX6(_>C=ue z>h68q_ukhuA34>JWfw*#`*N=Q+3CNsA~JQz7EOwSf4%BDtzUT z=>ijB83We0ThaBAW#G+%hCzL&*!l?6{17EzS!8vZ){)RE&25;F1h%A2PU)@Zxl~rA zVyVb5M(sZM+VT24Xll4Y+?nRI5UYIoJDA;dRobw}?BQCq`v%y-^>$r#n*ucIsR6hY z+R2Em435Gckx82eDNr_Ryglm6d&vqE{KddeZb=kfV zXtvy#SewSrjj1&!MVqN$V*j-u(rpFP&~@T5oRV z4UNJlZmZ{GGY0|53mJ$;;4`=-_>7NZeCiX#$l1JmP^AV)z$0=pu2o?v82yql%0goF zx}&d^^6|4{>Wn%q=jVzD7ZO_$hM;2l&jHU-315QlO&F32#6&9ydqF6Ph)H8v)-hB? z>ST?#lk5nRCO=dljakT=%D#e3qdl~gcr>(b!85@oyaC7%ZCh&oc{z_xVJqDL8K5(g zZ|s0fTG7)3G4zL36($opj}o$4kk|aQsuuMQwCxiU()U%nu2=uZv7eUn$?POduG3h{ zz`zYygyX_J_&U1|gQiv-G)AwptKD92UQYjpu$Qh&j5tWzM6~AlFoN5eHjJz8=Iki2 zc?!4(-{xtyR*Umr`r=x}kt;j<)vrbl=cpi@g|l4?JzZ(Qbd~~k)%+XB1x;CNDc~;M3~e^+H7WM@gW5%8||s+cc}~-Fj?@IS1?!F z7BTJLzhBah?K@O135p^y1Pf!J*?4HSk1!tFwJ&OSPGd)}Xt#?{wJ$&Ugj#;)nNZ=h zRb<4C!jt$xR`+I( z40vjt43a70A6ckT{1J9c?NEm=IV|ViBWYDyhFBNtV<5Yl0Q>@LMOujA+=ch1`M*Uc8YnWMNZd`qkyBp08$_f55DyD^uSSe;r?$A#GG7wwB9XaPb5ZeXBrrz#~)ME##c@6 zme-9GqyXg(K8;MR7+R`ki9!p>3Z)Kq=Xk9#PONl>dA0B$*dgCy}hleYcSTAwJ{=tWUrDqXUcgwwJkR# zDd%AeKX$NXVrhiDKr1FNTU48g>I@7+qfL=vILNt}I&mI}t(`E?@NboubIBuf)0kS# zNoIpa+<8|^sAy+dI|?ebt6B|ivkxZSnmC9 z>aa2t1P&n4GsbA-TJ424F#gb7I@^IES!z77@vN5Aa>fB>#?}E#B7bCf5{L)Jl*{JS z$jGS5;#g;ZY>V}eI?|Es_)}F)u}<@QvK` zvJg{hYp8`m8n-pfb*l@HJkm~c*LIT>2*|HD*?JSz={W4nCh8AjRcC-bh^wmSWDTw> zFcK~rwN*0d^Vn>ru6IyrKSJCs#fLYg9|)r=HIh*KFgPsXx@%vob{2Q4^XJd2C!Tmh z^61A$x1gLwb39~#YKF+DWj#j6z+AD0WvZlufEr8kLiiyPA*0<(CLvcd>Onh~Qn9L* z7d7ISzvxPJ{mz#PJ**LXHkL&j{qshHcP*6);Y1BeVS4Zln)hpxn9{gCx4%8SH^4fW zo&DxNty9Alcaj1uIM$HTh-PT@jsQJPu(F6?np=w@46{)`Mq?NPt+WH( zQp%I4YA??s@qsN3JoTt-B`r;uM^zBv@{uEIzA~%Mmd~p1rT$&=PQ{;Brz@xAJf8GK zN>YuLRWhcHuf?F-#uJ02M5E6zA{wUx#7m__Rn>0yYm%^h{MVM*e~$xx;EDwyaJUPpeB(2c;+jW8GD z>nB!YY|7vOtSXJh)D~4?!q5tv%2d$|ug3yY1Ewg|%)_AAwzf4v4eUN+xDG!g(5c6c zeL&u9u|=nul8*J}t13Q*EH78fCo^P*Cu3xtGmb8eh|zZr?)$foZI|koOq^#H=m9qV-$7!sE~Zk)pe&{{BGDzg0{_qU)7iYb>6^mLICA-RuDvfT2ARrN-YKTDXj^}AnNEvNxz6S$i zLIu8gs*C{w$i}nEqvRv2)JtCS63IJt{G9skiHGIfZpo);#Y%}N5wTJcs?YdGpFf3y zR#lZfgOKha5}%}L+~~l{^l%Lo-^}!kdd+KIBlyp!=f#Mf7jdILt6=VBqaM`l!#mYZ zKlnOfBQ&UizfXPT%WCqPYr=ao1Tdprl-s5sAW9D|dnz$<2G~luAdx+|tNSp7-J);! zpBe`Ke0y#S$)W`7v&`g39t0m`; zqW5=sU_?GAgDFsDol9Y9P^W$rMg-p`@#<_k7aJLV!x~ckDXrV!+V>FW1D)H;bxgG+ z+!Ch5f#FPURxwScC)G$23;|LC2G8*l>cc~|Zl^c$8^4IK%Yg(6IEvuJ;2@|0ZJG)B zlQoL=A6pSYW{gfaw}#X}4NaL=?Pac*qxE3TBSCv{V$3(4^a7Ef2mp(LN)Vzk%_L{k zTyj>;CT7%3Y+4dnMpOsQNN9r;8BWGWg3(kJ)-@=8E!G_ZPL4{Kh+WP*& zXFoHj%Mj{$HNR?g5nX41t<<2nr|;rz()F&%BLqP|o?GO$EHFA6U-fvZUeT~3uxMtU z&ZkPtf^8j3@nLSj7(dhZ5S=>`CS%+5Lp@bhJ1)^-LQdqLSC1_GNXkz>`>X^{twJ?d zn$&Hm(kqqp2p+eZVE`IoTm4!BJ;tnlPkP~ww3td2YnYf^giaCvKebCaDZ`IaIk2rP zj`kI^gUzxYG|-L(!i2DcD*QCraIRGGNgi}wR(+UE0%f35fq{CV3LPRKpU#dPb~OzwNS#*S@apKBC+K z4FIF;Pb12-cepzWgoy&(dhjK%W3OGv4X|{dzrm;Py|-Nwb-jIM6;?@IYvfI(RmTCs80}pef+;I_g2c$gcolrCL3pVMkq#tGlnNOw8p^SS-~1BU zHq?lTt`7$f9<1ploN(opSE)=Qqh_XNM0qBeoRSs8nO8l!MPM8{0+nc-w?$1dL-29# z{`ytS$8}kHxh672W0@J~R2i2iNht&Lz9ikqiii}K< z;eG1UMZPs&r)`n7Hu6>I`)RlAM>_S=HOSI^?(_zylrY z%XPGQ3iMo+mdIw+$ZKCe)JA1|Tj38Q#nldaO984=U_aah)?u>0Kz4dT^3Vtne^3Mx z0C>#_|6){6ud*@@xqiSDjN@^`loK)vxXVt07S@ngb z|4;H#skBH|cyYdbUL+-y;3U&2DbME1DrJJ)N(Me?3|*$bqyQA-K}=bph39I#UKMor z+7A=y1Ig8x|JOxFEI~LBhAF{}SqFh&2`e~XG(&kAzN^8L_i|EAJTW2V*TC=0{{8#q z{OZYTK`VIt#$UWZ$^k&Tsu_zCCjvcm11mY#fl)Y?sV}+x{pgk&ZTxMwcWh#VF5O@F z)F;)2U;L#|`jvBPw2s}z8|o;y3$y1EBVsvz(@h&fc91JM!@5CU-L>YXnSr%xG?wvU zUQeF#R3?^|l#V5_l|O|zX-V)IU=Fll4(ju3U1tYmMAL;%;{U|Uxl%g(Tl zwZBVZz!~wg4z`^zO(+t^2W|-BNU3;AWjt)kNAh5d+7p$M7Kyk+}t=$lh7Jot8;2%dO6Y;UY zb9%OBq6hCXVw-W6nJkzgq=}IUH8TdkMMyNyEE&g1gc$Jy3wB7;ls8=&8gA4vC^CCv-y{FbM3cOd+f-OEwvk@M$XP? zkXExhbKyoTYPfhT!S@hT6{;xu<>BA47adbK+;D@s>Z+@tK7%tzB&y9Ub7GkUebmed z&pQsnjOhNki%t^Q62ag6!dOFWdaS0=xQ8Q|75))t&tpD(7Zob-TLi8LQlahJw@bQw z-(~70hhHp-5~M0rfmN#E=-LPgJP>*@NpRr>7%cP0`I?b<+IM*FOJD374&KOUNBizH zqt#L5I@B3=sD%lf8m<4U(HF*uwl%?Mw*4nXZ!xfTM!g-?jpiueD9~>TbUHSge?HlL z`}~(}pHID5$LMRjqY!t%-{KSh@b{s>(5cZvu?#R+=o5^;>Q(9m+>pZzv@@E%@y1~$ zL7(PM9ZVxwZ_NJOR>BqWk7K+%uU9t#sYP=iR<_}9&GY7(He3cv<4Ikfjb&7-GRZ7; z>G)A-l`3$sJ+7{vyH4^>!MD-*v*+YIHNB#$#%jBw9Y@3G5sMnlD22( z)qHN3njhJ%c8%?oG_IzUx0KcihS^>`2}5NBo(12+1g?W0jR}kmcX0(%1-sIM2~}lR zA9wcI#QrL1&m@c(@Fed@W{ioDigT`Sg&^+)dk2U^pqz z&F_4-45g=Te#KBM)(gM;VRhlr{yE?6rcwS-EMNyG$rrZ1z_5sD*CH<8QYc#Ql&9LY z#&TuRqX4BW>qAUUPE4xT->PE}e(5V;QqMpCoHQ$yOHescRD^$#6!ExzkDIN*kR&D3 zF%78+)RG~RhX9MT3Qu42Q}0BBXbG^ea6v6CF3I`LH{YyMsgyc8aYS8r<%=b+5-W>8 zCb=*lY@bT$0T4nL(lAPl&@a4XTd+Ids45HtaQ9O;RZ{6L#Wx$K%`)7+f5jhutS`4q zw|ramWik;PG`MM@{wvali422?c0IB(Zkhu}fvr(MvrbgSks%SLWMkaa)`r_+tMyQ_ zyXf>5e?6BNjE2bAND@Wd76(T&t*YF5`%bm&_HLtYnI}*^N2k2Q0NlRcFGK8;fA*)t zK;lr&F}P`=uZ+?7wc21bl$7cbQVaKgyGJ9rVKfCOJuvntz9Xdy&3__n5qSe!+0>Gf zbgmdkd>}Xf@j=~~q(9ra|0wv;Fvu7PiS&o#BqdeLv>k%8!%AAssA)+%GxKWi_-;vF z1zX~=y*&osKZzisPpTvI05Tyd~kY#je+r&ic zDoY!&J;L}j_J%x5Qns2=)8z?Cmrfp3mu)+&_Kod9J9@ZIqjd(>LRm?rNd9;LO zHzY|qX}coY7S)WYfCs&7*AwU8zn?p*NxB{TD){4WmZT$xVRFiQ{N3*oNq$ENx4}-j zZIr)3G+Z@9fxfBIIR50d*AD$coTpEd_x`t`r$bK(zz1poxGRl3i68Ve*_b+r@+Vl4 z#dJIv06H=Z0-Ls|iU)eICa&Y$N~Tpr5{(sCRbh(KQW=^ufi$E=M^l%|`MIf6D!Uhq z6X4;K-$gU*0s|iOA4Di8imDjk54dqHX;fiS2_M9~9H_^#cqOU4vhIu5gfdlG3B3i) zs_ba?%+MW#5-hNbWgQcwoI%g{XT36Zs1bI7DVi(plJhqlxK)jhkE_wxBp9J_$s;aI zgnaolK6Oytl&AaW%Gq+N^$0Q0DQSWhv5S{@Z z|Ii0}s#5E=QGR$R8x2fa{Pgt>ap%tn4;9!M_0!<5cdPA2augU|3cT>CPjz+j^b6O` zt>(0Y946m$y;P}*?;^Jmr>ozO&34{b#)!z$pV6?xg@?XB>?CmWZ$$x0Kj^TBiH>?` zcOwZB5qxtc8F30Wu0ys|R{QtwS2x{olcbT6oJu2jY%&Rzn@`2nYN&SdXm3q{6cf)y zP=1+aNI-Q?#dMvLxU! z<)x6Yr60ZvD2ZYcDHX~nb+hx73quV%(n{Y&-89PY=6yG)edA}Wzc7ByvHl_ug3-@@^{XMk!l4j43dqA=p7xsCAje@I z_6vwC54P2{?1cx@*?dCfKA z1>0T^!;zAlAOFvfc5SMZNOfD9*frz_xZ}PWUz4uXi*qX|nS)^lic?*9kY3yYcsjx( zbP%)|Z!ovTH6N~xw#T|stGrD#=fV0LYAX0Bri&*5L`i>r~4m$b{00}o&>y|ot>4; z*BySn`cIetjGU(uDZ?5eu&iOSgcH-vRRO9r0U(H>^eL^xjYWHnjGO=y4}JWV>xI!Z z=QwuBB|LN;w!QUd6&)cC6L2@-2?Rdh`NlVOeA>@^{4YA*r|WR@6!3C7B#~`d`yH_v z{WuQOR?yYx>%xPqMj4myC=gu=Fa}q*p<5oWTj4$Plee_W9{9X&*c(w=wD74<4m|Cm zFu|acvO&X1l{&@ZAmq9^r$`U`jL{UVcMPM zCn)WW=x?Y~;G{*JqGad$2?b-sFB>B`4>M4g-N`TAFw=5i=FX_GlJGS(oP{tUqNsw= zvHcUy1G5I>Awd2D>F~}=)REms)PeZ}ibmN?NvOZ9QF9)eI;JwzP{|zBTS!#t36le7 zlGaGtZ`>TCF}Fz-h8#gE^XOxYv=?rw)PM$YA!7t1YbNeeD-;S6_T&2NuUAa-^Ya7A z7`^cM&*}B(^5@uc3We{kX-RMn*NPCVGqikicVS--Ww+rj1kG>!hfU-)L~Fxm(O%CH)S2OuqZFM;5=B2ej7 zoRm`pbA_skA|b$KY7S*(HJ6L4(xtab-lv{lQfC*=$vK0y65_FvN)&YvSNa#S5~5fd zA|3+zvwe=NU<;Z&bJ;sL5qI_15xtv(k(jTbjMvD{Dya)+Vv8WBcuOGi2K8i-w zj;S5$mEO%#J{Q{wL6!AlZQj)=YN~7fq#i#!8d>XU*YX(Y!(dv=W0g4YYKB1L<_aCj zIrYE4){%bJ>x618I?C-O&>mx$H_XuZ+#4HHdv1UGmdMUnY;GsomPpsLMxkMh+_rrs zJM$=}?WPCYp50k)ptdQ1XkTDK_=ktQ*@+gz4y?G@fu%RJgKxpJzMy#=u^B3vNb}=} zF(&)NyfL+Zd|vPwJ2j%>43q{)njKyYsgav z>evs4FA;*+3>+EjjWh@gaEpGgc5d~{>Ee#J|Gau^h<6g7yyJZ;cks}rcM|j7=!HRk zSiTEU#l!vz^&Nk^ZNKk`^cewe+jrn7uqg_x!W2|tbVTV74WU!8En@OK#5#lFGn2I; zYT`7S247_WQ*Z53T}c~d0}n{4(&@C|(lks%Mk{GgLxs>@^lFPf3D7SGxqOiwcDvBW zZvR?2-c^%p<#?T|Jk2@bDzi%o?+d#{qU?szcJzNtrE74@n}IC zG#kG{{Pr^WI1`q~p<-jYP`IcT3JWTgOR1M%|8hy!TzR#+1e+b0tRZx<$gmn(Qsiex zY#0yrWr3)mx++0TV;QUj{2nDBduG!35hbYKP}%N4%feq7(@(tWRqFMxf4zG7%`Zpj zS^Y=+#mOTaJyxI!WTKQRia?2PFC_wF@UPPk;EODM8Vx&ahBORqfHHr){}BQz97nsU)D1hdN9xnxJ|)}i%b6ur z%$Rsk4DxEkMn&$g2Z)W48I#@vDB+<*sA42p71$M7`HrB=M!+|-Fv2r6F%DM&+q5C} zp3DKYGc}J8Af6hl=)6=cjd@B{j77UDbKb&u+S*l_Dz$^Q#HwA&Nvpio+Bde``qjxl z`_rvA-ek*u*%w(fhusL<(t$@ZN98~Hb2WF%E&lA<`J;Edzh?|{ZBTczWeS9fG2$%f zBjaS3BT;XeT0Lrb?!JF@j8Tth9p+);@uV;bWK>|kEI?i%QRk5)k2}u_WzFm`q=pBk zgWwfL#e9f{&GD+<^>y|1F*P}+Lveij+_yy4wKQ>7otil=S>KWSmNIHF8EXKKrH)slz)COWh;6E7V+C8(r5Mp~%ds0)|R> zBz(|QQxL!^2D4C0|1}o0%Hv#`+c&n``W5X>dihe9+>Q8y_x*Ow2_%_i?qG8eEeeEAVr0EwQ>8SlKqq35+cl`rj?h`(xdCHy$&k6vU;lUxO(E^kEA@4 zVPATX{EAkPQCeiBR0)WSm?%{$7PUl&5-sCRK$W1;<25BllM~vx(&0mg)r~K`(ew|M zo}GhvEo1bND*PuA*tY;gV^%$ zaW|R`Ns_P}mah#5uw&W&F-k*?uwOj;iNES8ieU=8z5Ui#JE>7%_4#7|!=HYGvnvYF zB`TMpWQ?Y6yeTU5>l&VCzVY?0_3b*24h6^__>&)VKhHwT944_>He3e%Q|mf7LhQpt zV{Q-57nvqA6Ou+#qp(Y&ZBhy?AVjXf#}Pbi<3bXhC)%;f;9PW~eKp)iii8JJB-jQq zc`%-4LT_p!JuYc9F#>Pd8A+6G)xdUI%UJ)!Ht`>cou5Ybyf%S`()GvKYz=Ofpp>_l z5GIoYw{^dlEAPhYGzC1wkPJfTRB1$T@|tVp4P= zO;z1>@}Jc=&+9mSc|?^h1;R^# zUIvS`RM_^8ciPj?9BB_Ziyc`G90dk}0-K*qR_{J z@3dx@Q-RcnvCNogQxr$I4<^wd*@ot51Wm4C=s%$W{U*tX?o~p8Xq55)6>&xc6L@>a_CY?+qO+p{v>}5=vaLiI*&mL z#Z)o#u#%^S`XRjNhqP!MM)P3=N{t*i3Jd}TLa9n4VVycn2bEoZ<{5E(IS4nKo5Ln4 zK&Hqxu^bKaOeR%nOuVedw6ZG(4~%iMLyb*sv)UQkm_ErA5oTgD>QMENoS!&xLKq`< znlgscWL7HNI!3^N6{7?5%s6xaNk)o5#weG|skz*oy7cg+lE;s_3rIVeOA| z7>FCqO~O%N&?wLkX;Ht!fULKMPHMCsgo%sXFAJxf zh?$HnfMiR^XRqzCL-yGHk`^&CK$yWvjl#S-4!4a`!2Yyuw0=+PS#?@Z2MyU^+?hw` zaSj{>f)ucJKU77@PBGan#;>$Z&D+?u;*H!zJ*DGmHJ#ihX(Bcbc4kyk6;2wNR2j?| zw(v8>t2TkdKt7XKNvUilCuuq}qvl4otLf|vSup))m^m_Z!nk2*Iifi?o@utmFmZww za(HYPEWhMGw%ey^-w$aKhYr&={nbcGsgd)By&82~e%L7B8IuA(UieuGH_ZL=uY?=H z9$@#v=RenjQQQ#2MgeO?&7;`);FC5&Gcz+{Kpm$cHxliAEY#Y7ow4P#DPkL@jF*9= zO8?cY!_n9pYTJJNN8yDG2LAl8p7%>yL}nvw+w@zl5v4}-hV8&nAPN-7?%UUdW~fzZ z#CGOYKcP-Gp*>s~^l4Oh&u_XAA;Y!oU74*gA%@Z6E;0eU9@)UNH@x%)b@hv`R>g8r zed1Gpt;@kUamO2hrqL9@kkQFx;FAb&*?}YKO;@~0&i8HGr*g3zoIFC2hk&#Mj>+9^ z1f3(NlFu{lMuXQPooPP18SJ%Tpe~O6(5n3=QL?$DH|&#O?CmWcNFM51*q2UwU=u<=_t7{p*QUPANsIG-5nhTB1M7F zON>a(*|)t-9l!5iB1PCvBkPQ?J5AB>_ooe~DWCC!R(tt#D3hU4b`at^K1f5hsLafE zdmFbbRZXh#qBh=6!ZUU;QH1GGS-tV{pOUtxPM=m!KJkQ{duiAjLA{ntCe=%>dx_*- zJ$1FZV*Cm@pNef$N!TJ0C-~5oJW7NZiiA*&<>e^nB75SQR^-_;eS{3DuEArLKmyXM zAXFf|s)$`xy@d8maH!pJIQ|EBcD&Dxb~tuv-TlEv+w`;E^o=*FXA$hSUyNiw=kCUi zHU%QV7|q=L3Uxf%bZ#4aYlO`o8wbNdfmVzWSqL%`jUdXZ#w=WMubMV(t{0Jo(8k&I zNcM?xQl*MIqz7C+idL&!Evr`@e5EvetooSB|8PmpbEz~K7VJ`kEzxU^{-osXO&m~% zl85D7hQtswrM{q+C65e_2WE;;RosE56>3^3$6Wpl>7OE4G%+K<=17)&G%DP5ei3bK z$P!yKfxJ#LMwAxi`}BfFZ`YK)B zI)eAG)7#D8QNU85yu7TQ`0f6MyqLnvVHAPJ;x1H36jyGzagJe^Nf!jpohK^a)*ie4 zXFK0<#Cp8^D_@Jm&XkCeHI(R96#WD>em$uo(o!Qo%Gd^PBrg73t+oobulqr&!oQFJ zx8vvSJCc^zRH24@WaNiZTqqUPV)3G!rxC1{zK|%`$!4|j^<;Tk&6VeMS1`DipA?iA za(ZlyvFTSWqMnu*C5*u}gT2~-uU3l0js9t<_?n?{J}fT+*K&YPiAqc*>1rSd#&=RM zd%f!u|M2(f_`Bb=UbU{3U-$hVL{efzf{>|v;B~KYhS*)ZJ6y-@P~h0NzSFd^sl_ix zJ41A@?c}jz-RnF;J)ipDUyBg74%J9dfbT}+H~c&Y&Qpn$q%7j6jgOA&G%}{fm~vyH zQlnbU+7HQGO+P&N8KNK^nwh&`jqqF-&YV#v`?M2{REVk?&90y`#I}@h$1qZW?j?c{ z8ODfTE+>*jVAjU!0RRU~4R ziXpX_5|M8Sjs5AXAc-jG+6p}Lw|~=DX-8DmXntDLHgf!JxBIi)xuZa6DG(V|8YxJ} zY7f8qme3NjSBTAj;DatqNUw<9$8o6WB6Hw}I5L`^@2sS?Q~s)|2z%yWd#_S}_iCd; zGdsmp1wFz5TY$`x1<&7F7ZfVz5kyvp07T5;AKnW>p;DRMlCMF@S785HJYEKe4+d9 zTYkE)Or{?nckU=a3S9sAlTCzZ`i*U>5uywl@;RSIN^$Ex_4W=N1snysOM!lc{;;!( zx*E;SgGZ_?_Pp;7f0jFU6bK~+BE=Yyg7j%L>>ykB@4kE3$>8R{6$;SnwN3P9Egr2Q z;-Msk2?p8KBK%t;)#@Rep0Al$B~ED$q)o{LydELZ&ZwsMLb+688}nz?XjW=OMHgjJ zW%yHKD#2j83`r4F9BMo!qS$ZXh7Wq3?
`ZfI=zT;!h-Exalsdb#HuES1KAaaZm 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 literal 0 HcmV?d00001 diff --git a/cgreen-guide-en-docinfo.html b/cgreen-guide-en-docinfo.html new file mode 100644 index 00000000..ad701941 --- /dev/null +++ b/cgreen-guide-en-docinfo.html @@ -0,0 +1,44 @@ + + + + + Thomas + Nilefalk + thomas@junovagen.se + + + + Marcus + Baker + lastcraft + marcus@lastcraft.com + + + + João + Freitas + joaohf + joaohf@gmail.com + + + + +Cgreen Unit Test for C/C++ language + + + 2009-2012 + Cgreen Team + + + +Permission to use, copy, modify and distribute. + + + +The copyright holders make no representation about the suitability of +this document for any purpose. It is provided +as is without expressed or implied warranty. + diff --git a/cgreen-guide-en-docinfo.xml b/cgreen-guide-en-docinfo.xml new file mode 100644 index 00000000..b4b52434 --- /dev/null +++ b/cgreen-guide-en-docinfo.xml @@ -0,0 +1,47 @@ + + + + + Marcus + Baker + lastcraft + marcus@lastcraft.com + + + + João + Freitas + joaohf + joaohf@gmail.com + + + + Thomas + Nilefalk + thoni56 + thomas@junovagen.se + + + + +Cgreen Unit Test for C language + + + 2012 + Cgreen Team + + + +Permission to use, copy, modify and distribute. + + + +The copyright holders make no representation about the suitability of +this document for any purpose. It is provided +as is without expressed or implied warranty. + + + diff --git a/cgreen-guide-en.html b/cgreen-guide-en.html new file mode 100644 index 00000000..3cb91410 --- /dev/null +++ b/cgreen-guide-en.html @@ -0,0 +1,6612 @@ + + + + + + + + +Cgreen : Unit Tests, Stubbing and Mocking for C and C++ + + + + + + + +
+
+

1. Cgreen Quickstart Guide

+
+
+

1.1. What is Cgreen?

+
+

Cgreen is a unit testing framework for the C and C++ software developer, a test automation and software quality assurance tool for programmers and development teams. +The tool is completely open source published under the ISC, OpenBSD, license.

+
+
+

Unit testing is a development practice popularised by the agile development community. +It is characterised by writing many small tests alongside the normal code. +Often the tests are written before the code they are testing, in a tight test-code-refactor loop. +Done this way, the practice is known as Test Driven Development. + Cgreen was designed specifically to support this style of development.

+
+
+

Unit tests are written in the same language as the code, in our case C or C++. +This avoids the mental overhead of constantly switching language, and also allows you to use any application code in your tests.

+
+
+

Here are some of its features:

+
+
+
    +
  • +

    Fluent API resulting in very readable tests

    +
  • +
  • +

    Expressive and clear output using the default reporter

    +
  • +
  • +

    Fully functional mocks, both strict, loose and learning

    +
  • +
  • +

    Mocks with side effects

    +
  • +
  • +

    Each test runs in its own process for test suite robustness

    +
  • +
  • +

    Automatic discovery and running of tests using dynamic library inspection

    +
  • +
  • +

    Extensive and expressive constraints for many datatypes

    +
  • +
  • +

    Custom constraints can be constructed by user

    +
  • +
  • +

    BDD-flavoured test declarations with Before and After declarations

    +
  • +
  • +

    Extensible reporting mechanism

    +
  • +
  • +

    Fully composable test suites

    +
  • +
  • +

    A single test can be run in a single process for easier debugging

    +
  • +
+
+
+

Cgreen also supports the classic xUnit-style assertions for easy porting from other frameworks.

+
+
+

Cgreen was initially developed to support C programming, but there is also support for C++. +It was initially a spinoff from a research project at Wordtracker and created by Marcus Baker. +Significant additions by Matt Hargett and continuous nurturing by Thomas Nilefalk has made Cgreen what it is today.

+
+
+
+

1.2. Cgreen - Vanilla or Chocolate?

+
+

Test driven development (TDD) really catched on when the JUnit framework for Java spread to other langauges, giving us a family of xUnit tools. +Cgreen was born in this wave and have many similarities to the xUnit family.

+
+
+

But TDD evolved over time and modern thinking and practice is more along the lines of BDD, an acronym for Behaviour Driven Development, made popular by people like Dan North and frameworks like JBehave, RSpec, Cucumber and Jasmine.

+
+
+

Cgreen follows this trend and has evolved to embrace a BDD-flavoured style of testing. +Although the fundamental mechanisms in TDD and 'technical' BDD are much the same, the shift in focus by changing wording from 'tests' to 'behaviour specifications' is very significant.

+
+
+

This document will present Cgreen using the more modern and better BDD-inspired style. +In a later section you can have a peek at the classic xUnit-family TDD API, but you should consider that as outdated.

+
+
+
+

1.3. Installing Cgreen

+
+

There are two ways to install Cgreen in your system.

+
+
+

1.3.1. Installing a package

+
+

The first way is to use packages provided by the Cgreen Team and porters for the various operating systems. +If your system uses a package manager ('apt', 'yum', 'brew' and so on) there might be a prebuilt package that you can just install using your systems package manager.

+
+
+

If no Cgreen package is distributed for your system you can download a package from Cgreen GitHub project. +Install it using the normal procedures for your system.

+
+
+ + + + + +
+ + +At this point there are pre-built packages available for quite a few environments. +They are not all using the latest version, though. +If you need that, you can still build from source. +
+
+
+
+

1.3.2. Installing from source

+
+

A second way is available for developers and advanced users. +Basically this consists of fetching the sources of the project on GitHub, just click on "Download ZIP", and then compile them. +To do this you need the CMake build system.

+
+
+

Once you have the CMake tool installed, the steps are:

+
+
+
+
$ unzip cgreen-master.zip
+$ cd cgreen-master
+$ make
+$ make test
+$ make install
+
+
+
+

The initial make command will configure the build process and create a separate build directory before going there and building using CMake. +This is called an 'out of source build'. +It compiles Cgreen from outside the sources directory. +This helps the overall file organization and enables multi-target builds from the same sources by leaving the complete source tree untouched.

+
+
+ + + + + +
+ + +Experienced users may tweak the build configuration by going to the build subdirectory and use ccmake .. to modify the build configuration in that subtree. +
+
+
+ + + + + +
+ + +The Makefile is just there for convenience, it creates the build directory and invokes CMake there, so that you don’t have to. +This means that experienced CMake users can just do as they normally do with a CMake-based project instead of invoking make. +
+
+
+

The build process will create a library (on unix called libcgreen.so) which can be used in conjunction with the cgreen.h header file to compile and link your test code. +The created library is installed in the system directories, by default in /usr/local/lib/.

+
+
+
+

1.3.3. Your First Test

+
+

We will start demonstrating the use of Cgreen by writing some tests for Cgreen itself to confirm that everything is working as it should. +Let’s start with a simple test module with no tests, called first_test.c…​

+
+
+
+
#include <cgreen/cgreen.h>
+
+Describe(Cgreen);
+BeforeEach(Cgreen) {}
+AfterEach(Cgreen) {}
+
+int main(int argc, char **argv) {
+    TestSuite *suite = create_test_suite();
+    return run_test_suite(suite, create_text_reporter());
+}
+
+
+
+

This is very unexciting. +It just creates an empty test suite and runs it. +It’s usually easier to proceed in small steps, and this is the smallest one I could think of. +The only complication is the cgreen.h header file and the mysterious looking "declarations" at the beginning of the file.

+
+
+

The BDD flavoured Cgreen notation calls for a System Under Test +(SUT), or a 'context'. +The declarations give a context to the tests and it also makes it more natural to talk about which module or class, the system under test, is actually responsible for the functionality we are describing. +In one way we are 'describing', or spec’ing, the functionality of the SUT. +That’s what the Describe(); does. +And for technical reasons (actually requirements of the C language), you must declare the BeforeEach() and AfterEach() functions even if they are empty. +(You will get strange errors if you don’t!)

+
+
+ + + + + +
+ + +We are using the name "Cgreen" as the SUT in these first examples, as Cgreen itself is the object or class we want to test or describe. +
+
+
+

I am assuming you have the Cgreen folder in the include search path to ensure compilation works, otherwise you’ll need to add that in the compilation command.

+
+
+

Then, building this test is, of course, trivial…​

+
+
+
+
$ gcc -c first_test.c
+$ gcc first_test.o -lcgreen -o first_test
+$ ./first_test
+
+
+
+

Invoking the executable should give…​

+
+
+
+
Running "main" (0 tests)...
+Completed "main": No assertions.
+
+
+
+

All of the above rather assumes you are working in a Unix like environment, probably with 'gcc'. +The code is pretty much standard C99, so any C compiler should work. +Cgreen should compile on all systems that support the sys/msg.h messaging library. +It has been tested on Linux, MacOSX, Cygwin. +If you are on Windows we would be glad if you could figure out how to build there.

+
+
+

So far we have tried compilation, and shown that the test suite actually runs. +Let’s add a meaningless test or two so that you can see how it runs…​

+
+
+
+
#include <cgreen/cgreen.h>
+
+Describe(Cgreen);
+BeforeEach(Cgreen) {}
+AfterEach(Cgreen) {}
+
+Ensure(Cgreen, passes_this_test) {
+    assert_that(1 == 1);
+}
+
+Ensure(Cgreen, fails_this_test) {
+    assert_that(0 == 1);
+}
+
+int main(int argc, char **argv) {
+    TestSuite *suite = create_test_suite();
+    add_test_with_context(suite, Cgreen, passes_this_test);
+    add_test_with_context(suite, Cgreen, fails_this_test);
+    return run_test_suite(suite, create_text_reporter());
+}
+
+
+
+

A test is denoted by the macro Ensure which takes an optional context (Cgreen) and a, hopefully descriptive, testname (passes_this_test). +You add the test to your suite using add_test_with_context().

+
+
+

On compiling and running, we now get the output…​

+
+
+
+
Running "main" (2 tests)...
+first_tests.c:12: Failure: fails_this_test 
+	Expected [0 == 1] to [be true]
+
+  "main": 1 pass, 1 failure in 42ms.
+Completed "main": 1 pass, 1 failure in 42ms.
+
+
+
+

The TextReporter, created by the call to create_text_reporter(), is the easiest way to output the test results. +It prints the failures as intelligent and expressive text messages on your console.

+
+
+

Of course "0" would never equal "1", but this shows that Cgreen presents the value you expect ([be true]) and the expression that you want to assert ([0 == 1]). +We can also see a handy short form for asserting boolean expressions (assert_that(0 == 1);).

+
+
+
+
+

1.4. Five Minutes Doing TDD with Cgreen

+
+

For a more realistic example we need something to test. +We’ll pretend that we are writing a function to split the words of a sentence in place. +It would do this by replacing any spaces with string terminators and returns the number of conversions plus one. +Here is an example of what we have in mind…​

+
+
+
+
char *sentence = strdup("Just the first test");
+word_count = split_words(sentence);
+
+
+
+

The variable sentence should now point at "Just\0the\0first\0test". +Not an obviously useful function, but we’ll be using it for something more practical later.

+
+
+

This time around we’ll add a little more structure to our tests. +Rather than having the test as a stand alone program, we’ll separate the runner from the test cases. +That way, multiple test suites of test cases can be included in the main() runner file. +This makes it less work to add more tests later.

+
+
+

Here is the, so far empty, test case in words_test.c…​

+
+
+
+
#include <cgreen/cgreen.h>
+#include <cgreen/mocks.h>
+
+#include "words.h"
+#include <string.h>
+
+Describe(Words);
+BeforeEach(Words) {}
+AfterEach(Words) {}
+
+TestSuite *words_tests() {
+    TestSuite *suite = create_test_suite();
+    return suite;
+}
+
+
+
+

Here is the all_tests.c test runner…​

+
+
+
+
#include <cgreen/cgreen.h>
+
+TestSuite *words_tests();
+
+int main(int argc, char **argv) {
+    TestSuite *suite = create_test_suite();
+    add_suite(suite, words_tests());
+    if (argc > 1) {
+        return run_single_test(suite, argv[1], create_text_reporter());
+    }
+    return run_test_suite(suite, create_text_reporter());
+}
+
+
+
+

Cgreen has two ways of running tests. +The default is to run all tests in their own protected processes. +This is what happens if you invoke run_test_suite(). +All tests are then completely independent since they run in separate processes, preventing a single run-away test from bringing the whole program down with it. +It also ensures that one test cannot leave any state to the next, thus forcing you to setup the prerequisites for each test correctly and clearly.

+
+
+

Building this scaffolding…​

+
+
+
+
$ gcc -c words_test.c
+$ gcc -c all_tests.c
+$ gcc words_test.o all_tests.o -lcgreen -o all_tests
+
+
+
+

…​and executing the result gives the familiar…​

+
+
+
+
Running "main" (0 tests)...
+  "words_tests": No assertions.
+Completed "main": No assertions.
+
+
+
+

Note that we get an extra level of output here, we have both main and words_tests. +That’s because all_tests.c adds the words test suite to its own (named main since it was created in the function main()). +All this scaffolding is pure overhead, but from now on adding tests will be a lot easier.

+
+
+

Here is a first test for split_words() in words_test.c…​

+
+
+
+
#include <cgreen/cgreen.h>
+
+#include "words.h"
+#include <string.h>
+
+Describe(Words);
+BeforeEach(Words) {}
+AfterEach(Words) {}
+
+Ensure(Words, returns_word_count) {
+    char *sentence = strdup("Birds of a feather");
+    int word_count = split_words(sentence);
+    assert_that(word_count, is_equal_to(4));
+    free(sentence);
+}
+
+TestSuite *words_tests() {
+    TestSuite *suite = create_test_suite();
+    add_test_with_context(suite, Words, returns_word_count);
+    return suite;
+}
+
+
+
+

The assert_that() macro takes two parameters, the value to assert and a constraint. +The constraints comes in various forms. +In this case we use the probably most common, is_equal_to(). +With the default TextReporter the message is sent to STDOUT.

+
+
+

To get this to compile we need to create the words.h header file…​

+
+
+
+
int split_words(char *sentence);
+
+
+
+

…​and to get the code to link we need a stub function in words.c…​

+
+
+
+
int split_words(char *sentence) {
+    return 0;
+}
+
+
+
+

A full build later…​

+
+
+
+
$ gcc -c all_tests.c
+$ gcc -c words_test.c
+$ gcc -c words.c
+$ gcc all_tests.o words_test.o words.o -lcgreen -o all_tests
+$ ./all_tests
+
+
+
+

…​and we get the more useful response…​

+
+
+
+
Running "main" (1 test)...
+words_tests.c:13: Failure: words_tests -> returns_word_count 
+	Expected [word_count] to [equal] [4]
+		actual value:			[0]
+		expected value:			[4]
+
+  "words_tests": 1 failure in 42ms.
+Completed "main": 1 failure in 42ms.
+
+
+
+

The breadcrumb trail following the "Failure" text is the nesting of the tests. +It goes from the test suites, which can be nested in each other, through the test function, and finally to the message from the assertion. +In the language of Cgreen, a "failure" is a mismatched assertion, or constraint, and an "exception" occurs when a test fails to complete for any reason, e.g. a segmentation fault.

+
+
+

We could get this to pass just by returning the value 4. +Doing TDD in really small steps, you would actually do this, but we’re not teaching TDD here. +Instead we’ll go straight to the core of the implementation…​

+
+
+
+
#include <string.h>
+
+int split_words(char *sentence) {
+  int i, count = 1;
+  for (i = 0; i < strlen(sentence); i++) {
+    if (sentence[i] == ' ') {
+      count++;
+    }
+  }
+  return count;
+}
+
+
+
+

Running it gives…​

+
+
+
+
Running "main" (1 test)...
+  "words_tests": 1 pass in 42ms.
+Completed "main": 1 pass in 42ms.
+
+
+
+

There is actually a hidden problem here, but our tests still passed so we’ll pretend we didn’t notice.

+
+
+

So it’s time to add another test. +We want to confirm that the string is broken into separate words…​

+
+
+
+
...
+Ensure(Words, returns_word_count) {
+    ...
+}
+
+Ensure(Words, converts_spaces_to_zeroes) {
+    char *sentence = strdup("Birds of a feather");
+    split_words(sentence);
+    int comparison = memcmp("Birds\0of\0a\0feather", sentence, strlen(sentence));
+    assert_that(comparison, is_equal_to(0));
+    free(sentence); 
+}
+
+
+
+

Sure enough, we get a failure…​

+
+
+
+
Running "main" (2 tests)...
+words_tests.c:21: Failure: words_tests -> converts_spaces_to_zeroes 
+	Expected [comparison] to [equal] [0]
+		actual value:			[-32]
+		expected value:			[0]
+
+  "words_tests": 1 pass, 1 failure in 42ms.
+Completed "main": 1 pass, 1 failure in 42ms.
+
+
+
+

Not surprising given that we haven’t written the code yet.

+
+
+

The fix…​

+
+
+
+
#include <string.h>
+
+int split_words(char *sentence) {
+  int i, count = 1;
+  for (i = 0; i < strlen(sentence); i++) {
+    if (sentence[i] == ' ') {
+      sentence[i] = '\0';
+      count++;
+    }
+  }
+  return count;
+}
+
+
+
+

…​reveals our previous hack…​

+
+
+
+
Running "main" (2 tests)...
+words_tests.c:13: Failure: words_tests -> returns_word_count 
+	Expected [word_count] to [equal] [4]
+		actual value:			[2]
+		expected value:			[4]
+
+  "words_tests": 1 pass, 1 failure in 42ms.
+Completed "main": 1 pass, 1 failure in 42ms.
+
+
+
+

Our earlier test now fails, because we have affected the strlen() call in our loop. +Moving the length calculation out of the loop…​

+
+
+
+
int split_words(char *sentence) {
+  int i, count = 1, length = strlen(sentence);
+  for (i = 0; i < length; i++) {
+    ...
+  }
+  return count;
+}
+
+
+
+

…​restores order…​

+
+
+
+
Running "main" (2 tests)...
+  "words_tests": 2 passes in 42ms.
+Completed "main": 2 passes in 42ms.
+
+
+
+

It’s nice to keep the code under control while we are actually writing it, rather than debugging later when things are more complicated.

+
+
+

That was pretty straight forward. +Let’s do something more interesting.

+
+
+
+

1.5. What are Mock Functions?

+
+

The next example is a more realistic extension of our previous attempts. +As in real life we first implement something basic and then we go for the functionality that we need. +In this case a function that invokes a callback for each word found in a sentence. +Something like…​

+
+
+
+
void act_on_word(const char *word, void *memo) { ... }
+words("This is a sentence", &act_on_word, &memo);
+
+
+
+

Here the memo pointer is just some accumulated data that the act_on_word() callback might work with. +Other people will write the act_on_word() function and probably many other functions like it. +The callback is actually a flex point, and not of interest right now.

+
+
+

The function under test is the words() function and we want to make sure it walks the sentence correctly, dispatching individual words as it goes. +So what calls are made are very important. +How do we go about to test this?

+
+
+

Let’s start with a one word sentence. +In this case we would expect the callback to be invoked once with the only word, right? +Here is the test for that…​

+
+
+
+
#include <cgreen/cgreen.h>
+#include <cgreen/mocks.h>
+...
+void mocked_callback(const char *word, void *memo) {
+    mock(word, memo);
+}
+
+Ensure(Words, invokes_callback_once_for_single_word_sentence) {
+    expect(mocked_callback,
+           when(word, is_equal_to_string("Word")), when(memo, is_null));
+    words("Word", &mocked_callback, NULL);
+}
+
+TestSuite *words_tests() {
+    TestSuite *suite = create_test_suite();
+    ...
+    add_test_with_context(suite, Words, invokes_callback_once_for_single_word_sentence);
+    return suite;
+}
+
+
+
+

What is the funny looking mock() function?

+
+
+

A mock is basically a programmable object. +In C objects are limited to functions, so this is a mock function. +The macro mock() compares the incoming parameters with any expected values and dispatches messages to the test suite if there is a mismatch. +It also returns any values that have been preprogrammed in the test.

+
+
+

The test is invokes_callback_once_for_single_word_sentence(). +It programs the mock function using the expect() macro. +It expects a single call, and that single call should use the parameters "Word" and NULL. +If they don’t match, we will get a test failure.

+
+
+

So when the code under test (our words() function) calls the injected mocked_callback() it in turn will call mock() with the actual parameters.

+
+
+

Of course, we don’t add the mock callback to the test suite, it’s not a test.

+
+
+

For a successful compile and link, the words.h file must now look like…​

+
+
+
+
int split_words(char *sentence);
+void words(const char *sentence, void (*callback)(const char *, void *), void *memo);
+
+
+
+

…​and the words.c file should have the stub…​

+
+
+
+
void words(const char *sentence, void (*callback)(const char *, void *), void *memo) {
+}
+
+
+
+

This gives us the expected failing test…​

+
+
+
+
Running "main" (3 tests)...
+words_tests.c:32: Failure: words_tests -> invokes_callback_once_for_single_word_sentence 
+	Expected call was not made to mocked function [mocked_callback]
+
+  "words_tests": 2 passes, 1 failure in 42ms.
+Completed "main": 2 passes, 1 failure in 42ms.
+
+
+
+

Cgreen reports that the callback was never invoked. +We can easily get the test to pass by filling out the implementation with…​

+
+
+
+
void words(const char *sentence, void (*callback)(const char *, void *), void *memo) {
+  (*callback)(sentence, memo);
+}
+
+
+
+

That is, we just invoke it once with the whole string. +This is a temporary measure to get us moving. +For now everything should pass, although it doesn’t drive much functionality yet.

+
+
+
+
Running "main" (3 tests)...
+  "words_tests": 4 passes in 42ms.
+Completed "main": 4 passes in 42ms.
+
+
+
+

That was all pretty conventional, but let’s tackle the trickier case of actually splitting the sentence. +Here is the test function we will add to words_test.c…​

+
+
+
+
Ensure(Words, invokes_callback_for_each_word_in_a_phrase) {
+    expect(mocked_callback, when(word, is_equal_to_string("Birds")));
+    expect(mocked_callback, when(word, is_equal_to_string("of")));
+    expect(mocked_callback, when(word, is_equal_to_string("a")));
+    expect(mocked_callback, when(word, is_equal_to_string("feather")));
+    words("Birds of a feather", &mocked_callback, NULL);
+}
+
+
+
+

Each call is expected in sequence. +Any failures, or left-over or extra calls, and we get failures. +We can see all this when we run the tests…​

+
+
+
+
Running "main" (4 tests)...
+words_tests.c:38: Failure: words_tests -> invokes_callback_for_each_word_in_a_phrase 
+	Expected [[word] parameter in [mocked_callback]] to [equal string] ["Birds"]
+		actual value:			["Birds of a feather"]
+		expected to equal:		["Birds"]
+
+words_tests.c:39: Failure: words_tests -> invokes_callback_for_each_word_in_a_phrase 
+	Expected call was not made to mocked function [mocked_callback]
+
+words_tests.c:40: Failure: words_tests -> invokes_callback_for_each_word_in_a_phrase 
+	Expected call was not made to mocked function [mocked_callback]
+
+words_tests.c:41: Failure: words_tests -> invokes_callback_for_each_word_in_a_phrase 
+	Expected call was not made to mocked function [mocked_callback]
+
+  "words_tests": 4 passes, 4 failures in 42ms.
+Completed "main": 4 passes, 4 failures in 42ms.
+
+
+
+

The first failure tells the story. +Our little words() function called the mock callback with the entire sentence. +This makes sense, because that was the hack we did to get to the next test.

+
+
+

Although not relevant to this guide, I cannot resist getting these tests to pass. +Besides, we get to use the function we created earlier…​

+
+
+
+
void words(const char *sentence, void (*callback)(const char *, void *), void *memo) {
+  char *words = strdup(sentence);
+  int word_count = split_words(words);
+  char *word = words;
+  while (word_count-- > 0) {
+    (*callback)(word, memo);
+    word = word + strlen(word) + 1;
+  }
+  free(words);
+}
+
+
+
+

And with some work we are rewarded with…​

+
+
+
+
Running "main" (4 tests)...
+  "words_tests": 8 passes in 42ms.
+Completed "main": 8 passes in 42ms.
+
+
+
+

More work than I like to admit as it took me three goes to get this right. +I firstly forgot the + 1 added on to strlen(), then forgot to swap sentence for word in the (*callback)() call, and finally third time lucky. +Of course running the tests each time made these mistakes very obvious. +It’s taken me far longer to write these paragraphs than it has to write the code.

+
+
+
+
+
+

2. Building Cgreen test suites

+
+
+

Cgreen is a tool for building unit tests in the C or C++ languages. +These are usually written alongside the production code by the programmer to prevent bugs. +Even though the test suites are created by software developers, they are intended to be human readable C code, as part of their function is an executable specification. +Used in this way, the test harness delivers constant quality assurance.

+
+
+

In other words you’ll get less bugs.

+
+
+

2.1. Writing Basic Tests

+
+

Cgreen tests are like C, or C++, functions with no parameters and no return value. +To signal that they actually are tests we mark them with the Ensure macro. +Here’s an example…​

+
+
+
+
Ensure(Strlen, returns_five_for_hello) {
+    assert_that(strlen("Hello"), is_equal_to(5));
+}
+
+
+
+

The Ensure macro takes two arguments (in the BDD style) where the first is the System Under Test (SUT) which must be declared with the Describe macro.

+
+
+
+
Describe(Strlen);
+
+
+
+

The second argument is the test name and can be anything you want as long as it fullfills the rules for an identifier in C and C++. +A typical way to choose the named of the tests is what we see here, reading the declaration of the test makes sense since it is almost plain english, "Ensure strlen returns five for 'hello'". +No problem understanding what we aim to test, or in TDD lingo, test drive. +And it can be viewed as an example from a description of what strlen should be able to do. +In a way, extracting all the Ensure:s from your test might give you all the documentation you’ll need.

+
+
+

The call to assert_that() is the primary part of an assertion, which is complemented with a constraint, in this case +is_equal_to(), as a parameter. +This makes a very fluent interface to the asserts, that actually reads like English. +The general format is then

+
+
+
+
assert_that(actual, <constraint>);
+
+
+
+ + + + + +
+ + +Sometimes you just want to fail the test explicitly, and there is a function for that too, fail_test(const char *message). +And there is a function to explicitly pass, pass_test(void). +
+
+
+

Assertions send messages to Cgreen, which in turn outputs the results.

+
+
+
+

2.2. The Standard Constraints

+
+

Here are the standard constraints…​

+
+ ++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Constraint

Passes if actual value/expression…​

Basic

is_true

evaluates to true, buy you can also just leave out the constraint, + e.g. assert_that(found) if found is of boolean type

is_false

evaluates to false

is_null

equals null

is_non_null

is a non null value

is_not_null

d:o

Integer compatible

is_equal_to(value)

'== value'

is_equal_to_hex(value)

'== value', but will show values in HEX

is_not_equal_to(value)

'!= value'

is_greater_than(value)

'> value'

is_less_than(value)

'< value'

Structs and general data

is_equal_to_contents_of(pointer, size)

matches the data pointed + to by pointer to a size + of size bytes

is_not_equal_to_contents_of(pointer, size)

does not match the data + pointed to by pointer + to a size of size bytes

Strings

is_equal_to_string(value)

are equal when compared using strcmp()

is_not_equal_to_string(value)

are not equal when compared using strcmp()

contains_string(value)

contains value when evaluated using strstr()

does_not_contain_string(value)

does not contain value when evaluated + using strstr()

begins_with_string(value)

starts with the string value

does_not_begin_with_string(value)

does not start with the string value

ends_with_string(value)

ends with the string value

does_not_end_with_string(value)

does not end with the string value

Double floats

is_equal_to_double(value)

are equal to value within the number of + significant digits (which you can set with a call + to significant_figures_for_assert_double_are(int figures))

is_not_equal_to_double(value)

are not equal to value within the number of + significant digits

is_less_than_double(value)

< value withing the number of significant digits

is_greater_than_double(value)

> value within the number of significant digits

+
+

The boolean assertion macros accept an int value. +The equality assertions accept anything that can be cast to intptr_t and simply perform an == operation. +The string comparisons are slightly different in that they use the <string.h> library function strcmp(). +If you use is_equal_to() with char * pointers then it is the value of the pointers themselves that has to be the same, i.e. the pointers have to point at the same string for the test to pass.

+
+
+

The constraints above should be used as the second argument to one of the assertion functions:

+
+ ++++ + + + + + + + + + + + + + + +

Assertion

Description

assert_that(expected, constraint)

Passes if expected fullfulls constraint, + to be used for all assertions except double + type values

assert_that_double(expected, constraint)

Passes if expected fullfulls constraint, + only to be used for assertions on double + type values

+
+ + + + + +
+ + +You cannot use C/C++ string literal concatenation (like "don’t" "use" "string" "concatenation") in the parameters to the constraints. +If you do, you will get weird error messages about missing arguments to the constraint macros. +This is caused by the macros using argument strings to produce nice failure messages. +
+
+
+
+

2.3. Asserting C++ Exceptions

+
+

When you use CGreen with C++ there is one extra assertion available:

+
+ ++++ + + + + + + + + + + +

Assertion

Description

assert_throws(exception, expression)

Passes if evaluating expression throws exception

+
+
+

2.4. BDD Style vs. TDD Style

+
+

So far we have encouraged the modern BDD style. +It has merits that we really want you to benefit from. +But you might come across Cgreen test in another style, more like the standard TDD style, which is more inline with previous thinking and might be more similar to other frameworks.

+
+
+

The only difference, in principle, is the use of the SUT or 'context'. +In the BDD style you have it, in the TDD style you don’t.

+
+
+
BDD style:
+
+
Describe(Strlen);                                                 (1)
+BeforeEach(Strlen) {}                                             (2)
+AfterEach(Strlen) {}                                              (3)
+
+Ensure(Strlen, returns_five_for_hello) {                          (4)
+    assert_that(strlen("Hello"), is_equal_to(5));
+}
+
+TestSuite *our_tests() {
+    TestSuite *suite = create_test_suite();
+    add_test_with_context(suite, Strlen, returns_five_for_hello); (5)
+    return suite;
+}
+
+
+
+ + + + + + + + + + + + + + + + + + + + + +
1The Describe macro must name the SUT
2The BeforeEach function…​
3…​ and the AfterEach functions must exist and name the SUT
4The test need to name the SUT
5Adding to the test suite
+
+
+ + + + + +
+ + +You can only have tests for a single SUT in the same source file. +
+
+
+

If you use the older pure-TDD style you skip the Describe macro, the BeforeEach and AfterEach functions. +You don’t need a SUT in the Ensure() macro or when you add the test to the suite.

+
+
+
TDD style:
+
+
                                                               (1)
+Ensure(strlen_returns_five_for_hello) {                        (2)
+    assert_that(strlen("Hello"), is_equal_to(5));
+}
+
+TestSuite *our_tests() {
+    TestSuite *suite = create_test_suite();
+    add_test(suite, strlen_returns_five_for_hello);            (3)
+    return suite;
+}
+
+
+
+ + + + + + + + + + + + + +
1No Describe, BeforeEach() or AfterEach()
2No SUT/context in the Ensure() macro
3No SUT/context in add_test() and you should use this function instead +of ..with_context().
+
+
+ + + + + +
+ + +You might think of the TDD style as the BDD style with a default SUT or context. +
+
+
+
+

2.5. A Runner

+
+

The tests are only run by running a test suite in some form. +(But see also Using the Runner.) +We can create and run one especially for this test like so…​

+
+
+
+
TestSuite *our_tests() {
+    TestSuite *suite = create_test_suite();
+    add_test_with_context(suite, Strlen, returns_five_for_hello);
+    return suite;
+}
+
+
+
+

In case you have spotted that the reference to returns_five_for_hello should have an ampersand in front of it, add_test_with_context() is actually a macro. +The & is added automatically. +Further more, the Ensure()-macro actually mangles the tests name, so it is not actually a function name. +(This might also make them a bit difficult to find in the debugger…​.)

+
+
+

To run the test suite, we call run_test_suite() on it. +So we can just write…​

+
+
+
+
    return run_test_suite(our_tests(), create_text_reporter());
+
+
+
+

The results of assertions are ultimately delivered as passes and failures to a collection of callbacks defined in a TestReporter structure. +There is a predefined TestReporter in Cgreen called the TextReporter that delivers messages in plain text like we have already seen.

+
+
+

The return value of run_test_suite() is a standard C library/Unix exit code that can be returned directly by the main() function.

+
+
+

The complete test code now looks like…​

+
+
+
+
#include <cgreen/cgreen.h>
+#include <string.h>
+
+Describe(Strlen);
+BeforeEach(Strlen) {}
+AfterEach(Strlen) {}
+
+Ensure(Strlen, returns_five_for_hello) {
+    assert_that(strlen("Hello"), is_equal_to(5));
+}
+
+TestSuite *our_tests() {
+    TestSuite *suite = create_test_suite();
+    add_test_with_context(suite, Strlen, returns_five_for_hello);
+    return suite;
+}
+
+int main(int argc, char **argv) {
+    return run_test_suite(our_tests(), create_text_reporter());
+}
+
+
+
+

Compiling and running gives…​

+
+
+
+
$ gcc -c strlen_test.c
+$ gcc strlen_test.o -lcgreen -o strlen_test
+$ ./strlen_test
+Running "our_tests" (1 test)...
+  "our_tests": 1 pass in 42ms.
+Completed "our_tests": 1 pass in 42ms.
+
+
+
+

We can see that the outer test suite is called our_tests since it was in our_tests() we created the test suite. +There are no messages shown unless there are failures. +So, let’s break our test to see it…​

+
+
+
+
Ensure(Strlen, returns_five_for_hello) {
+    assert_that(strlen("Hiya"), is_equal_to(5));
+}
+
+
+
+

…​we’ll get the helpful message…​

+
+
+
+
Running "our_tests" (1 test)...
+strlen_tests.c:9: Failure: returns_five_for_hello 
+	Expected [strlen("Hiya")] to [equal] [5]
+		actual value:			[4]
+		expected value:			[5]
+
+  "our_tests": 1 failure in 42ms.
+Completed "our_tests": 1 failure in 42ms.
+
+
+
+

Cgreen starts every message with the location of the test failure so that the usual error message identifying tools (like Emacs’s next-error) will work out of the box.

+
+
+

Once we have a basic test scaffold up, it’s pretty easy to add more tests. +Adding a test of strlen() with an empty string for example…​

+
+
+
+
...
+Ensure(Strlen, returns_zero_for_empty_string) {
+    assert_equal(strlen("\0"), 0);
+}
+
+TestSuite *our_tests() {
+    TestSuite *suite = create_test_suite();
+    add_test_with_context(suite, Strlen, returns_five_for_hello);
+    add_test_with_context(suite, Strlen, returns_zero_for_empty_string);
+    return suite;
+}
+...
+
+
+
+

And so on.

+
+
+
+

2.6. BeforeEach and AfterEach

+
+

It’s common for test suites to have a lot of duplicate code, especially when setting up similar tests. +Take this database code for example…​

+
+
+
+
#include <cgreen/cgreen.h>
+#include <stdlib.h>
+#include <mysql.h>
+#include "person.h"
+
+Describe(Person);
+BeforeEach(Person) {}
+AfterEach(Person) {}
+
+static void create_schema() {
+    MYSQL *connection = mysql_init(NULL);
+    mysql_real_connect(connection, "localhost", "me", "secret", "test", 0, NULL, 0);
+    mysql_query(connection, "create table people (name, varchar(255) unique)");
+    mysql_close(connection);
+}
+
+static void drop_schema() {
+    MYSQL *connection = mysql_init(NULL);
+    mysql_real_connect(connection, "localhost", "me", "secret", "test", 0, NULL, 0);
+    mysql_query(connection, "drop table people");
+    mysql_close(connection);
+}
+
+Ensure(Person, can_add_person_to_database) {
+    create_schema();
+    Person *person = create_person();
+    set_person_name(person, "Fred");
+    save_person(person);
+    Person *found = find_person_by_name("Fred");
+    assert_that(get_person_name(found), is_equal_to_string("Fred"));
+    drop_schema();
+}
+
+Ensure(Person, cannot_add_duplicate_person) {
+    create_schema();
+    Person *person = create_person();
+    set_person_name(person, "Fred");
+    assert_that(save_person(person), is_true);
+    Person *duplicate = create_person();
+    set_person_name(duplicate, "Fred");
+    assert_that(save_person(duplicate), is_false);
+    drop_schema();
+}
+
+TestSuite *person_tests() {
+    TestSuite *suite = create_test_suite();
+    add_test_with_context(suite, Person, can_add_person_to_database);
+    add_test_with_context(suite, Person, cannot_add_duplicate_person);
+    return suite;
+}
+
+int main(int argc, char **argv) {
+    return run_test_suite(person_tests(), create_text_reporter());
+}
+
+
+
+

We have already factored out the duplicate code into its own functions create_schema() and drop_schema(), so things are not so bad. +At least not yet. +But what happens when we get dozens of tests? +For a test subject as complicated as a database ActiveRecord, having dozens of tests is very likely.

+
+
+

We can get Cgreen to do some of the work for us by calling these methods before and after each test in the test suite.

+
+
+

Here is the new version…​

+
+
+
+
...
+static void create_schema() {
+    ...
+}
+
+static void drop_schema() {
+    ...
+}
+
+Describe(Person);
+BeforeEach(Person) { create_schema(); }
+AfterEach(Person) { drop_schema(); }
+
+Ensure(Person, can_add_person_to_database) {
+    Person *person = create_person();
+    set_person_name(person, "Fred");
+    save_person(person);
+    Person *found = find_person_by_name("Fred");
+    assert_that(get_person_name(found), is_equal_to_string("Fred"));
+}
+
+Ensure(Person, cannot_add_duplicate_person) {
+    Person *person = create_person();
+    set_person_name(person, "Fred");
+    assert_that(save_person(person), is_true);
+    Person *duplicate = create_person();
+    set_person_name(duplicate, "Fred");
+    assert_that(save_person(duplicate), is_false);
+}
+
+TestSuite *person_tests() {
+...
+
+
+
+

With this new arrangement Cgreen runs the create_schema() function before each test, and the drop_schema() function after each test. +This saves some repetitive typing and reduces the chance of accidents. +It also makes the tests more focused.

+
+
+

The reason we try so hard to strip everything out of the test functions is the fact that the test suite acts as documentation. +In our person.h example we can easily see that Person has some kind of name property, and that this value must be unique. +For the tests to act like a readable specification we have to remove as much mechanical clutter as we can.

+
+
+

In this particular case there are more lines that we could move from the tests to BeforeEach():

+
+
+
+
    Person *person = create_person();
+    set_person_name(person, "Fred");
+
+
+
+

Of course that would require an extra variable, and it might make the tests less clear. +And as we add more tests, it might turn out to not be common to all tests. +This is a typical judgement call that you often get to make with BeforeEach() and AfterEach().

+
+
+ + + + + +
+ + +If you use the pure-TDD notation, not having the test subject named by the Describe macro, you can’t have the BeforeEach() and AfterEach() either. +In this case you can still run a function before and after every test. +Just nominate any void(void) function by calling the function set_setup() and/or set_teardown() with the suite and the function that you want to run before/after each test. +In the example above that would be set_setup(suite, create_schema); and set_teardown(suite, drop_schema);. +
+
+
+

A couple of details. +There is only one BeforeEach() and one AfterEach() allowed in each TestSuite. +Also, the AfterEach() function might not be run if the test crashes, causing some test interference. +This brings us nicely onto the next section…​

+
+
+
+

2.7. Each Test in its Own Process

+
+

Consider this test method…​

+
+
+
+
Ensure(CrashExample, seg_faults_for_null_dereference) {
+    int *p = NULL;
+    (*p)++;
+}
+
+
+
+

Crashes are not something you would normally want to have in a test run. +Not least because it will stop you receiving the very test output you need to tackle the problem.

+
+
+

To prevent segmentation faults and other problems bringing down the test suites, Cgreen runs every test in its own process.

+
+
+

Just before calling the BeforeEach() (or setup) function, Cgreen fork():s. +The main process waits for the test to complete normally or die. +This includes calling the AfterEach()(or teardown) function, if any. +If the test process dies, an exception is reported and the main test process carries on with the next test.

+
+
+

For example…​

+
+
+
+
#include <cgreen/cgreen.h>
+#include <stdlib.h>
+
+Describe(CrashExample);
+BeforeEach(CrashExample) {}
+AfterEach(CrashExample) {}
+
+Ensure(CrashExample, seg_faults_for_null_dereference) {
+    int *p = NULL;
+    (*p)++;
+}
+
+int main(int argc, char **argv) {
+    TestSuite *suite = create_test_suite();
+    add_test_with_context(suite, CrashExample, seg_faults_for_null_dereference);
+    return run_test_suite(suite, create_text_reporter());
+}
+
+
+
+

When built and run, this gives…​

+
+
+
+
Running "main" (1 test)...
+crash_tests.c:8: Exception: seg_faults_for_null_dereference 
+	Test terminated with signal: Segmentation fault
+
+  "main": 1 exception in 42ms.
+Completed "main": 1 exception in 42ms.
+
+
+
+

The normal thing to do in this situation is to fire up the debugger. +Unfortunately, the constant fork():ing of Cgreen can be one extra complication too many when debugging. +It’s enough of a problem to find the bug.

+
+
+

To get around this, and also to allow the running of one test at a time, Cgreen has the run_single_test() function. +The signatures of the two run methods are…​

+
+
+
    +
  • +

    int run_test_suite(TestSuite *suite, TestReporter *reporter);

    +
  • +
  • +

    int run_single_test(TestSuite *suite, char *test, TestReporter *reporter);

    +
  • +
+
+
+

The extra parameter of run_single_test(), the test string, is the name of the test to select. +This could be any test, even in nested test suites (see below). +Here is how we would use it to debug our crashing test…​

+
+
+
+
int main(int argc, char **argv) {
+    TestSuite *suite = create_test_suite();
+    add_test_with_context(suite, CrashExample, seg_faults_for_null_dereference);
+    return run_single_test(suite, "seg_faults_for_null_dereference", create_text_reporter());
+}
+
+
+
+

When run in this way, Cgreen will not fork(). +But see the section on Debugging Cgreen tests.

+
+
+ + + + + +
+ + +The function run() is a good place to place a breakpoint. +
+
+
+

The following is a typical session:

+
+
+
+
$ gdb crash2
+...
+(gdb) break main
+(gdb) run
+...
+(gdb) break run
+(gdb) continue
+...
+Running "main" (1 tests)...
+
+Breakpoint 2, run_the_test_code (suite=suite@entry=0x2003abb0,
+    spec=spec@entry=0x402020 <CgreenSpec__CrashExample__seg_faults_for_null_dereference__>,
+    reporter=reporter@entry=0x2003abe0) at /cygdrive/c/Users/Thomas/Utveckling/Cgreen/cgreen/src/runner.c:270
+270         run(spec);
+(gdb) step
+run (spec=0x402020 <CgreenSpec__CrashExample__seg_faults_for_null_dereference__>)
+    at /cygdrive/c/Users/Thomas/Utveckling/Cgreen/cgreen/src/runner.c:217
+217             spec->run();
+(gdb) step
+CrashExample__seg_faults_for_null_dereference () at crash_test2.c:9
+9           int *p = NULL;
+(gdb) step
+10          (*p)++;
+(gdb) step
+
+Program received signal SIGSEGV, Segmentation fault.
+0x004011ea in CrashExample__seg_faults_for_null_dereference () at crash_test2.c:10
+10          (*p)++;
+
+
+
+

Which shows exactly where the problem is.

+
+
+

This deals with the case where your code throws an exception like segmentation fault, but what about a process that fails to complete by getting stuck in a loop?

+
+
+

Well, Cgreen will wait forever too. +But, using the C signal handlers, we can place a time limit on the process by sending it an interrupt. +To save us writing this ourselves, Cgreen includes the die_in() function to help us out.

+
+
+

Here is an example of time limiting a test…​

+
+
+
+
...
+Ensure(CrashExample, seg_faults_for_null_dereference) {
+    ...
+}
+
+Ensure(CrashExample, will_loop_forever) {
+    die_in(1);
+    while(0 == 0) { }
+}
+
+int main(int argc, char **argv) {
+    TestSuite *suite = create_test_suite();
+    add_test_with_context(suite, CrashExample, seg_faults_for_null_dereference);
+    add_test_with_context(suite, CrashExample, will_loop_forever);
+    return run_test_suite(suite, create_text_reporter());
+}
+
+
+
+

When executed, the code will slow for a second, and then finish with…​

+
+
+
+
Running "main" (2 tests)...
+crash_tests.c:8: Exception: seg_faults_for_null_dereference 
+	Test terminated with signal: Segmentation fault
+
+crash_tests.c:13: Exception: will_loop_forever 
+	Test terminated unexpectedly, likely from a non-standard exception or Posix signal
+
+  "main": 2 exceptions in 42ms.
+Completed "main": 2 exceptions in 42ms.
+
+
+
+

Note that you see the test results as they come in. +Cgreen streams the results as they happen, making it easier to figure out where the test suite has problems.

+
+
+

Of course, if you want to set a general time limit on all your tests, then you can add a die_in() to a BeforeEach() (or setup()) function. +Cgreen will then apply the limit to each of the tests in that context, of course.

+
+
+

Another possibility is the use of an environment variable named CGREEN_TIMEOUT_PER_TEST which, if set to a number will apply that timeout to every test run. +This will apply to all tests in the same run.

+
+
+
+

2.8. Debugging Cgreen tests

+
+

Cgreen protects itself from being torn down by an exception in a test by fork()-ing each test into a separate process. +A catastrophic error will then only affect the child process for that specific test and Cgreen can catch it, rather than crashing too. +It can then report the exception and continue with the next test.

+
+
+

2.8.1. No fork, please

+
+

If you want to debug any of your tests the constant fork()-ing might make that difficult or impossible. +There are also other circumstances that might require that you don’t use fork().

+
+
+

There are two ways to make Cgreen refrain from fork()-ing.

+
+
+

Cgreen does not fork() when only a single test is run by name with the function run_single_test(). +To debug you can then obviously set a breakpoint at that test (but note that its actual name probably have been mangled). +Cgreen does some book-keeping before actually getting to the test, so a function easier to find might be the one simply called run().

+
+
+

The second way is to define the environment variable CGREEN_NO_FORK. +If Cgreen can get that variable from the environment using getenv() it will run the test(s) in the same process. +In this case the non-forking applies to all tests run, so all test will run in the same process, namely *Cgreen*s main process.

+
+
+ + + + + +
+ + +This might bring your whole test suite down if a single test causes an exception. +So it is not a recommended setting for normal use. +
+
+
+
+

2.8.2. Debugging with cgreen-runner

+
+

If you use the convenient auto-discovery feature of Cgreen (see +Automatic Test Discovery) by running dynamic loadable libraries through +cgreen-runner, it might be tricky to figure out to where to put +breaks and to get them to "take".

+
+
+

cgreen-runner obviously loads the library (or libraries) with your +tests dynamically so the tests are not available before executing the +code that loads them.

+
+
+ + + + + +
+ + +The function run() is a good place to place a breakpoint. +
+
+
+
+

2.8.3. cgreen-debug

+
+

For some platforms a utility script, cgreen-debug, is installed when you install Cgreen. +It makes it very convenient to start a debugging session for a particular test.

+
+
+

Find out the logical name of the test, which is composed of the Context and the Testname, in the form <context>:<testname>. +Then just invoke cgreen-debug

+
+
+
+
$ cgreen-debug <library> <context>:<testname>
+
+
+
+

The script will try to find a debugger, invoke it on the +cgreen-runner and break on that test.

+
+
+ + + + + +
+ + +Currently it only supports gdb and will prefer cgdb if +that’s available. +
+
+
+
+
+

2.9. Building Composite Test Suites

+
+

The TestSuite is a composite structure. +This means test suites can be added to test suites, building a tree structure that will be executed in order.

+
+
+

Let’s combine the strlen() tests with the Person tests above. +Firstly we need to remove the main() functions. +E.g…​

+
+
+
+
Ensure(Strlen, returns_five_for_hello) {
+   ...
+}
+
+Ensure(Strlen, returns_zero_for_empty_string) {
+   ...
+}
+
+TestSuite *our_tests() {
+    TestSuite *suite = create_test_suite();
+    add_test_with_context(suite, Strlen, returns_five_for_hello);
+    add_test_with_context(suite, Strlen, returns_zero_for_empty_string);
+    return suite;
+}
+
+
+
+

Then we can write a small runner with a new main() function…​

+
+
+
+
#include <cgreen/cgreen.h>
+
+TestSuite *our_tests();
+TestSuite *person_tests();
+
+int main(int argc, char **argv) {
+    TestSuite *suite = create_test_suite();
+    add_suite(suite, our_tests());
+    add_suite(suite, person_tests());
+    if (argc > 1) {
+        return run_single_test(suite, argv[1], create_text_reporter());
+    }
+    return run_test_suite(suite, create_text_reporter());
+}
+
+
+
+

It’s usually easier to place the TestSuite prototypes directly in the runner source, rather than have lot’s of header files. +This is the same reasoning that let us drop the prototypes for the test functions in the actual test scripts. +We can get away with this, because the tests are more about documentation than encapsulation.

+
+
+

As we saw above, we can run a single test using the run_single_test() function, and we’d like to be able to do that from the command line. +So we added a simple if block to take the test name as an optional argument. +The entire test suite will be searched for the named test. +This trick also saves us a recompile when we debug.

+
+
+

When you use the BDD notation you can only have a single test subject (which is actually equivalent of a suite) in a single file because you can only have one Describe() macro in each file. +But using this strategy you can create composite suites that takes all your tests and run them in one go.

+
+
+ + + + + +
+ + +Rewrite pending. +The next couple of sections does not reflect the current best thinking. +They are remnants of the TDD notation. +Using BDD notation you would create separate contexts, each in its own file, with separate names, for each of the fixture cases. +
+
+
+ + + + + +
+ + +If you use the TDD (non-BDD) notation you can build several test suites in the same file, even nesting them. +We can even add mixtures of test functions and test suites to the same parent test suite. +Loops will give trouble, however. +
+
+
+ + + + + +
+ + +If we do place several suites in the same file, then all the suites will be named the same in the breadcrumb trail in the test message. +They will all be named after the function the create call sits in. +If you want to get around this, or you just like to name your test suites, you can use create_named_test_suite() instead of create_test_suite(). +This takes a single string parameter. +In fact create_test_suite() is just a macro that inserts the func constant into create_named_test_suite(). +
+
+
+

What happens to setup and teardown functions in a TestSuite that contains other TestSuite:s?

+
+
+

Well firstly, Cgreen does not fork() when running a suite. +It leaves it up to the child suite to fork() the individual tests. +This means that a setup and teardown will run in the main process. +They will be run once for each child suite.

+
+
+

We can use this to speed up our Person tests above. +Remember we were creating a new connection and closing it again in the fixtures. +This means opening and closing a lot of connections. +At the slight risk of some test interference, we could reuse the connection accross tests…​

+
+
+
+
...
+static MYSQL *connection;
+
+static void create_schema() {
+    mysql_query(connection, "create table people (name, varchar(255) unique)");
+}
+
+static void drop_schema() {
+    mysql_query(connection, "drop table people");
+}
+
+Ensure(can_add_person_to_database) { ... }
+Ensure(cannot_add_duplicate_person) { ... }
+
+void open_connection() {
+    connection = mysql_init(NULL);
+    mysql_real_connect(connection, "localhost", "me", "secret", "test", 0, NULL, 0);
+}
+
+void close_connection() {
+    mysql_close(connection);
+}
+
+TestSuite *person_tests() {
+    TestSuite *suite = create_test_suite();
+    set_setup(suite, create_schema);
+    set_teardown(suite, drop_schema);
+    add_test(suite, can_add_person_to_database);
+    add_test(suite, cannot_add_duplicate_person);
+
+    TestSuite *fixture = create_named_test_suite("Mysql fixture");
+    add_suite(fixture, suite);
+    set_setup(fixture, open_connection);
+    set_teardown(fixture, close_connection);
+    return fixture;
+}
+
+
+
+

The trick here is creating a test suite as a wrapper whose sole purpose is to wrap the main test suite in the fixture. +This is our 'fixture' pointer. +This code is a little confusing, because we have two sets of fixtures in the same test script.

+
+
+

We have the MySQL connection fixture. +This will run open_connection() and close_connection() just once at the beginning and end of the person tests. +This is because the suite pointer is the only member of fixture.

+
+
+

We also have the schema fixture, the create_schema() and drop_schema(), which is run before and after every test. +Those are still attached to the inner suite.

+
+
+

In the real world we would probably place the connection fixture in its own file…​

+
+
+
+
static MYSQL *connection;
+
+MYSQL *get_connection() {
+    return connection;
+}
+
+static void open_connection() {
+    connection = mysql_init(NULL);
+    mysql_real_connect(connection, "localhost", "me", "secret", "test", 0, NULL, 0);
+}
+
+static void close_connection() {
+    mysql_close(connection);
+}
+
+TestSuite *connection_fixture(TestSuite *suite) {
+    TestSuite *fixture = create_named_test_suite("Mysql fixture");
+    add_suite(fixture, suite);
+    set_setup(fixture, open_connection);
+    set_teardown(fixture, close_connection);
+    return fixture;
+}
+
+
+
+

This allows the reuse of common fixtures across projects.

+
+
+
+
+
+

3. Mocking functions with Cgreen

+
+
+

When testing you want certainty above all else. +Random events destroy confidence in your test suite and force needless extra runs "to be sure". +A good test places the system under test into a tightly controlled environment. +A test chamber if you like. +This makes the tests fast, repeatable and reliable.

+
+
+

To create a test chamber for testing code, we have to control any outgoing calls from the code under test. +We won’t believe our test failure if our code is making calls to the internet for example. +The internet can fail all by itself. +Not only do we not have total control, but it also means we have to get dependent components working before we can test the higher level code. +This makes it difficult to code top down.

+
+
+

The solution to this dilemma is to write stub code for the components whilst the higher level code is written. +This pollutes the code base with temporary code, and the test isolation disappears when the system is eventually fleshed out.

+
+
+

The ideal is to have minimal stubs written for each individual test. +Cgreen encourages this approach by making such tests easier to write.

+
+
+

3.1. The Problem with Streams

+
+

How would we test the following code…​?

+
+
+
+
char *read_paragraph(int (*read)(void *), void *stream) {
+    int buffer_size = 0, length = 0;
+    char *buffer = NULL;
+    int ch;
+    while ((ch = (*read)(stream)) != EOF) {
+        if (++length > buffer_size) {
+            buffer_size += 100;
+            buffer = (char *)realloc(buffer, buffer_size + 1);
+        }
+        if ((buffer[length] = ch) == '\n') {
+            break;
+        }
+        buffer[length + 1] = '\0';
+    }
+    return buffer;
+}
+
+
+
+

This is a fairly generic stream filter that turns the incoming characters into C string paragraphs. +Each call creates one paragraph, returning a pointer to it or returning NULL if there is no paragraph. +The paragraph has memory allocated to it and the stream is advanced ready for the next call. +That’s quite a bit of functionality, and there are plenty of nasty boundary conditions. +I really want this code tested before I deploy it.

+
+
+

The problem is the stream dependency. +We could use a real stream, but that will cause all sorts of headaches. +It makes the test of our paragraph formatter dependent on a working stream. +It means we have to write the stream first, bottom up coding rather than top down. +It means we will have to simulate stream failures - not easy. +It will also mean setting up external resources. +This is more work, will run slower, and could lead to spurious test failures.

+
+
+

By contrast, we could write a simulation of the stream for each test, called a "server stub".

+
+
+

For example, when the stream is empty nothing should happen. +We hopefully get NULL from read_paragraph when the stream is exhausted. +That is, it just returns a steady stream of `EOF`s.

+
+
+

Fortunately, this function takes the stream as a parameter. +This is called dependency injection and is a very important concept. +Thanks to this we can write a small function, a stub, with the same signature, that simulates a real stream, and inject that instead of a real stream, which the production code probably does.

+
+
+ + + + + +
+ + +If the code does not inject the dependency this way we can often compile the stub separately and link with that instead the real stream. +In this case your stub will have to have the same name as the original function, of course. +(This is sometimes called the linkage seam.) +
+
+
+
+
static int empty_stream(void *stream) {
+    return EOF;
+}
+
+Describe(ParagraphReader);
+BeforeEach(ParagraphReader) {}
+AfterEach(ParagraphReader) {}
+
+Ensure(ParagraphReader, gives_null_when_reading_empty_stream) {
+    assert_that(read_paragraph(&empty_stream, NULL), is_null);
+}
+
+
+
+

Our simulation is easy here, because our fake stream returns only one value. +Things are harder when the function result changes from call to call as a real stream would. +Simulating this would mean messing around with static variables and counters that are reset for each test. +And of course, we will be writing quite a few stubs. +Often a different one for each test. +That’s a lot of clutter.

+
+
+

Cgreen can handle this clutter for us by letting us write a single programmable function for all our tests.

+
+
+
+

3.2. Record and Playback

+
+

We can redo our example by creating a stream_stub() function. +We can call it anything we want, and since I thought we wanted to have a stubbed stream…​

+
+
+
+
static int stream_stub(void *stream) {
+    return (int)mock(stream);
+}
+
+
+
+

Hardly longer that our trivial server stub above, it is just a macro to generate a return value, but we can reuse this in test after test. +Let’s see how.

+
+
+

For our simple example above we just tell it to always return EOF…​

+
+
+
+
#include <cgreen/cgreen.h>
+#include <cgreen/mocks.h>
+
+char *read_paragraph(int (*read)(void *), void *stream);
+
+static int stream_stub(void *stream) {
+    return (int)mock(stream);
+}
+
+Describe(ParagraphReader);
+BeforeEach(ParagraphReader) {}
+AfterEach(ParagraphReader) {}
+
+Ensure(ParagraphReader, gives_null_when_reading_empty_stream) {
+    always_expect(stream_stub, will_return(EOF));                                 (1)
+    assert_that(read_paragraph(&stream_stub, NULL), is_null);
+}
+
+
+
+ + + + + +
1The always_expect() macro takes the function name as an argument and also defines the return value using the call to will_return(). +This is a declaration of an expectation of a call to the stub, and we have told our stream_stub() to always return EOF when called.
+
+
+

Let’s see if our production code actually works…​

+
+
+
+
Running "stream" (1 test)...
+  "ParagraphReader": 1 pass in 42ms.
+Completed "stream": 1 pass in 42ms.
+
+
+
+

So far, so good. +On to the next test.

+
+
+

If we want to test a one character line, we have to send the terminating EOF or "\n" as well as the single character. +Otherwise our code will loop forever, giving an infinite line of that character.

+
+
+

Here is how we can do this…​

+
+
+
+
Ensure(ParagraphReader, gives_one_character_line_for_one_character_stream) {
+    expect(stream_stub, will_return('a'));
+    expect(stream_stub, will_return(EOF));
+    char *line = read_paragraph(&stream_stub, NULL);
+    assert_that(line, is_equal_to_string("a"));
+    free(line);
+}
+
+
+
+

Unlike the always_expect() instruction, expect() sets up an expectation of a single call and specifying will_return() sets the single return value for just that call. +It acts like a record and playback model. +Successive expectations map out the return sequence that will be given back once the test proper starts.

+
+
+

We’ll add this test to the suite and run it…​

+
+
+
+
Running "stream" (2 tests)...
+stream_tests.c:23: Failure: ParagraphReader -> gives_one_character_line_for_one_character_stream 
+	Expected [line] to [equal string] ["a"]
+		actual value:			[""]
+		expected to equal:		["a"]
+
+  "ParagraphReader": 1 pass, 1 failure in 42ms.
+Completed "stream": 1 pass, 1 failure in 42ms.
+
+
+
+

Oops. +Our code under test doesn’t work. +Already we need a fix…​

+
+
+
+
char *read_paragraph(int (*read)(void *), void *stream) {
+    int buffer_size = 0, length = 0;
+    char *buffer = NULL;
+    int ch;
+    while ((ch = (*read)(stream)) != EOF) {
+        if (++length > buffer_size) {
+            buffer_size += 100;
+            buffer = (char *)realloc(buffer, buffer_size + 1);
+        }
+        if ((buffer[length - 1] = ch) == '\n') {              (1)
+            break;
+        }
+        buffer[length] = '\0';                                (2)
+    }
+    return buffer;
+}
+
+
+
+ + + + + + + + + +
1After moving the indexing here…​
2and here…​
+
+
+

around a bit everything is fine:

+
+
+
+
Running "stream" (2 tests)...
+  "ParagraphReader": 2 passes in 42ms.
+Completed "stream": 2 passes in 42ms.
+
+
+
+

So, how do the Cgreen stubs work? +Each expect() describes one call to the stub and when a call to will_return() is included, the return values will be collected and returned in order as the expected calls arrive.

+
+
+

The mock() macro captures the parameter names, their values and the func property (the name of the stub function). +Cgreen can then use these to look up entries in the return list, and also to generate more helpful messages.

+
+
+

We can now crank out our tests quite quickly…​

+
+
+
+
Ensure(ParagraphReader, gives_one_word_line_for_one_word_stream) {
+    expect(stream_stub, will_return('t'));
+    expect(stream_stub, will_return('h'));
+    expect(stream_stub, will_return('e'));
+    always_expect(stream_stub, will_return(EOF));
+    assert_that(read_paragraph(&stream_stub, NULL), is_equal_to_string("the"));
+}
+
+
+
+

I’ve been a bit naughty. +As each test runs in its own process, I haven’t bothered to free the pointers to the paragraphs. +I’ve just let the operating system do it. +Purists may want to add the extra clean up code.

+
+
+

I’ve also used always_expect() for the last instruction. +Without this, if the stub is given an instruction it does not expect, it will throw a test failure. +This is overly restrictive, as our read_paragraph() function could quite legitimately call the stream after it had run off of the end. +OK, that would be odd behaviour, but that’s not what we are testing here. +If we were, it would be placed in a test of its own. +The always_expect() call tells Cgreen to keep going after the first three letters, allowing extra calls.

+
+
+

As we build more and more tests, they start to look like a specification of the wanted behaviour…​

+
+
+
+
Ensure(ParagraphReader, drops_line_ending_from_word_and_stops) {
+    expect(stream_stub, will_return('t'));
+    expect(stream_stub, will_return('h'));
+    expect(stream_stub, will_return('e'));
+    expect(stream_stub, will_return('\n'));
+    assert_that(read_paragraph(&stream_stub, NULL), is_equal_to_string("the"));
+}
+
+
+
+

…​and just for luck…​

+
+
+
+
Ensure(ParagraphReader, gives_empty_line_for_single_line_ending) {
+    expect(stream_stub, will_return('\n'));
+    assert_that(read_paragraph(&stream_stub, NULL), is_equal_to_string(""));
+}
+
+
+
+

This time we must not use always_return(). +We want to leave the stream where it is, ready for the next call to read_paragraph(). +If we call the stream beyond the line ending, we want to fail.

+
+
+

Oops, that was a little too fast. +Turns out we are failing anyway…​

+
+
+
+
Running "stream" (5 tests)...
+stream_tests.c:40: Failure: ParagraphReader -> drops_line_ending_from_word_and_stops 
+	Expected [read_paragraph(&stream_stub, ((void *)0))] to [equal string] ["the"]
+		actual value:			["the
+"]
+		expected to equal:		["the"]
+
+stream_tests.c:45: Failure: ParagraphReader -> gives_empty_line_for_single_line_ending 
+	Expected [read_paragraph(&stream_stub, ((void *)0))] to [equal string] [""]
+		actual value:			["
+"]
+		expected to equal:		[""]
+
+  "ParagraphReader": 3 passes, 2 failures in 42ms.
+Completed "stream": 3 passes, 2 failures in 42ms.
+
+
+
+

Clearly we are passing through the line ending. +Another fix later…​

+
+
+
+
char *read_paragraph(int (*read)(void *), void *stream) {
+    int buffer_size = 0, length = 0;
+    char *buffer = NULL;
+    int ch;
+    while ((ch = (*read)(stream)) != EOF) {
+        if (++length > buffer_size) {
+            buffer_size += 100;
+            buffer = (char *)realloc(buffer, buffer_size + 1);
+        }
+        if ((buffer[length - 1] = ch) == '\n') {
+            buffer[--length] = '\0';
+            break;
+        }
+        buffer[length] = '\0';
+    }
+    return buffer;
+}
+
+
+
+

And we are passing again…​

+
+
+
+
Running "stream" (5 tests)...
+  "ParagraphReader": 5 passes in 42ms.
+Completed "stream": 5 passes in 42ms.
+
+
+
+

There are no limits to the number of stubbed methods within a test, +only that two stubs cannot have the same name. +The following will cause problems…​

+
+
+
+
static int stream_stub(void *stream) {
+    return (int)mock(stream);
+}
+
+Ensure(Streams, bad_test) {
+    expect(stream_stub, will_return('a'));
+    do_stuff(&stream_stub, &stream_stub);
+}
+
+
+
+

You could program the same stub to return values for the two streams, but that would make a very brittle test. +Since we’d be making it heavily dependent on the exact internal behaviour that we are trying to test, or test drive, it will break as soon as we change that implementation. +The test will also become very much harder to read and understand. And we really don’t want that.

+
+
+

So, it will be necessary to have two stubs to make this test behave, but that’s not a problem…​

+
+
+
+
static int first_stream_stub(void *stream) {
+    return (int)mock(stream);
+}
+
+static int second_stream_stub(void *stream) {
+    return (int)mock(stream);
+}
+
+Ensure(Streams, good_test) {
+    expect(first_stream_stub, will_return('a'));
+    expect(second_stream_stub, will_return('a'));
+    do_stuff(&first_stream_stub, &second_stream_stub);
+}
+
+
+
+

We now have a way of writing fast, clear tests with no external dependencies. +The information flow is still one way though, from stub to the code under test. +When our code calls complex procedures, we won’t want to pick apart the effects to infer what happened. +That’s too much like detective work. +And why should we? We just want to know that we dispatched the correct information down the line.

+
+
+

Things get more interesting when we think of the traffic going the other way, from code to stub. +This gets us into the same territory as mock objects.

+
+
+
+

3.3. Setting Expectations on Mock Functions

+
+

To swap the traffic flow, we’ll look at an outgoing example instead. +Here is the prewritten production code…​

+
+
+
+
void by_paragraph(int (*read)(void *), void *in, void (*write)(void *, char *), void *out) {
+    while (1) {
+        char *line = read_paragraph(read, in);
+        if ((line == NULL) || (strlen(line) == 0)) {
+            return;
+        }
+        (*write)(out, line);
+        free(line);
+    }
+}
+
+
+
+

This is the start of a formatter utility. +Later filters will probably break the paragaphs up into justified text, but right now that is all abstracted behind the void write(void *, char *) interface. +Our current interests are: does it loop through the paragraphs, and does it crash?

+
+
+

We could test correct paragraph formation by writing a stub that collects the paragraphs into a struct. +We could then pick apart that struct and test each piece with assertions. +This approach is extremely clumsy in C. +The language is just not suited to building and tearing down complex edifices, never mind navigating them with assertions. +We would badly clutter our tests.

+
+
+

Instead we’ll test the output as soon as possible, right in the called function…​

+
+
+
+
...
+void expect_one_letter_paragraph(void *stream, char *paragraph) {
+    assert_that(paragraph, is_equal_to_string("a"));
+}
+
+Ensure(Formatter, makes_one_letter_paragraph_from_one_character_input) {
+    by_paragraph(
+            &one_character_stream,
+            NULL,
+            &expect_one_letter_paragraph,
+            NULL);
+}
+...
+
+
+
+

By placing the assertions into the mocked function, we keep the tests minimal. +The catch with this method is that we are back to writing individual functions for each test. +We have the same problem as we had with hand coded stubs.

+
+
+

Again, Cgreen has a way to automate this. +Here is the rewritten test…​

+
+
+
+
static int reader(void *stream) {
+    return (int)mock(stream);
+}
+
+static void writer(void *stream, char *paragraph) {
+    mock(stream, paragraph);
+}
+
+Ensure(Formatter, makes_one_letter_paragraph_from_one_character_input) {
+    expect(reader, will_return('a'));
+    always_expect(reader, will_return(EOF));
+    expect(writer, when(paragraph, is_equal_to_string("a")));
+    by_paragraph(&reader, NULL, &writer, NULL);
+}
+
+
+
+

Where are the assertions?

+
+
+

Unlike our earlier stub, reader() can now check its parameters. +In object oriented circles, an object that checks its parameters as well as simulating behaviour is called a mock object. +By analogy reader() is a mock function, or mock callback.

+
+
+

Using the expect() macro, we have set up the expectation that writer() will be called just once. +That call must have the string "a" for the paragraph parameter. +If the actual value of that parameter does not match, the mock function will issue a failure straight to the test suite. +This is what saves us writing a lot of assertions.

+
+
+
+

3.4. Running Tests With Mocked Functions

+
+

It’s about time we actually ran our test…​

+
+
+
+
Running "formatter" (1 test)...
+  "Formatter": 1 pass in 42ms.
+Completed "formatter": 1 pass in 42ms.
+
+
+
+

Confident that a single character works, we can further specify the behaviour. +Firstly an input sequence…​

+
+
+
+
Ensure(Formatter, makes_one_paragraph_if_no_line_endings) {
+    expect(reader, will_return('a'));
+    expect(reader, will_return(' '));
+    expect(reader, will_return('b'));
+    expect(reader, will_return(' '));
+    expect(reader, will_return('c'));
+    always_expect(reader, will_return(EOF));
+    expect(writer, when(paragraph, is_equal_to_string("a b c")));
+    by_paragraph(&reader, NULL, &writer, NULL);
+}
+
+
+
+

A more intelligent programmer than me would place all these calls in a +loop.

+
+
+
+
Running "formatter" (2 tests)...
+  "Formatter": 2 passes in 42ms.
+Completed "formatter": 2 passes in 42ms.
+
+
+
+

Next, checking an output sequence…​

+
+
+
+
Ensure(Formatter, generates_separate_paragraphs_for_line_endings) {
+    expect(reader, will_return('a'));
+    expect(reader, will_return('\n'));
+    expect(reader, will_return('b'));
+    expect(reader, will_return('\n'));
+    expect(reader, will_return('c'));
+    always_expect(reader, will_return(EOF));
+    expect(writer, when(paragraph, is_equal_to_string("a")));
+    expect(writer, when(paragraph, is_equal_to_string("b")));
+    expect(writer, when(paragraph, is_equal_to_string("c")));
+    by_paragraph(&reader, NULL, &writer, NULL);
+}
+
+
+
+

Again we can se that the expect() calls follow a record and playback model. +Each one tests a successive call. +This sequence confirms that we get "a", "b" and "c" in order.

+
+
+
+
Running "formatter" (3 tests)...
+  "Formatter": 5 passes in 42ms.
+Completed "formatter": 5 passes in 42ms.
+
+
+
+

So, why the 5 passes? +Each expect() with a constrait is actually an assert. +It asserts that the call specified is actually made with the parameters given and in the specified order. +In this case all the expected calls were made.

+
+
+

Then we’ll make sure the correct stream pointers are passed to the correct functions. +This is a more realistic parameter check…​

+
+
+
+
Ensure(Formatter, pairs_the_functions_with_the_resources) {
+    expect(reader, when(stream, is_equal_to(1)), will_return('a'));
+    always_expect(reader, when(stream, is_equal_to(1)), will_return(EOF));
+    expect(writer, when(stream, is_equal_to(2)));
+    by_paragraph(&reader, (void *)1, &writer, (void *)2);
+}
+
+
+
+
+
Running "formatter" (4 tests)...
+  "Formatter": 9 passes in 42ms.
+Completed "formatter": 9 passes in 42ms.
+
+
+
+

And finally we’ll specify that the writer is not called if +there is no paragraph.

+
+
+
+
Ensure(Formatter, ignores_empty_paragraphs) {
+    expect(reader, will_return('\n'));
+    always_expect(reader, will_return(EOF));
+    never_expect(writer);
+    by_paragraph(&reader, NULL, &writer, NULL);
+}
+
+
+
+

This last test is our undoing…​

+
+
+
+
Running "formatter" (5 tests)...
+formatter_tests.c:59: Failure: Formatter -> ignores_empty_paragraphs 
+	Mocked function [writer] has an expectation that it will never be called, but it was
+
+  "Formatter": 9 passes, 1 failure in 42ms.
+Completed "formatter": 9 passes, 1 failure in 42ms.
+
+
+
+

Obviously blank lines are still being dispatched to the writer(). +Once this is pointed out, the fix is obvious…​

+
+
+
+
void by_paragraph(int (*read)(void *), void *in, void (*write)(void *, char *), void *out) {
+    while (1) {
+        char *line = read_paragraph(read, in);
+        if ((line == NULL) || (strlen(line) == 0)) {
+            return;
+        }
+        (*write)(out, line);
+        free(line);
+    }
+}
+
+
+
+

Tests with never_expect() can be very effective at uncovering subtle +bugs.

+
+
+
+
Running "formatter" (5 tests)...
+  "Formatter": 10 passes in 42ms.
+Completed "formatter": 10 passes in 42ms.
+
+
+
+

All done.

+
+
+
+

3.5. Mocks Are…​

+
+

Using mocks is a very handy way to isolate a unit by catching and +controlling calls to external units. +Depending on your style of coding two schools of thinking have emerged. And of course Cgreen supports both!

+
+
+

3.5.1. Strict or Loose Mocks

+
+

The two schools are thinking a bit differently about what mock expectations means. +Does it mean that all external calls must be declared and expected? +What happens if a call was made to a mock that wasn’t expected? +And vice versa, if an expected call was not made?

+
+
+

Actually, the thinking is not only a school of thought, you might want to switch from one to the other depending on the test. +So Cgreen allows for that too.

+
+
+

By default Cgreen mocks are 'strict', which means that a call to an non-expected mock will be considered a failure. +So will an expected call that was not fullfilled. +You might consider this a way to define a unit through all its exact behaviours towards its neighbours.

+
+
+

On the other hand, 'loose' mocks are looser. +They allow both unfulfilled expectations and try to handle unexpected calls in a reasonable way.

+
+
+

You can use both with in the same suite of tests using the call cgreen_mocks_are(strict_mocks); and cgreen_mocks_are(loose_mocks); respectively. +Typically you would place that call at the beginning of the test, or in a setup or BeforeEach() if it applies to all tests in a suite.

+
+
+
+

3.5.2. Learning Mocks

+
+

Working with legacy code and trying to apply TDD, BDD, or even simply adding some unit tests, is not easy. +You’re working with unknown code that does unknown things with unknown counterparts.

+
+
+

So the first step would be to isolate the unit. +We won’t go into details on how to do that here, but basically you would replace the interface to other units with mocks. +This is a somewhat tedious manual labor, but will result in an isolated unit where you can start applying your unit tests.

+
+
+

Once you have your unit isolated in a harness of mocks, we need to figure out which calls it does to other units, now replaced by mocks, in the specific case we are trying to test.

+
+
+

This might be complicated, so Cgreen can make that a bit simpler. +There is a third 'mode' of the Cgreen mocks, the learning mocks.

+
+
+

If you temporarily add the call cgreen_mocks_are(learning_mocks); at the beginning of your unit test, the mocks will record all calls and present a list of those calls in order, including the actual parameter values, on the standard output.

+
+
+

So let’s look at the following example from the Cgreen unit tests. +It’s a bit contorted since the test actually call the mocked functions directly, but I believe it will serve as an example.

+
+
+
+
static int integer_out() {
+    return (int)mock();
+}
+
+static char *string_out(int p1) {
+    return (char *)mock(p1);
+}
+
+Ensure(LearningMocks, emit_pastable_code) {
+    cgreen_mocks_are(learning_mocks);
+    string_out(1);
+    string_out(2);
+    integer_out();
+    integer_out();
+    string_out(3);
+    integer_out();
+}
+
+
+
+

We can see the call to cgreen_mocks_are() starting the test and +setting the mocks into learning mode.

+
+
+

If we run this, just as we usually run tests, the following will show +up in our terminal:

+
+
+
+
Running "learning_mocks" (1 tests)...
+LearningMocks -> emit_pastable_code : Learned mocks are
+        expect(string_out, when(p1, is_equal_to(1)));
+        expect(string_out, when(p1, is_equal_to(2)));
+        expect(integer_out);
+        expect(integer_out);
+        expect(string_out, when(p1, is_equal_to(3)));
+        expect(integer_out);
+Completed "LearningMocks": 0 passes, 0 failures, 0 exceptions.
+Completed "learning_mocks": 0 passes, 0 failures, 0 exceptions.
+
+
+
+

If this was for real we could just copy this and paste it in place of +the call to cgreen_mocks_are() and we have all the expectations +done.

+
+
+ + + + + +
+ + +Before you can do this you need to implement the mock functions, of course. +I.e. write functions that replaces the real functions and instead calls mock(). +
+
+
+ + + + + +
+ + +If a test fails with an exception, you won’t get the learned calls unfortunately. +They are collected and printed at the end of the test. +This might be improved at some future time. +
+
+
+ + + + + +
+ + +You can try the cgreen-mocker for this, as described in cgreen-mocker - Automated Mocking. +
+
+
+
+
+
+
+

4. More on expect() and mock()

+
+
+

4.1. Important Things To Remember About expect() and mock()

+
+

Using expect() and mock() is a very powerful way to isolate your code under test from its dependencies. +But it is not always easy to follow what happens, and when.

+
+
+

Here are some important things to remember when working with Cgreen mocks.

+
+
+
    +
  • +

    calls to expect() collects constraints and any other required information when it is called

    +
  • +
  • +

    this also goes for will_return() which will save the value of its parameter when it is called

    +
  • +
  • +

    the actual evaluation and execution of those constraints occur when mock() is called in the function named in the expect() call(s)

    +
  • +
  • +

    calls to a function specified by the expect() calls are evaluated in the same order as the expect()s were executed, but only for that named function

    +
  • +
  • +

    the lexical scope of the first parameter in a when() is always inside the mocked function where the mock() call is made

    +
  • +
  • +

    the lexical scope of arguments to an is_equal_to…​() is where that call is made

    +
  • +
+
+
+ + + + + +
+ + +In summary, expect() does early collection, including evaluation of return value expression, and mock() does late evaluation of the constraints collected against the given arguments to mock(). +
+
+
+
+

4.2. Refactoring Tests with Mocks - CAUTION!

+
+

After a while you are bound to get tests with calls to expect(). +You might even have common patterns in multiple tests. +So your urge to refactor starts to set in. +And that is good, go with it, we have tests to rely on.

+
+
+

But there are a lot of things going on behind the scenes when you use Cgreen, often with the help of some serious macro-magic, so special care needs to be taken when refactoring tests that have expect() in them.

+
+
+

4.2.1. Renaming

+
+

The first "gotcha" is when you rename a function that you mock. +You are likely to have `expect()`s for that function too.

+
+
+ + + + + +
+ + +the function name in an expect() is "text" so it will not be catched by a refactoring tool. +You will need to change the name there manually. +
+
+
+
+

4.2.2. Local Variables

+
+

For example, consider this code

+
+
+
+
Ensure(Readline, can_read_some_characters) {
+    char canned_a = 'a';
+    char canned_b = 'b';
+    char canned_c = 'c';
+
+    expect(mocked_read,
+           will_set_contents_of_parameter(buf, &canned_a, sizeof(char)),
+           will_return(1));
+    expect(mocked_read,
+           will_set_contents_of_parameter(buf, &canned_b, sizeof(char)),
+           will_return(1));
+    expect(mocked_read,
+           will_set_contents_of_parameter(buf, &canned_c, sizeof(char)),
+           will_return(1));
+
+    ...
+    <call something that calls mocked_read()>
+    ...
+
+
+
+

It is very tempting to break out the common expect:

+
+
+
+
static void expect_char(char ch) {
+    expect(mocked_read,
+        will_set_contents_of_parameter(buf, &ch, sizeof(char)),
+        will_return(1));
+}
+
+Ensure(Readline, can_read_some_characters) {
+    char canned_a = 'a';
+    char canned_b = 'b';
+    char canned_c = 'c';
+
+    expect_char(canned_a);
+    expect_char(canned_b);
+    expect_char(canned_c);
+
+    ...
+    <call something that calls mocked_read()>
+    ...
+
+
+
+

Much nicer, right?

+
+
+

This will most likely lead to a segmentation fault or illegal memory reference, something that can be really tricky to track down. +The problem is that when mocked_read() is actually called, as an effect of calling something that calls mocked_read(), the parameter ch to the nicely extracted expect_char() does not exist anymore.

+
+
+

Good thing that you run the tests after each and every little refactoring, right? +Because then you know that it was the extraction you just did that was the cause. +Then you can come here and read up on what the problem might be and what to do about it.

+
+
+

At first glance the fix might look easy:

+
+
+
+
static void expect_char(char ch) {
+    char saved_ch = ch;
+    expect(mocked_read,
+        will_set_contents_of_parameter(buf, &saved_ch, sizeof(char)),
+        will_return(1));
+}
+
+Ensure(Readline, can_read_some_characters) {
+   ...
+
+
+
+

Close! But the local variable is also gone at the call to mocked_read(). Of course.

+
+
+

Ok, so let’s make it static:

+
+
+
+
static void expect_char(char ch) {
+    static char saved_ch = ch;
+    expect(mocked_read,
+        will_set_contents_of_parameter(buf, &saved_ch, sizeof(char)),
+        will_return(1));
+}
+
+Ensure(Readline, can_read_some_characters) {
+   ...
+
+
+
+

Ok, so then it must exist. +But the problem then becomes the three consequtive calls to expect_char().

+
+
+
+
Ensure(Readline, can_read_some_characters) {
+    char canned_a = 'a';
+    char canned_b = 'b';
+    char canned_c = 'c';
+
+    expect_char(canned_a);
+    expect_char(canned_b);
+    expect_char(canned_c);
+
+    ...
+    <call something that calls mocked_read()>
+    ...
+
+
+
+

Each of those have a different actual parameter, which is hard to store in one variable. +Even if it is static.

+
+
+

The solution is now quite obvious:

+
+
+
+
static void expect_char(char *ch_p) {
+    expect(mocked_read,
+        will_set_contents_of_parameter(buf, ch_p, sizeof(char)),
+        will_return(1));
+}
+
+Ensure(Readline, can_read_some_characters) {
+    char canned_a = 'a';
+    char canned_b = 'b';
+    char canned_c = 'c';
+
+    expect_char(&canned_a);
+    expect_char(&canned_b);
+    expect_char(&canned_c);
+
+    ...
+    <call something that calls mocked_read()>
+    ...
+
+
+
+

By using pointers to the variables in the test, we can ensure that the values are live when the expected call is made. +So we don’t have to make the character variables used in the test static, because as local variables those will remain live long enough.

+
+
+

And this is the moral here, you cannot use local variables in an extracted function as data for a mocked function call.

+
+
+ + + + + +
+ + +Variables that are to be sent to a mocked function MUST be live at the call to that mocked function. +
+
+
+
+
+

4.3. Other Use Cases For Mocks

+
+

4.3.1. Out Parameters

+
+

In C all function parameters are by value so if a function needs to return a value through a parameter that has to be done using a pointer. +Typically this is a pointer to the area or variable the function should fill.

+
+
+

Cgreen provides will_set_contents_of_parameter() to handle this use case. +For example

+
+
+
+
void convert_to_uppercase(char *converted_string, const char *original_string) {
+    mock(converted_string, original_string);
+}
+
+Ensure(setting_content_of_out_parameter) {
+    expect(convert_to_uppercase,
+           when(original_string, is_equal_to_string("upper case")),
+           will_set_contents_of_parameter(converted_string,
+                                          "UPPER CASE", 11));
+...
+
+
+
+

When the mock for convert_to_uppercase() is called it will write the string "UPPER CASE" in the area pointed to by converted_string.

+
+
+
+

4.3.2. Setting fields

+
+

Sometimes you need to set a field in a struct sent by reference to a mocked function. +You cannot use the will_set_contents_of_parameter() directly since you can’t, or even don’t want to, know the complete information in the structure. +But with a little bit of boilerplate in your mock function you can still write to a single field.

+
+
+

In the mock function you need to create a local variable that points to the field you want to update. +You can then use this pointer variable in the mock call to supplement the real parameters.

+
+
+

This local variable will then be accessible in expect() calls as if it was a parameter, and you can use it to wrote data to where it points, which then should be the field in the incoming structure.

+
+
+
+
struct structure {
+    int field;
+    char *string;
+};
+
+void update_field(struct structure *struct_to_update) {
+    int *field = &struct_to_update->field;
+    mock(struct_to_update, field);
+}
+
+Ensure(setting_field_of_parameter) {
+    int fourty_two = 42;
+    expect(update_field,
+           will_set_contents_of_parameter(field, &fourty_two, sizeof(int)));
+}
+...
+
+
+
+

The local variable field in the mock function is set to point to the field that we need to update. +It is then exposed by including it in the mock() call, and will_set_contents_of_parameter() will use it to update whatever it points to with the data provided in the expect().

+
+
+ + + + + +
+ + +Both the local variable and the data argument in the call to will_set_contents_of_parameter() must be pointers. +You cannot use literals as data, except when it is a string literal which as per C convention is converted to a pointer. +
+
+
+
+

4.3.3. Side Effects

+
+

Sometimes returning simple values is not enough. +The function that you want to mock might have some side effect, like setting a global error code, or aggregate some data.

+
+
+

Let’s assume that the reader increments a counter every time it gets called and we need to mimic that behaviour. +There are many ways to do this, but here is one using the side effect feature. +It works by calling a callback function that you provide, allowing you to feed some data to it.

+
+
+

We create the "side effect function" which needs to take a single argument which should be a pointer to the "side effect data". +You will have to cast that datapointer to the correct type.

+
+
+
+
static void update_counter(void * counter) {
+    *(int*)counter = *(int*)counter + 1;
+}
+
+Ensure(using_side_effect) {
+    int number_of_times_reader_was_called = 0;
+    expect(reader, will_return('\n'));
+    always_expect(reader,
+                  will_return(EOF),
+                  with_side_effect(&update_counter,
+                                  &number_of_times_reader_was_called));
+    never_expect(writer);
+    by_paragraph(&reader, NULL, &writer, NULL);
+
+    assert_that(number_of_times_reader_was_called, is_equal_to(1));
+}
+
+
+
+
+
+

4.4. The Mock Macros

+
+

When specifying behavior of mocks there are three parts. +First, how often the specified behaviour or expectation will be executed:

+
+ ++++ + + + + + + + + + + + + + + + + + + +

Macro

Description

expect(function, …​)

Expected once, in the specified order, for the same function

always_expect(function, …​)

Expect this behavior from here onwards

never_expect(function)

From this point this mocked function must never be called

+
+

You can specify constraints and behaviours for each expectation (except for never_expect() naturally). +A constraint places restrictions on the parameters (and will tell you if the expected restriction was not met), and a behaviour specifies what the mock should do if the parameter constraints are met.

+
+
+

A parameter constraint is defined using the when(parameter, constraint) macro. +It takes two parameters:

+
+ ++++ + + + + + + + + + + + + + + +

Parameter

Description

parameter

The name of the parameter to the mock function

constraint

A constraint placed on that parameter

+
+

There is a multitude of constraints available (actually, exactly the same as for the assertions we saw earlier):

+
+ ++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Constraint

Type

is_equal_to(value)

Integers

is_not_equal_to(value)

Integers

is_greater_than(value)

Integers

is_less_than(value)

Integers

is_equal_to_contents_of(pointer, size_of_contents)

Bytes/Structures

is_not_equal_to_contents_of(pointer, size_of_contents)

Bytes/Structures

is_equal_to_string(value)

String

is_not_equal_to_string(value)

String

contains_string(value)

String

does_not_contain_string(value)

String

begins_with_string(value)

String

is_equal_to_double(value)

Double

is_not_equal_to_double(value)

Double

is_less_than_double(value)

Double

is_greater_than_double(value)

Double

+
+

For the double valued constraints you can set the number of significant digits to consider a match with a call to significant_figures_for_assert_double_are(int figures). +The section on how to work with doubles has a more detailed discussion of the algorithm used for comparing floating point numbers.

+
+
+

Then there are a couple of ways to return results from the mocks. +They all provide ways to return various types of values through mock(). +In your mocked function you can then simply return that value, or manipulate it as necessary.

+
+ ++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Macro

mock() will…​

will_return(value)

return value, for integer types

will_return_double(value)

return value as a "boxed double", for double floats (required because of C’s type coercion rules which would otherwise convert a double into an int)

will_return_by_value(struct, size)

return a pointer to an allocated copy of the struct that can be copied and returned by value from the mocked function

will_set_contents_of_parameter(parameter_name, pointer_to_value, size)

write size bytes from the pointed out value (pointer_to_value) into where the referenced out parameter (parameter_name) is pointing

will_capture_parameter(parameter_name, local_variable)

capture the value of the parameter and store it in the named local variable

with_side_effect(function, pointer_to_data)

call the side effect function and pass pointer_to_data to it

+
+ + + + + +
+ + +will_return_double(): The "boxed double" returned by mock() have to be "unboxed" by the caller see Double Mocks for details. +
+
+
+ + + + + +
+ + +will_return_by_value: The memory allocated for the copy of the struct returned by mock() needs to be deallocated by the caller or it will be lost. You can do this with the code in the Box example below. +
+
+
+ + + + + +
+ + +will_set_contents_of_parameter: The data to set must be correct at the time of the call to the mock function, and not be overwritten or released between the call to the expect() and the mock function. See Refactoring Tests with Mocks - CAUTION! for details. +
+
+
+ + + + + +
+ + +will_capture_parameter: The local variable to capture the value in must be live at the time of the call to the mock function, so using a local variable in a function called by your test will not work. See Refactoring Tests with Mocks - CAUTION! for details. +
+
+
+
+

4.5. Combining Expectations

+
+

You can combine the expectations for a mock() in various ways:

+
+
+
+
  expect(mocked_file_writer,
+        when(data, is_equal_to(42)),
+        will_return(EOF));
+  expect(mocked_file_reader,
+        when(file, is_equal_to_contents_of(&FD, sizeof(FD))),
+        when(input, is_equal_to_string("Hello world!"),
+        with_side_effect(&update_counter, &counter),
+        will_set_contents_of_parameter(status, FD_CLOSED, sizeof(bool))));
+
+
+
+

If multiple when() are specified they all need to be fullfilled. +You can of course only have one for each of the parameters of your mock function.

+
+
+

You can also have multiple will_set_contents_of_parameter() in an expectation, one for each reference parameter, but naturally only one will_return().

+
+
+

To ensure that a specific call happens n times the macro times(number_times_called) can be passed as a constraint to a specific call:

+
+
+
+
  expect(mocked_file_writer,
+        when(data, is_equal_to(42)),
+        times(1));
+
+
+
+

This feature only works for expect().

+
+
+
+

4.6. Order of constraints

+
+

When you have multiple constraints in an expect the order in which they are executed is not always exactly then order in which they where given.

+
+
+

First all constraints are inspected for validity, such as if the parameter name given cannot be found, but primarily to see if the parameters, if any, matche the actual parameters in the call.

+
+
+

Then all read-only constraints are processed, followed by constraints that set contents.

+
+
+

Finally all side effect constraints are executed.

+
+
+
+

4.7. Order of multiple `expect`s

+
+

The expections still need to respect the order of calling, so if we call the function +mocker_file_writer with the following pattern:

+
+
+
+
  mocked_file_writer(42);
+  mocked_file_writer(42);
+  mocked_file_writer(43);
+  mocked_file_writer(42);
+
+
+
+

The expectation code should look like the following

+
+
+
+
  expect(mocked_file_writer,
+        when(data, is_equal_to(42)),
+        times(2));
+  expect(mocked_file_writer,
+        when(data, is_equal_to(43)),
+        times(1));
+  expect(mocked_file_writer,
+        when(data, is_equal_to(42)),
+        times(1));
+
+
+
+
+

4.8. Returning struct

+
+

If the function we are mocking returns structs by value, then our mock function need to do that too. +To do this we must use specific return macro, will_return_by_value(). +Below is some example code using an imaginary struct typedef’ed as Struct and a corresponding function, retrieve_struct(), which we want to mock.

+
+
+

The underlying mechanism of this is that in the test we create the struct that we want to return. +The macro will_return_by_value() then copies that to a dynamically allocated area, saving it so that a pointer to that area can be returned by mock().

+
+
+
+
  Struct returned_struct = {...};
+  expect(retrieve_struct,
+         will_return_by_value(returned_struct, sizeof(Struct));
+         /* `returned_struct` has been copied to an allocated area */
+
+
+
+ + + + + +
+ + +In some future version the size argument will be removed from will_return_by_value() size since the macro can easily calculate that for you. +
+
+
+

The mock function will then look like this:

+
+
+
+
Struct retrieve_struct() {
+   return *(Struct *)mock();       /* De-reference the returned pointer to the allocated area */
+}
+
+
+
+

This would cause a memory leak since the area allocated by the return_by_value() macro is not deallocated. +And in many scenarious this might not be a big problem, and you could make do with that simple version.

+
+
+

In case we wanted to be sure, we should free the area automatically allocated by will_return_by_value(). +The pointer returned by mock() will point to that area. +So, here’s a better, although slightly more complicated, version:

+
+
+
+
Struct retrieve_struct() {
+    Struct *struct_p = (Struct *)mock(); /* Get the pointer */
+    Struct the_struct = *struct_p;       /* Dereference to get a struct */
+    free(struct_p);                      /* Deallocate the returned area */
+    return the_struct;                   /* Finally we can return the struct by value */
+}
+
+
+
+
+

4.9. Mocking struct Parameters

+
+

Modern C standards allows function parameters to be structs by value. +Since our mock() only can handle scalar values this presents a bit of a conundrum.

+
+
+
+
typedef struct {
+    int i;
+    char *string;
+} Struct;
+
+int function_taking_struct(Struct s) {
+    return (int)mock(?);
+
+
+
+

And we also can not compare a non-scalar value with any of the is_equal_to…​() constraint macros in the expect() call. +Also remember that the C language does not allow comparing non-scalar values using ==.

+
+
+

There are a couple of ways to handle this and which one to select depends on what you want to do.

+
+
+

4.9.1. Checking Single struct Fields

+
+

In an expect(when()) we probably want to check one, or more, of the fields in the struct.

+
+
+

Since mock() actually can "mock" anything we can use a normal field expression to access the value we want to check:

+
+
+
+
int function_checking_a_field(Struct s) {
+    return (int)mock(s.i);
+
+
+
+

The trick here is that mock() just saves the "name", as a string, given as the argument, in this case "s.i", and pair it with the value of that expression. +There is no requirement that the "name" is actually a parameter, it can be anything. +The only thing to remember is that the exact same string needs to be used when invoking when():

+
+
+
+
    expect(function_checking_a_field, when(s.i, is_equal_to(12)),
+           will_return(12));
+
+
+
+

You can do this with as many fields as you need. +And there is no (reasonable) limit to how many arguments mock() can take, so you can start with the ones that you require and add more as you need them.

+
+
+
+
int function_checking_multiple_fields(Struct s) {
+    return (int)mock(s.i, s.string);
+}
+
+Ensure(StructParameters, can_mock_muultiple_fields_in_parameter) {
+    Struct struct_to_send = { .i = 13, .string = "hello world!" };
+
+    expect(function_checking_multiple_fields,
+           when(s.i, is_equal_to(13)),
+           when(s.string, begins_with_string("hello")),
+           will_return(13));
+
+    assert_that(function_checking_multiple_fields(struct_to_send), is_equal_to(13));
+}
+
+
+
+ + + + + +
+ + +In both example we use an explicit value in will_return() instead of the value of the field, "s.i". +That is because it is not possible to use the value of a mocked value in will_return(). +Remember, expect() does early collection. +At the time of executing it, there is no parameter available, so the value must come from that run-time environment. +Also, since we already explicitly know the value, we have to use it in the when() clause, there will be no uncertainty of what it should be. +The only concern might be duplication of an explicit value, but that is not a big problem in a unittest, clarity over DRY, and you can easily fix that with a suitably named local variable. +
+
+
+
+
+

4.10. Capturing Parameters

+
+

TBD.

+
+
+
+
+
+

5. Special Cases

+
+
+

5.1. Working with doubles

+
+

We are not talking about +test doubles +here, but about values of C/C++ double type (a.k.a double float.)

+
+
+

Cgreen is designed to make it easy and natural to write assertions and expectations. +Many functions can be used for multiple data types, e.g. is_equal_to() applies to all integer type values, actually including pointers.

+
+
+

But the C language has its quirks. +One of them is the fact that it is impossible to inspect the datatypes of values during run-time. +This has e.g. forced the introduction of is_equal_to_string() to enable string comparisons.

+
+
+

5.1.1. Assertions and Constraints

+
+

When it comes to double typed values this has spilled over even further. +For double typed values we have

+
+ +++ + + + + + + + + + + + + + + + + + +

Constraint

is_equal_to_double(value)

is_not_equal_to_double(value)

is_less_than_double(value)

is_greater_than_double(value)

+
+

But there is also the special assert that you must use when asserting doubles

+
+ +++ + + + + + + + + +

Assertion

assert_that_double(expected, constraint)

+
+

and the utility function

+
+ +++ + + + + + + + + +

Utility

significant_figures_for_assert_double_are(int figures)

+
+

And of course they are designed to go together. +So, if you want to assert an expression yeilding a double typed value, you need to combine them:

+
+
+
+
Ensure(Doubles, can_assert_double_values) {
+    significant_figures_for_assert_double_are(3);
+    assert_that_double(3.14, is_equal_to_double(5.0));
+}
+
+
+
+ + + + + +
+ + +You have to use assert_that_double() and is_equal_to_double() +together. +
+
+
+

and you would get

+
+
+
+
double_tests.c:13: Failure: can_assert_double_values 
+	Expected [3.14] to [equal double] [5.0] within [3] significant figures
+		actual value:			[3.140000]
+		expected value:			[5.000000]
+
+
+
+
+

5.1.2. Double Mocks

+
+

The general mechanism Cgreen uses to transport values to and from mock functions is based on the simple idea that most types fit into a "large" integer and can be type converted to and from whatever type you need.

+
+
+

Since a double float will not fit into the same memory space as an integer Cgreen handles that by encapsulating ("boxing") the double into an area which is represented by the pointer to it. +And that pointer can fit into the integer type value (intptr_t) that Cgreen uses to transport values into and out of mock(). +To get the value back you "unbox" it.

+
+
+

There are two possible uses of double that you need to be aware of

+
+
+
    +
  1. +

    When a parameter to the mocked function is of double type and needs to be matched in an constraint in an expect() call.

    +
  2. +
  3. +

    When the mock function itself should return a double type value.

    +
  4. +
+
+
+

In the test you should use the special double type constraints and the will_return_double() convenience function. +In the mock function you will have to take care to box and unbox as required.

+
+ ++++ + + + + + + + + + + + + + + +

Boxing and unboxing in mock functions

Description

box_double(double value)

Wrap the value in an allocated + memory area and return a pointer + to it

unbox_double(BoxedDouble *box)

Unwrap the value by freeing the + area and returning the value

+
+

Here’s an example of that:

+
+
+
+
static double double_out(int i, double d) {
+    return unbox_double(mock(i, box_double(d))); (1)
+}
+
+Ensure(Doubles, can_be_arguments_to_mock_and_returned) {
+    expect(double_out,
+           when(i, is_equal_to(15)),
+           when(d, is_equal_to_double(31.32)), (2)
+           will_return_double(3.1415926));     (3)
+    assert_that_double(double_out(15, 31.32), is_equal_to_double(3.1415926));
+}
+
+
+
+ + + + + + + + + + + + + +
1We can see that the parameter d to the mock function, since it is a +double, it will have to be used as box_double(d) in the call to +mock().
2The corresponding expect() uses a double constraint.
3The mock function in this small example also returns a double. +The expect() uses will_return_double() so the mock function needs to unbox the return value from mock() to be able to return the double type value.
+
+
+ + + + + +
+ + +Strange errors may occur if you box and/or unbox or combine double constraints incorrectly. +
+
+
+
+

5.1.3. Details of Floating Point Comparison Algorithm

+
+

The number of significant digits set with significant_figures_for_assert_double_are() specifies a relative tolerance. +Cgreen considers two double precision numbers x and y equal if their difference normalized by the larger of the two is smaller than 10^(1 - significant_figures)^. +Mathematically, we check that |x - y| < max(|x|, |y|) * 10^(1 - significant_figures)^.

+
+
+

Well documented subtleties arise when comparing floating point numbers close to zero using this algorithm. +The article Comparing Floating Point Numbers, 2012 Edition by Bruce Dawson has an excellent discussion of the issue. +The essence of the problem can be appreciated if we consider the special case where y == 0. +In that case, our condition reduces to |x| < |x| * 10^(1 - significant_figures)^. +After cancelling |x| this simplifies to 1 < 10^(1 - significant_figures)^. +But this is only true if significant_figures < 1. +In words this can be summarized by saying that, in a relative sense, all numbers are very different from zero. +To circumvent this difficulty we recommend to use a constraint of the following form when comparing numbers close to zero:

+
+
+
+
assert_that(fabs(x - y) < abs_tolerance);
+
+
+
+
+
+

5.2. Using Cgreen with C++

+
+

The examples in this guide uses the C langauge to shows how to use CGreen. You can also use CGreen with C++.

+
+
+ + + + + +
+ + +The following needs expansion and more details as the support for C++ is extended. +
+
+
+

All you have to do is

+
+
+
    +
  • +

    Use the cgreen namespace by adding using namespace cgreen; +at the beginning of the file with your tests

    +
  • +
+
+
+

There is also one extra feature when you use C++, the assert_throws function.

+
+
+ + + + + +
+ + +If you use the autodiscovering runner, as described in Using the Runner, and thus link your tests into a shared library, don’t forget to link it with the same C++ library that was used to create the cgreen-runner. +
+
+
+
+
+
+

6. Context, System Under Test & Suites

+
+
+

As mentioned earlier, Cgreen promotes the behaviour driven style of test driving code. +The thinking behind BDD is that we don’t really want to test anything, if we just could specify the behaviour of our code and ensure that it actually behaves this way we would be fine.

+
+
+

This might seem like an age old dream, but when you think about it, there is actually very little difference in the mechanics from vanillla TDD. +First we write how we want it, then implement it. +But the small change in wording, from `test´ to `behaviour´, from `test that´ to `ensure that´, makes a huge difference in thinking, and also very often in quality of the resulting code.

+
+
+

6.1. The SUT - System Under Test

+
+

Since BDD talks about behaviour, there has to be something that we can talk about as having that wanted behaviour. +This is usually called the SUT, the System Under Test. +The "system" might be whatever we are testing, such as a C module ("MUT"), class ("CUT"), object ("OUT"), function ("FUT") or method ("MUT"). +We will stick with SUT in this document. +To use Cgreen in BDD-ish mode you must define a name for it.

+
+
+
+
#include <cgreen/cgreen.h>
+Describe(SUT);
+
+
+
+

Cgreen supports C++ and there you naturally have the objects and also the Class Under Test. +But in plain C you will have to think about what is actually the "class" under test. +E.g. in sort_test.c you might see

+
+
+
+
#include <cgreen/cgreen.h>
+Describe(Sorter);
+
+Ensure(Sorter, can_sort_an_empty_list) {
+  assert_that(sorter(NULL), is_null);
+}
+
+
+
+

In this example you can clearly see what difference the BDD-ish style makes when it comes to naming. +Convention, and natural language, dictates that typical names for what TDD would call tests, now starts with 'can' or 'finds' or other verbs, which makes the specification so much easier to read.

+
+
+

Yes, I wrote 'specification'. Because that is how BDD views what TDD basically calls a test suite. The suite specifies the behaviour of a `class´. +(That’s why some BDD frameworks draw on 'spec', like RSpec.)

+
+
+
+

6.2. Contexts and Before and After

+
+

The complete specification of the behaviour of a SUT might become long and require various forms of setup. +When using TDD style you would probably break this up into multiple suites having their own setup() and teardown().

+
+
+

With BDD-ish style we could consider a suite as a behaviour specification for our SUT 'in a particular context'. +E.g.

+
+
+
+
#include <cgreen/cgreen.h>
+
+Describe(shopping_basket_for_returning_customer);
+
+Customer *customer;
+
+BeforeEach(shopping_basket_for_returning_customer){
+  customer = create_test_customer();
+  login(customer);
+}
+
+AfterEach(shopping_basket_for_returning_customer) {
+  logout(customer);
+  destroy_customer(customer);
+}
+
+Ensure(shopping_basket_for_returning_customer, allows_use_of_discounts) {
+  ...
+}
+
+
+
+

The 'context' would then be shopping_basket_for_returning_customer, with the SUT being the shopping basket 'class'.

+
+
+

So 'context', 'system under test' and 'suite' are mostly interchangable concepts in Cgreen lingo. +It’s a named group of 'tests' that share the same BeforeEach and AfterEach and lives in the same source file.

+
+
+
+
+
+

7. Automatic Test Discovery

+
+
+

7.1. Forgot to Add Your Test?

+
+

When we write a new test we focus on the details about the test we are trying to write. +And writing tests is no trivial matter so this might well take a lot of brain power.

+
+
+

So, it comes as no big surprise, that sometimes you write your test and then forget to add it to the suite. +When we run it it appears that it passed on the first try! +Although this should really make you suspicious, sometimes you get so happy that you just continue with churning out more tests and more code. +It’s not until some (possibly looong) time later that you realize, after much headache and debugging, that the test did not actually pass. +It was never even run!

+
+
+

There are practices to minimize the risk of this happening, such as always running the test as soon as you can set up the test. +This way you will see it fail before trying to get it to pass.

+
+
+

But it is still a practice, something we, as humans, might fail to do at some point. +Usually this happens when we are most stressed and in need of certainty.

+
+
+
+

7.2. The Solution - the 'cgreen-runner'

+
+

Cgreen gives you a tool to avoid not only the risk of this happening, but also the extra work and extra code. +It is called the cgreen-runner.

+
+
+

The cgreen-runner should come with your Cgreen installation if your platform supports the technique that is required, which is 'programatic access to dynamic loading of libraries'. +This means that a program can load an external library of code into memory and inspect it. +Kind of self-inspection, or reflexion.

+
+
+

So all you have to do is to build a dynamically loadable library of all tests (and of course your objects under test and other necessary code). +Then you can run the cgreen-runner and point it to the library. +The runner will then load the library, enumerate all tests in it, and run every test.

+
+
+

It’s automatic, and there is nothing to forget.

+
+
+
+

7.3. Using the Runner

+
+

Assuming your tests are in first_test.c the typical command to build your library using gcc would be

+
+
+
+
$ gcc -shared -o first_test.so -fPIC first_test.c -lcgreen
+
+
+
+

The -fPIC means to generate 'position independent code' which is required if you want to load the library dynamically. +To explicitly state this is required on many platforms.

+
+
+

How to build a dynamically loadable shared library might vary a lot depending on your platform. +Can’t really help you there, sorry!

+
+
+

As soon as we have linked it we can run the tests using the cgreen-runner by just giving it the shared, dynamically loadable, object library as an argument:

+
+
+
+
$ cgreen-runner first_test.so
+Running "first_tests" (2 tests)...
+first_tests.c:12: Failure: Cgreen -> fails_this_test 
+	Expected [0 == 1] to [be true]
+
+  "Cgreen": 1 pass, 1 failure in 42ms.
+Completed "first_tests": 1 pass, 1 failure in 42ms.
+
+
+
+

More or less exactly the same output as when we ran our first test in the beginning of this quickstart tutorial. We can see that the top level of the tests will be named as the library it was discovered in, and the second level is the context for our System Under Test, in this case 'Cgreen'. +We also see that the context is mentioned in the failure message, giving a fairly obvious Cgreen → fails_this_test.

+
+
+

Now we can actually delete the main function in our source code. +We don’t need all this, since the runner will discover all tests automatically.

+
+
+
+
int main(int argc, char **argv) {
+    TestSuite *suite = create_test_suite();
+    add_test_with_context(suite, Cgreen, passes_this_test);
+    add_test_with_context(suite, Cgreen, fails_this_test);
+    return run_test_suite(suite, create_text_reporter());
+}
+
+
+
+

It always feel good to delete code, right?

+
+
+

We can also select which test to run:

+
+
+
+
$ cgreen-runner first_test.so Cgreen:this_test_should_fail
+Running "first_tests" (1 test)...
+first_tests.c:12: Failure: Cgreen -> fails_this_test 
+	Expected [0 == 1] to [be true]
+
+  "Cgreen": 1 failure in 42ms.
+Completed "first_tests": 1 failure in 42ms.
+
+
+
+

We recommend the BDD notation to discover tests, and you indicate which context the test we want to run is in. +In this example it is Cgreen so the test should be refered to as Cgreen:this_test_should_fail.

+
+
+

If you don’t use the BDD notation there is actually a context anyway, it is called default.

+
+
+
+

7.4. Cgreen Runner Options

+
+

Once you get the build set up right for the cgreen-runner everything +is fairly straight-forward. But you have a few options:

+
+
+
+
--xml <prefix>
+
+

Instead of messages on stdout with the TextReporter, +write results into one XML-file per suite or context, +compatible with Hudson/Jenkins CI. The filename(s) +will be <prefix>-<suite>.xml

+
+
--suite <name>
+
+

Name the top level suite

+
+
--no-run
+
+

Don’t run the tests

+
+
--verbose
+
+

Show progress information and list discovered tests

+
+
--colours
+
+

Use colours (or colors) to emphasis result (requires ANSI-capable terminal)

+
+
--quiet
+
+

Be more quiet

+
+
+
+
+

The verbose option is particularly handy since it will give you the actual names of all tests discovered. +So if you have long test names you can avoid mistyping them by copying and pasting from the output of cgreen-runner --verbose. +It will also give the mangled name of the test which should make it easier to find in the debugger. +Here’s an example:

+
+
+
+
Discovered Cgreen:fails_this_test (CgreenSpec__Cgreen__fails_this_test__)
+Discovered Cgreen:passes_this_test (CgreenSpec__Cgreen__passes_this_test__)
+Discovered 2 test(s)
+Opening [first_tests.so] to only run one test: 'Cgreen:fails_this_test' ...
+Running "first_tests" (1 test)...
+first_tests.c:12: Failure: Cgreen -> fails_this_test 
+	Expected [0 == 1] to [be true]
+
+  "Cgreen": 1 failure in 42ms.
+Completed "first_tests": 1 failure in 42ms.
+
+
+
+
+

7.5. Selecting Tests To Run

+
+

You can name a single test to be run by giving it as the last argument on the command line. +The name should be in the format <SUT>:<test>. +If not obvious you can get that name by using the --verbose command option which will show you all tests discovered and both there C/C++ and Cgreen names. +Copying the Cgreen name from that output is an easy way to run only that particular test. +When a single test is named it is run using run_single_test(). +As described in Five Minutes Doing TDD with Cgreen this means that it is not protected by fork()-ing it to run in its own process.

+
+
+

The cgreen-runner supports selecting tests with limited pattern matching. +Using an asterisk as a simple 'match many' symbol you can say things like

+
+
+
+
$ cgreen-runner <library> Cgreen:*
+$ cgreen-runner <library> C*:*this*
+
+
+
+
+

7.6. Multiple Test Libraries

+
+

You can run tests in multiple libraries in one go by adding them to the cgreen-runner command:

+
+
+
+
$ cgreen-runner first_set.so second_set.so ...
+
+
+
+
+

7.7. Setup, Teardown and Custom Reporters

+
+

The cgreen-runner will only run setup and teardown functions if you use the BDD-ish style with BeforeEach() and AfterEach() as described above. +The runner does not pickup setup() and teardown() added to suites, because it actually doesn’t run suites. +It discovers all tests and runs them one by one. +The macros required by the BDD-ish style ensures that the corresponding BeforeEach() and AfterEach() are run before and after each test.

+
+
+ + + + + +
+ + +The cgreen-runner will discover your tests in a shared library even if you don’t use the BDD-ish style. +But it will not be able to find and run the setup() and/or teardown() attached to your suite(s). +This will probably cause your tests to fail or crash. +
+
+
+

In case you have non-BDD style tests without any setup() and/or teardown() you can still use the runner. +The default suite/context where the tests live in this case is called default. +But why don’t you convert your tests to BDD notation? +This removes the risk of frustrating trouble-shooting when you added setup() and teardown() and can’t understand why they are not run…​

+
+
+

So, the runner encourages you to use the BDD notation. +But since we recommend that you do anyway, that’s no extra problem if you are starting out from scratch. +But see Changing Style for some easy tips on how to get you there if you already have non-BDD tests.

+
+
+

You can choose between the TextReporter, which we have been seeing so far, and the built-in JUnit/Ant compatible XML-reporter using the --xml option. +But it is not currently possible to use custom reporters as outlined in Changing Cgreen Reporting with the runner.

+
+
+

If you require another custom reporter you need to resort to the standard, programatic, way of invoking your tests. +For now…​

+
+
+
+

7.8. Skipping Tests

+
+

Sometimes you find that you need to temporarily remove a test, perhaps to do a refactoring when you have a failing test. +Ignoring that test will allow you to do the refactoring while still in the green.

+
+
+

An old practice is then to comment it out. +That is a slightly cumbersome. +It is also hazardous habit as there is no indication of a missing test if you forget to uncomment it when you are done.

+
+
+

Cgreen offers a much better solution. +You can just add an 'x' infront of the Ensure for the test and that test will be skipped.

+
+
+
+
...
+xEnsure(Reader, ...) {
+  ...
+}
+...
+
+
+
+

With this method, it is a one character change to temporarily ignore, and un-ignore, a test. +It is also easily found using text searches through a complete source tree. +Cgreen will also tally the skipped tests, so it is clearly visible that you have some skipped test when you run them.

+
+
+
+
+
+

8. Changing Style

+
+
+

If you already have some TDD style Cgreen test suites, it is quite easy to change them over to BDD-ish style. +Here are the steps required

+
+
+
    +
  • +

    Add Describe(SUT);

    +
  • +
  • +

    Turn your current setup function into a BeforeEach() definition by changing its signature to match the macro, or simply call the existing setup function from the BeforeEach(). +If you don’t have any setup function you still need to define an empty BeforeEach().

    +
  • +
  • +

    Ditto for AfterEach().

    +
  • +
  • +

    Add the SUT to each Ensure() by inserting it as a first parameter.

    +
  • +
  • +

    Change the call to add the tests to add_test_with_context() by +adding the name of the SUT as the second parameter.

    +
  • +
  • +

    Optionally remove the calls to set_setup() and set_teardown().

    +
  • +
+
+
+

Done.

+
+
+

If you want to continue to run the tests using a hand-coded runner, +you can do that by keeping the setup and teardown functions and their +corresponding set_-calls.

+
+
+

It’s nice that this is a simple process, because you can change over from TDD style to BDD-ish style in small steps. +You can convert one source file at a time, by just following the recipe above. +Everything will still work as before but your tests and code will likely improve.

+
+
+

And once you have changed style you can fully benefit from the +automatic discovery of tests as described in Automatic Test Discovery.

+
+
+
+
+

9. Changing Cgreen Reporting

+
+
+

9.1. Replacing the Reporter

+
+

In every test suite so far, we have run the tests with this line…​

+
+
+
+
return run_test_suite(our_tests(), create_text_reporter());
+
+
+
+

We can change the reporting mechanism just by changing this call to create another reporter.

+
+
+
+

9.2. Built-in Reporters

+
+

Cgreen has the following built-in reporters that you can choose from when your code runs the test suite.

+
+ ++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ReporterPurposeSignatureNote

Text

Human readable, with clear messages

create_text_reporter(void)

XML

ANT/Jenkins compatible

create_xml_reporter(const char *file_prefix)

file_prefix is the prefix of the XML files generated.

CUTE

CUTE Eclipse-plugin (http://cute-test.org) compatible output

create_cute_reporter(void)

CDash

CMake (http://cmake.org) dashboard

create_cdash_reporter(CDashInfo *info)

info is a structure defined in cdash_reporter.h

+
+

If you write a runner function like in most examples above, you can just substitute which runner to create. +If you use the cgreen-runner (Automatic Test Discovery) to dynamically find all your tests you can force it to use the XML-reporter with the -x <prefix> option.

+
+
+ + + + + +
+ + +Currently cgreen-runner only supports the built-in text and XML reporters. +
+
+
+
+

9.3. Rolling Our Own

+
+

Although Cgreen has a number of options, there are times when you’d like a different output from the reporter, the CUTE and CDash reporters are examples that grew out of such a need.

+
+
+

Perhaps your Continuous Integration server want the result in a different format, or you just don’t like the text reporter…​

+
+
+

Writing your own reporter is supported. +And we’ll go through how that can be done using an XML-reporter as an example.

+
+
+ + + + + +
+ + +Cgreen already has an XML-reporter compatible with ANT/Jenkins, see Built-in Reporters. +
+
+
+

Here is the code for create_text_reporter()…​

+
+
+
+
TestReporter *create_text_reporter(void) {
+    TestReporter *reporter = create_reporter();
+    if (reporter == NULL) {
+        return NULL;
+    }
+    reporter->start_suite = &text_reporter_start_suite;
+    reporter->start_test = &text_reporter_start_test;
+    reporter->show_fail = &show_fail;
+    reporter->show_skip = &show_skip;
+    reporter->show_incomplete = &show_incomplete;
+    reporter->finish_test = &text_reporter_finish_test;
+    reporter->finish_suite = &text_reporter_finish;
+    return reporter;
+}
+
+
+
+

The TestReporter structure contains function pointers that control the reporting. +When called from create_reporter() constructor, these pointers are set up with functions that display nothing. +The text reporter code replaces these with something more dramatic, and then returns a pointer to this new object. +Thus the create_text_reporter() function effectively extends the object from create_reporter().

+
+
+

The text reporter only outputs content at the start of the first test, at the end of the test run to display the results, when a failure occurs, and when a test fails to complete. +A quick look at the text_reporter.c file in Cgreen reveals that the overrides just output a message and chain to the versions in reporter.h.

+
+
+

To change the reporting mechanism ourselves, we just have to know a little about the methods in the TestReporter structure.

+
+
+
+

9.4. The TestReporter Structure

+
+

The Cgreen TestReporter is a pseudo class that looks +something like…​

+
+
+
+
typedef struct _TestReporter TestReporter;
+struct _TestReporter {
+    void (*destroy)(TestReporter *reporter);
+    void (*start_suite)(TestReporter *reporter, const char *name, const int count);
+    void (*start_test)(TestReporter *reporter, const char *name);
+    void (*show_pass)(TestReporter *reporter, const char *file, int line,
+                                   const char *message, va_list arguments);
+    void (*show_skip)(TestReporter *reporter, const char *file, int line);
+    void (*show_fail)(TestReporter *reporter, const char *file, int line,
+                                   const char *message, va_list arguments);
+    void (*show_incomplete)(TestReporter *reporter, const char *file, int line,
+                                   const char *message, va_list arguments);
+    void (*assert_true)(TestReporter *reporter, const char *file, int line, int result,
+                                   const char * message, ...);
+    void (*finish_test)(TestReporter *reporter, const char *file, int line);
+    void (*finish_suite)(TestReporter *reporter, const char *file, int line);
+    int passes;
+    int failures;
+    int exceptions;
+    void *breadcrumb;
+    int ipc;
+    void *memo;
+    void *options;
+};
+
+
+
+

The first block are the methods that can be overridden:

+
+
+
+
void (*destroy)(TestReporter *reporter)
+
+

This is the destructor for the default structure. +If this is overridden, then the overriding function must call destroy_reporter(TestReporter *reporter) to finish the clean up.

+
+
void (*start_suite)(TestReporter *reporter, const char *name, const int count)
+
+

This is the first of the callbacks. +At the start of each test suite Cgreen will call this method on the reporter with the name of the suite being entered and the number of tests in that suite. +The default version keeps track of the stack of tests in the breadcrumb pointer of TestReporter. +If you make use of the breadcrumb functions, as the defaults do, then you will need to call reporter_start_suite() to keep the book-keeping in sync.

+
+
void (*start_test)(TestReporter *reporter, const char *name)
+
+

At the start of each test Cgreen will call this method on the reporter with the name of the test being entered. +Again, the default version keeps track of the stack of tests in the breadcrumb pointer of TestReporter. +If you make use of the breadcrumb functions, as the defaults do, then you will need to call reporter_start_test() to keep the book-keeping in sync.

+
+
void (*show_pass)(TestReporter *reporter, const char *file, int line, const char *message, va_list arguments)
+
+

This method is initially empty as most reporters see little point in reporting passing tests (but you might do), so there is no need to chain the call to any other function. +Besides the pointer to the reporter structure, Cgreen also passes the file name of the test, the line number of failed assertion, the message to show and any additional parameters to substitute into the message. +The message comes in as printf() style format string, and so the variable argument list should match the substitutions.

+
+
void (*show_fail)(TestReporter *reporter, const char *file, int line, const char *message, va_list arguments)
+
+

The partner of show_pass(), and the one you’ll likely overload first.

+
+
void (*show_skip)(TestReporter *reporter, const char *file, int line)
+
+

This method will be called when a skipped test is encountered, see Skipping Tests.

+
+
void (*show_incomplete)(TestReporter *reporter, const char *file, int line, const char *message, va_list arguments)
+
+

When a test fails to complete, this is the handler that is called. +As it’s an unexpected outcome, no message is received, but we do get the name of the test. +The text reporter combines this with the breadcrumb to produce the exception report.

+
+
void (*assert_true)(TestReporter *reporter, const char *file, int line, int result, const char * message, …​)
+
+

This is not normally overridden and is really internal. +It is the raw entry point for the test messages from the test suite. +By default it dispatches the call to either show_pass() or show_fail().

+
+
void (*finish_test)(TestReporter *reporter, const char *file, int line)
+
+

The counterpart to the (*start_test)() call. +It is called on leaving the test. +It needs to be chained to the reporter_finish() to keep track of the breadcrumb book keeping.

+
+
void (*finish_suite)(TestReporter *reporter, const char *file, int line)
+
+

The counterpart to the (*start_suite)() call called on leaving the test suite, and similar to the (*finish_test)() if your reporter needs a handle on that event too. +The default text reporter chains both this and (*finish_test)() to the same function where it figures out if it is the end of the top level suite. +If so, it prints the familiar summary of passes and fails.

+
+
+
+
+ + + + + +
+ + +The show_fail() and show_pass() functions are called from the child process, i.e. the isolated process that is fork():ed to run a single test case. +All others, notably start_…​(), finish_…​(), show_incomplete() and show_skip() are run in the main (parent) process. +This fact might be important since the processes do not share memory. +Information is passed from the child to the parent using messaging performed within the show_…​() functions. +
+
+
+

The second block is simply resources and book keeping that the reporter can use to liven up the messages…​

+
+
+ + + + + + + + + + + + + + + + + + + + + +
+passes + +

The number of passes so far.

+
+skips + +

The number of tests that has been skipped by the xEnsure mechanism (see Skipping Tests)

+
+failures + +

The number of failures generated so far.

+
+exceptions + +

The number of test functions that have failed to complete so far.

+
+breadcrumb + +

This is a pointer to the list of test names in the stack.

+
+
+
+

The breadcrumb pointer is different and needs a little explanation. +Basically it is a stack, analogous to the breadcrumb trail you see on websites. +Everytime a start() handler is invoked, the name is placed in this stack. +When a finish() message handler is invoked, a name is popped off.

+
+
+

There are a bunch of utility functions in cgreen/breadcrumb.h that can read the state of this stack. +Most useful are get_current_from_breadcrumb() which takes the breadcrumb pointer and returns the current test name, and get_breadcrumb_depth() which gives the current depth of the stack. +A depth of zero means that the test run has finished.

+
+
+

If you need to traverse all the names in the breadcrumb, then you can call walk_breadcrumb(). +Here is the full signature…​

+
+
+
+
void walk_breadcrumb(Breadcrumb *breadcrumb, void (*walker)(const char *, void *), void *memo);
+
+
+
+

The void (*walker)(const char *, void *) is a callback that will be +passed the name of the test suite for each level of nesting.

+
+
+

It is also passed the memo pointer that was passed to the walk_breadcrumb() call. +You can use this pointer for anything you want, as all Cgreen does is pass it from call to call. +This is so aggregate information can be kept track of whilst still being reentrant.

+
+
+

The last parts of the TestReporter structure are…​

+
+
+ + + + + + + + + + + + + +
+ipc + +

This is an internal structure for handling the messaging between reporter and test suite. +You shouldn’t touch this.

+
+memo + +

By contrast, this is a spare pointer for your own expansion.

+
+options + +

A pointer to a reporter specific structure that can be used to set options. +E.g. the textreporter defines the structure TextReporterOptions which can be used by calling code to define the use of colors when printing passes and failures. +You set it with set_reporter_options(*void).

+
+
+
+
+

9.5. An Example XML Reporter

+
+

Let’s make things real with an example. +Suppose we want to send the output from Cgreen in XML format, say for storing in a repository or for sending across the network.

+
+
+ + + + + +
+ + +The cgreen-runner already has an XML-reporter that you can +use if you need to produce Jenkins/ANT compatible XML output. +See Cgreen Runner Options. +
+
+
+

Suppose also that we have come up with the following format…​

+
+
+
+
<?xml?>
+<suite name="Top Level">
+    <suite name="A Group">
+        <test name="a_test_that_passes">
+        </test>
+        <test name="a_test_that_fails">
+            <fail>
+                <message>A failure</message>
+                <location file="test_as_xml.c" line="8"/>
+            </fail>
+        </test>
+    </suite>
+</suite>
+
+
+
+

In other words a simple nesting of tests with only failures encoded. +The absence of "fail" XML node is a pass.

+
+
+

Here is a test script, test_as_xml.c that we can use to construct the +above output…​

+
+
+
+
#include <cgreen/cgreen.h>
+
+Describe(XML_reporter);
+BeforeEach(XML_reporter) {}
+AfterEach(XML_reporter) {}
+
+Ensure(XML_reporter, reports_a_test_that_passes) {
+    assert_that(1 == 1);
+}
+
+Ensure(XML_reporter, reports_a_test_that_fails) {
+    fail_test("A failure");
+}
+
+TestSuite *create_test_group() {
+    TestSuite *suite = create_named_test_suite("A Group");
+    add_test_with_context(suite, XML_reporter, reports_a_test_that_passes);
+    add_test_with_context(suite, XML_reporter, reports_a_test_that_fails);
+    return suite;
+}
+
+int main(int argc, char **argv) {
+    TestSuite *suite = create_named_test_suite("Top Level");
+    add_suite(suite, create_test_group());
+    return run_test_suite(suite, create_text_reporter());
+}
+
+
+
+

We can’t use the auto-discovering cgreen-runner (see Automatic Test Discovery) here since we need to ensure that the nested suites are reported as a nested xml structure. +And we’re not actually writing real tests, just something that we can use to drive our new reporter.

+
+
+

The text reporter is used just to confirm that everything is working. +So far it is.

+
+
+
+
Running "Top Level" (2 tests)...
+test_as_xml.c:12: Failure: A Group -> reports_a_test_that_fails 
+	A failure
+
+  "A Group": 1 pass, 1 failure in 42ms.
+Completed "Top Level": 1 pass, 1 failure in 42ms.
+
+
+
+

Our first move is to switch the reporter from text, to our +not yet written XML version…​

+
+
+
+
#include "xml_reporter.h"
+...
+
+int main(int argc, char **argv) {
+    TestSuite *suite = create_named_test_suite("Top Level");
+    add_suite(suite, create_test_group());
+    return run_test_suite(suite, create_xml_reporter());
+}
+
+
+
+

We’ll start the ball rolling with the xml_reporter.h +header file…​

+
+
+
+
#ifndef _XML_REPORTER_HEADER_
+#define _XML_REPORTER_HEADER_
+
+#include <cgreen/reporter.h>
+
+TestReporter *create_xml_reporter();
+
+#endif
+
+
+
+

…​and the simplest possible reporter in xml_reporter.c.

+
+
+
+
#include <cgreen/reporter.h>
+
+#include "xml_reporter.h"
+
+TestReporter *create_xml_reporter() {
+    TestReporter *reporter = create_reporter();
+    return reporter;
+}
+
+
+
+

One that outputs nothing.

+
+
+
+
$ gcc -c test_as_xml.c
+$ gcc -c xml_reporter.c
+$ gcc xml_reporter.o test_as_xml.o -lcgreen -o xml
+$ ./xml
+
+
+
+

Yep, nothing.

+
+
+

Let’s add the outer XML tags first, so that we can see Cgreen +navigating the test suite…​

+
+
+
+
#include <cgreen/reporter.h>
+#include <cgreen/breadcrumb.h>
+
+#include <stdio.h>
+#include "xml_reporter.h"
+
+
+static void xml_reporter_start_suite(TestReporter *reporter, const char *name, int count) {
+    printf("<suite name=\"%s\">\n", name);
+    reporter_start_suite(reporter, name, count);
+}
+
+static void xml_reporter_start_test(TestReporter *reporter, const char *name) {
+    printf("<test name=\"%s\">\n", name);
+    reporter_start_test(reporter, name);
+}
+
+static void xml_reporter_finish_test(TestReporter *reporter, const char *filename, int line, const char *message) {
+    reporter_finish_test(reporter, filename, line, message);
+    printf("</test>\n");
+}
+
+static void xml_reporter_finish_suite(TestReporter *reporter, const char *filename, int line) {
+    reporter_finish_suite(reporter, filename, line);
+    printf("</suite>\n");
+}
+
+TestReporter *create_xml_reporter() {
+    TestReporter *reporter = create_reporter();
+    reporter->start_suite = &xml_reporter_start_suite;
+    reporter->start_test = &xml_reporter_start_test;
+    reporter->finish_test = &xml_reporter_finish_test;
+    reporter->finish_suite = &xml_reporter_finish_suite;
+    return reporter;
+}
+
+
+
+

Although chaining to the underlying reporter_start_*() and reporter_finish_*() functions is optional, I want to make use of some of the facilities later.

+
+
+

Our output meanwhile, is making its first tentative steps…​

+
+
+
+
<suite name="Top Level">
+<suite name="A Group">
+<test name="reports_a_test_that_passes">
+</test>
+<test name="reports_a_test_that_fails">
+</test>
+</suite>
+</suite>
+
+
+
+

We don’t require an XML node for passing tests, so the show_fail() +function is all we need…​

+
+
+
+
...
+
+static void xml_show_fail(TestReporter *reporter, const char *file, int line, const char *message, va_list arguments) {
+    printf("<fail>\n");
+    printf("\t<message>");
+    vprintf(message, arguments);
+    printf("</message>\n");
+    printf("\t<location file=\"%s\" line=\"%d\"/>\n", file, line);
+    printf("</fail>\n");
+...
+
+TestReporter *create_xml_reporter() {
+    TestReporter *reporter = create_reporter();
+    reporter->start_suite = &xml_reporter_start_suite;
+    reporter->start_test = &xml_reporter_start_test;
+    reporter->show_fail = &xml_show_fail;
+    reporter->finish_test = &xml_reporter_finish_test;
+    reporter->finish_suite = &xml_reporter_finish_suite;
+    return reporter;
+}
+
+
+
+

We have to use vprintf() to handle the variable argument list passed to us. +This will probably mean including the stdarg.h header as well as stdio.h.

+
+
+

This gets us pretty close to what we want…​

+
+
+
+
<suite name="Top Level">
+<suite name="A Group">
+<test name="reports_a_test_that_passes">
+</test>
+<test name="reports_a_test_that_fails">
+<fail>
+	<message>A failure</message>
+	<location file="test_as_xml.c" line="15"/>
+</fail>
+</test>
+</suite>
+</suite>
+
+
+
+

For completeness we should add a tag for a test that doesn’t complete. +We’ll output this as a failure, although we don’t bother with the +location this time…​

+
+
+
+
static void xml_show_incomplete(TestReporter *reporter, const char *file, int line, const char *message, va_list arguments) {
+    printf("<fail>\n");
+    printf("\t<message>Failed to complete</message>\n");
+    printf("</fail>\n");
+}
+...
+TestReporter *create_xml_reporter() {
+    TestReporter *reporter = create_reporter();
+    reporter->start_suite = &xml_reporter_start_suite;
+    reporter->start_test = &xml_reporter_start_test;
+    reporter->show_fail = &xml_show_fail;
+    reporter->show_incomplete = &xml_show_incomplete;
+    reporter->finish_test = &xml_reporter_finish_test;
+    reporter->finish_suite = &xml_reporter_finish_suite;
+    return reporter;
+}
+
+
+
+

All that’s left then is the XML declaration and the thorny issue of indenting. +Although the indenting is not strictly necessary, it would make the output a lot more readable.

+
+
+

Given that the test depth is kept track of for us with the breadcrumb object in the TestReporter structure, indentation will actually be quite simple. +We’ll add an indent() function that outputs the correct number of tabs…​

+
+
+
+
static void indent(TestReporter *reporter) {
+    int depth = get_breadcrumb_depth((CgreenBreadcrumb *)reporter->breadcrumb);
+    while (depth-- > 0) {
+        printf("\t");
+    }
+}
+
+
+
+

The get_breadcrumb_depth() function just gives the current test depth as recorded in the reporters breadcrumb (from cgreen/breadcrumb.h). +As that is just the number of tabs to output, the implementation is trivial.

+
+
+

We can then use this function in the rest of the code. +Here is the complete listing…​

+
+
+
+
#include <cgreen/reporter.h>
+#include <cgreen/breadcrumb.h>
+
+#include <stdio.h>
+#include "xml_reporter.h"
+
+static void indent(TestReporter *reporter) {
+    int depth = get_breadcrumb_depth((CgreenBreadcrumb *)reporter->breadcrumb);
+    while (depth-- > 0) {
+        printf("\t");
+    }
+}
+
+static void xml_reporter_start_suite(TestReporter *reporter, const char *name, int count) {
+    if (get_breadcrumb_depth((CgreenBreadcrumb *)reporter->breadcrumb) == 0) {
+        printf("<?xml?>\n");
+    }
+    indent(reporter);
+    printf("<suite name=\"%s\">\n", name);
+    reporter_start_suite(reporter, name, count);
+}
+
+static void xml_reporter_start_test(TestReporter *reporter, const char *name) {
+    indent(reporter);
+    printf("<test name=\"%s\">\n", name);
+    reporter_start_test(reporter, name);
+}
+
+static void xml_show_fail(TestReporter *reporter, const char *file, int line, const char *message, va_list arguments) {
+    indent(reporter);
+    printf("<fail>\n");
+    indent(reporter);
+    printf("\t<message>");
+    vprintf(message, arguments);
+    printf("</message>\n");
+    indent(reporter);
+    printf("\t<location file=\"%s\" line=\"%d\"/>\n", file, line);
+    indent(reporter);
+    printf("</fail>\n");
+}
+
+static void xml_show_incomplete(TestReporter *reporter, const char *file, int line, const char *message, va_list arguments) {
+    indent(reporter);
+    printf("<fail>\n");
+    indent(reporter);
+    printf("\t<message>Failed to complete</message>\n");
+    indent(reporter);
+    printf("</fail>\n");
+}
+
+
+static void xml_reporter_finish_test(TestReporter *reporter, const char *filename, int line, const char *message) {
+    reporter_finish_test(reporter, filename, line, message);
+    indent(reporter);
+    printf("</test>\n");
+}
+
+static void xml_reporter_finish_suite(TestReporter *reporter, const char *filename, int line) {
+    reporter_finish_suite(reporter, filename, line);
+    indent(reporter);
+    printf("</suite>\n");
+}
+
+TestReporter *create_xml_reporter() {
+    TestReporter *reporter = create_reporter();
+    reporter->start_suite = &xml_reporter_start_suite;
+    reporter->start_test = &xml_reporter_start_test;
+    reporter->show_fail = &xml_show_fail;
+    reporter->show_incomplete = &xml_show_incomplete;
+    reporter->finish_test = &xml_reporter_finish_test;
+    reporter->finish_suite = &xml_reporter_finish_suite;
+    return reporter;
+}
+
+
+
+

And finally the desired output…​

+
+
+
+
<?xml?>
+<suite name="Top Level">
+	<suite name="A Group">
+		<test name="reports_a_test_that_passes">
+		</test>
+		<test name="reports_a_test_that_fails">
+			<fail>
+				<message>A failure</message>
+				<location file="test_as_xml.c" line="15"/>
+			</fail>
+		</test>
+	</suite>
+</suite>
+
+
+
+

Job done.

+
+
+

Possible other reporter customizations include reporters that write to +syslog, talk to IDE plug-ins, paint pretty printed documents or just +return a boolean for monitoring purposes.

+
+
+
+
+
+

10. Advanced Usage

+
+
+

10.1. Custom Constraints

+
+

Sometimes the built-in constraints that Cgreen provide are not sufficient. +With Cgreen it is possible to create custom constraints, although you will be depending on some internal structures if you do so.

+
+
+

Here’s how to implement a simple example custom constraint that asserts that the value is bigger than 5. +We’ll implement this using a static constraint since it does not take any parameter.

+
+
+ + + + + +
+ + +static constraints are a bad idea…​ +
+
+
+

First we need the actual compare function:

+
+
+
+
#include <cgreen/cgreen.h>
+
+bool compare_want_greater_than_5(Constraint *constraint, CgreenValue actual) {
+    return actual.value.integer_value > 5;
+}
+
+
+
+

And then the static constraint structure, for which we’ll need some of +Cgreen's internal functions:

+
+
+
+
#include <cgreen/message_formatting.h>
+#include "constraint_internal.h"
+
+Constraint static_is_bigger_than_5 = {
+        /* .type */ CGREEN_VALUE_COMPARER_CONSTRAINT,
+        /* .name */ "be bigger than 5",
+        /* .destroy */ destroy_static_constraint,
+        /* .compare */ compare_want_greater_than_5,
+        /* .test */ test_want,
+        /* .format_failure_message_for */ failure_message_for,
+        /* .actual_value_message */ "",
+        /* .expected_value_message */ "",
+        /* .expected_value */ {CGREEN_INTEGER, {5}},
+        /* .stored_value_name */ "null",
+        /* .parameter_name */ NULL,
+        /* .size_of_stored_value */ 0
+};
+
+
+
+

This implementation can use a statically declared Constraint structure that is prefilled since it does not need to store the value to be checked. +This static custom constraint can then be used directly in the assert like this:

+
+
+
+
Ensure(TestConstraint, custom_constraint_using_static_function) {
+    Constraint * is_bigger_than_5 = &static_is_bigger_than_5;
+    assert_that(10, is_bigger_than_5);
+}
+
+
+
+

To create a custom constraint that takes an input parameter, we need to add a function that creates a constraint structure that correctly saves the value to be checked, and, for convenience, a macro. +This time we need to dig into how Cgreen stores expected values and we’ll also make use of Cgreen's utility function string_dup().

+
+
+
+
#include <cgreen/message_formatting.h>
+#include "cgreen_value_internal.h"
+#include "utils.h"
+
+
+bool compare_want_smaller_value(Constraint *constraint, CgreenValue actual) {
+    return actual.value.integer_value < constraint->expected_value.value.integer_value ;
+}
+
+Constraint *create_smaller_than_constraint(intptr_t expected_value, const char *expected_value_name) {
+    Constraint *constraint = create_constraint();
+
+    constraint->expected_value = make_cgreen_integer_value(expected_value);
+    constraint->expected_value_name = string_dup(expected_value_name);
+    constraint->type = CGREEN_VALUE_COMPARER_CONSTRAINT;
+
+    constraint->compare = &compare_want_smaller_value;
+    constraint->execute = &test_want;
+    constraint->name = "be smaller than";
+    constraint->size_of_expected_value = sizeof(intptr_t);
+
+    return constraint;
+}
+#define is_smaller_than(value) create_smaller_than_constraint(value, #value)
+
+
+
+

This gives a custom constraint that can be used in the assert in the +same way as Cgreen's built-in constraints:

+
+
+
+
Ensure(TestConstraint, custom_constraint_using_a_function_with_arguments_function) {
+    assert_that(9, is_smaller_than(10));
+}
+
+
+
+

The last, and definitely more complex, example is a constraint that takes two structures and compares fields in them. +The constraint will, given a structure representing a piece and another structure representing a box, check if the piece can fit inside the box using a size field.

+
+
+

Assuming two "application" structures with size fields:

+
+
+
+
typedef struct Box {
+    int id;
+    int size;
+} Box;
+
+typedef struct Piece {
+    int id;
+    int size;
+} Piece;
+
+
+
+

We want to be able to write a test like this:

+
+
+
+
Ensure(TestConstraint, more_complex_custom_constraint_function) {
+    Box box1 = {.id = 1, .size = 5};
+    Piece piece99 = {.id = 99, .size = 6};
+    assert_that(&piece99, can_fit_in_box(&box1));
+}
+
+
+
+

To implement the can_fit_in_box constraint we first need a comparer +function:

+
+
+
+
bool compare_piece_and_box_size(Constraint *constraint, CgreenValue actual) {
+    return ((Piece *)actual.value.pointer_value)->size
+        < ((Box*)constraint->expected_value.value.pointer_value)->size ;
+}
+
+
+
+

And this time we can’t rely on Cgreen's checker and message generating function test_want() which we used in the previous examples. +So we also need a custom function that calls the comparison and formats a possible error message:

+
+
+
+
static void test_fit_piece(Constraint *constraint, const char *function_name, CgreenValue actual,
+                           const char *test_file, int test_line, TestReporter *reporter) {
+    (*reporter->assert_true)(
+            reporter,
+            test_file,
+            test_line,
+            (*constraint->compare)(constraint, actual),
+            "Piece [%f], does not fit in [%f] in function [%s] parameter [%s]",
+            ((Piece *)constraint->expected_value.value.pointer_value)->id,
+            ((Box *)actual.value.pointer_value)->id,
+            function_name,
+            constraint->parameter_name);
+}
+
+
+
+

Finally we’ll use both of those in the constraint creating function +and add the convenience macro:

+
+
+
+
Constraint *create_piece_fit_in_box_constraint(intptr_t expected_value, const char *expected_value_name) {
+    Constraint *constraint = create_constraint();
+
+    constraint->expected_value = make_cgreen_pointer_value((void*)expected_value);
+    constraint->expected_value_name = string_dup(expected_value_name);
+    constraint->type = CGREEN_CONTENT_COMPARER_CONSTRAINT;
+
+    constraint->compare = &compare_piece_and_box_size;
+    constraint->execute = &test_fit_piece;
+    constraint->name = "fit in box";
+    constraint->size_of_expected_value = sizeof(intptr_t);
+
+    return constraint;
+}
+#define can_fit_in_box(box) create_piece_fit_in_box_constraint((intptr_t)box, #box)
+
+
+
+ + + + + +
+ + +As stated above, using custom constraints makes your tests vulnurable to changes in Cgreen's internals. +Hopefully a method to avoid this will emerge in the future. +
+
+
+ + + + + +
+ + +You can write custom constraints directly in a test file, but they can of course also be collected into a separately compiled module which is linked with your tests. +
+
+
+
+
+
+

11. Hints and Tips

+
+
+ + + + + +
+ + +This chapter is intended to contain tips for situations that you might need some help with, but it is nowhere near complete at this time. +
+
+
+

11.1. cgreen-mocker - Automated Mocking

+
+

Are you starting out with Cgreen on a largish legacy system? +And there are loads and loads of functions to mock to get a unit under test?

+
+
+

You could try the cgreen-mocker that is supplied as a contributed part of the Cgreen source distribution.

+
+
+

It is a Python program that parses C language header files and tries to create a corresponding .mock file where each function declaration is replaced with a call to mock().

+
+
+
+
Usage:
+  cgreen-mocker.py <headerfile> { <cpp_directive> }
+     <headerfile>: file with function declarations that you want
+                   to mock
+     <cpp_directive>: any 'cpp' directive but most useful is e.g.
+                      "-I <directory>" to ensure cpp finds files.
+
+
+
+

So given a header file containing lines like

+
+
+
+
extern CgreenValue make_cgreen_integer_value(intptr_t integer);
+extern CgreenValue make_cgreen_string_value(const char *string);
+
+
+
+

cgreen-mocker will, given that there are no errors, print something +like this on the screen:

+
+
+
+
CgreenValue make_cgreen_integer_value(intptr_t integer) {
+  return mock(integer);
+}
+
+CgreenValue make_cgreen_string_value(const char *string) {
+  return mock(string);
+}
+
+
+
+

Of course, you would pipe this output to a file.

+
+
+

To use cgreen-mocker you need Python, and the following packages:

+
+
+ +
+
+

These can easily be installed with:

+
+
+
+
$ pip install -r requirements.txt
+
+
+
+ + + + + +
+ + +cgreen-mocker is an unsupported contribution to the Cgreen +project by Thomas Nilefalk. +
+
+
+
+

11.2. Compiler Error Messages

+
+

Sometimes you might get cryptic and strange error messages from the compiler. +Since Cgreen uses some C/C++ macro magic this can happen and the error messages might not be straight forward to interpret.

+
+
+

Here are some examples, but the exact messages differ between compilers and versions.

+
+ ++++ + + + + + + + + + + + + + + + + + + + + + + +

Compiler error message

Probable cause…​

"contextFor<X>" is undeclared here

Missing Describe(<X>);

undefined reference to 'AfterEach_For_<X>'

Missing AfterEach(<X>)

CgreenSpec<X><Y>__ is undeclared

Missing test subject/context in the Ensure of a BDD style test

use of undeclared identifier 'contextFor<X>'

Missing Describe(<X>);

+
+
+

11.3. Signed, Unsigned, Hex and Byte

+
+

Cgreen attempts to handle primitive type comparisons with a single constraint, is_equal_to(). +This means that it must store the actual and expected values in a form that will accomodate all possible values that primitive types might take, typically an intptr_t.

+
+
+

This might sometimes cause unexpected comparisons since all actual values will be cast to match intptr_t, which is a signed value. +E.g.

+
+
+
+
Ensure(Char, can_compare_byte) {
+  char chars[4] = {0xaa, 0xaa, 0xaa, 0};
+  assert_that(chars[0], is_equal_to(0xaa));
+}
+
+
+
+

On a system which considers char to be signed this will cause the +following Cgreen assertion error:

+
+
+
+
char_tests.c:11: Failure: Char -> can_compare_byte
+        Expected [chars[0]] to [equal] [0xaa]
+                actual value:                   [-86]
+                expected value:                 [170]
+
+
+
+

This is caused by the C rules forcing an implicit cast of the signed char to intptr_t by sign-extension. +This might not be what you expected. +The correct solution, by any standard, is to cast the actual value to unsigned char which will then be interpreted correctly. +And the test passes.

+
+
+ + + + + +
+ + +Casting to unsigned will not always suffice since that is interpreted as unsigned int which will cause a sign-extension from the signed char and might or might not work depending on the size of int on your machine. +
+
+
+

In order to reveal what really happens you might want to see the actual and expected values in hex. This can easily be done with the is_equal_to_hex().

+
+
+
+
Ensure(Char, can_compare_byte) {
+  char chars[4] = {0xaa, 0xaa, 0xaa, 0};
+  assert_that(chars[0], is_equal_to_hex(0xaa));
+}
+
+
+
+

This might make the mistake easier to spot:

+
+
+
+
char_tests.c:11: Failure: Char -> can_compare_byte
+        Expected [chars[0]] to [equal] [0xaa]
+        actual value:                   [0xfffffffffffffaa]
+        expected value:                 [0xaa]
+
+
+
+
+

11.4. Cgreen and Coverage

+
+

Cgreen is compatible with coverage tools, in particular gcov/lcov. +So generating coverage data for your application should be straight forward.

+
+
+

This is what you need to do (using gcc or clang):

+
+
+
    +
  • +

    compile with -ftest-coverage and -fprofile-arcs

    +
  • +
  • +

    run tests

    +
  • +
  • +

    lcov --directory . --capture --output-file coverage.info

    +
  • +
  • +

    genhtml -o coverage coverage.info

    +
  • +
+
+
+

Your coverage data will be available in coverage/index.html.

+
+
+
+

11.5. Garbled Output

+
+

If the output from your Cgreen based tests appear garbled or duplicated, this can be caused by the way Cgreen terminates its test-running child process. +In many unix-like environments the termination of a child process should be done with _exit(). +However, this interfers severily with the ability to collect coverage data. +As this is important for many of us, Cgreen instead terminates its child process with the much cruder exit() (note: no underscore).

+
+
+

Under rare circumstances this might have the unwanted effect of output becoming garbled and/or duplicated.

+
+
+

If this happens you can change that behaviour using an environment variable CGREEN_CHILD_EXIT_WITH__EXIT (note: two underscores). +If set, Cgreen will terminate its test-running child process with the more POSIX-compliant _exit(). +But as mentioned before, this is, at least at this point in time, incompatible with collecting coverage data.

+
+
+

So, it’s coverage or POSIX-correct child exits and guaranteed output consistency. +You can’t have both…​

+
+
+
+
+
+

Appendix A: Legacy Style Assertions

+
+
+

Cgreen have been around for a while, developed and matured. +There is an older style of assertions that was the initial version, a style that we now call the 'legacy style', because it was more aligned with the original, now older, unit test frameworks. +If you are not interested in historical artifacts, I recommend that you skip this section.

+
+
+

But for completeness of documentation, here are the legacy style assertion macros:

+
+ ++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Assertion

Description

assert_true(boolean)

Passes if boolean evaluates true

assert_false(boolean)

Fails if boolean evaluates true

assert_equal(first, second)

Passes if 'first == second'

assert_not_equal(first, second)

Passes if 'first != second'

assert_string_equal(char *, char *)

Uses 'strcmp()' and passes if + the strings are equal

assert_string_not_equal(char *, char *)

Uses 'strcmp()' and fails + if the strings are equal

+
+

Each assertion has a default message comparing the two values. +If you want to substitute your own failure messages, then you must use the *_with_message() counterparts…​

+
+ +++ + + + + + + + + + + + + + + + + + + + + + + + +

Assertion

assert_true_with_message(boolean, message, …​)

assert_false_with_message(boolean, message, …​)

assert_equal_with_message(tried, expected, message, …​)

assert_not_equal_with_message(tried, unexpected, message, …​)

assert_string_equal_with_message(char *, char *, message, …​)

assert_string_not_equal_with_message(char *, char *, message, …​)

+
+

All these assertions have an additional char * message parameter, which is the message you wished to display on failure. +If this is set to NULL, then the default message is shown instead. +The most useful assertion from this group is assert_true_with_message() as you can use that to create your own assertion functions with your own messages.

+
+
+

Actually the assertion macros have variable argument lists. +The failure message acts like the template in printf(). +We could change the test above to be…​

+
+
+
+
Ensure(strlen_of_hello_is_five) {
+    const char *greeting = "Hello";
+    int length = strlen(greeting);
+    assert_equal_with_message(length, 5, "[%s] should be 5, but was %d", greeting, length);
+}
+
+
+
+

This should produce a slightly more user friendly message when things go wrong. +But, actually, Cgreens default messages are so good that you are encouraged to skip the legacy style and go for the more modern constraints style assertions. +This is particularly true when you use the BDD style test notation.

+
+
+ + + + + +
+ + +We strongly recommend the use of BDD Style notation with constraints based assertions. +
+
+
+
+
+

Appendix B: Release History

+
+
+

In this section only the introduction or changes of major features are listed, and thus only MINOR versions. +For a detailed log of features, enhancements and bug fixes visit the projects repository on GitHub, https://github.com/cgreen-devs/cgreen.

+
+
+

Since 1.4.1 Cgreen has included the following C pre-processer definition variables

+
+
+
    +
  • +

    CGREEN_VERSION, a SemVer string

    +
  • +
  • +

    CGREEN_VERSION_MAJOR

    +
  • +
  • +

    CGREEN_VERSION_MINOR

    +
  • +
  • +

    CGREEN_VERSION_PATCH

    +
  • +
+
+
+

You can use them to conditionally check for Cgreen features introduced as declared in the following sections.

+
+
+

Since 1.2.0 Cgreen has featured a public version variable in the loaded library, cgreen_library_version. +This is mainly used by the cgreen-runner to present version of the loaded library, but it can also be used to check for availability of features in the same way.

+
+
+

B.1. 1.6.0

+
+
    +
  • +

    Reverted use of libbfd introduced in 1.5.0 due to portability issues and Debian deeming it to be a serious bug due to libbfd not having a stable interface

    +
  • +
+
+
+
+

B.2. 1.5.1

+
+
    +
  • +

    Fixed a problem with ends_with_string() which randomly crashed

    +
  • +
+
+
+
+

B.3. 1.5.0

+
+
    +
  • +

    Replaced calling nm with BFD library calls, this makes the cgreen-runner a bit more fiddly to build on some systems

    +
  • +
  • +

    Introduced will_capture_parameter()

    +
  • +
+
+
+
+

B.4. 1.4.0

+
+
    +
  • +

    A memory leak in will_return_by_value() was fixed but now requires user deallocation.

    +
  • +
+
+
+
+

B.5. 1.3.0

+
+
    +
  • +

    Renamed CgreenValueType values to avoid clash, now all start with CGREEN_

    +
  • +
+
+
+
+

B.6. 1.2.0

+
+
    +
  • +

    Introduced will_return_by_value()

    +
  • +
  • +

    Introduced with_side_effect()

    +
  • +
+
+
+
+

B.7. 1.1.0

+
+

None.

+
+
+
+

B.8. 1.0.0

+
+

First official non-beta release.

+
+
+
+
+
+

Appendix C: License

+
+
+

Copyright (c) 2006-2021, Cgreen Development Team and contributors
+(https://github.com/cgreen-devs/cgreen/graphs/contributors)

+
+
+

Permission to use, copy, modify, and/or distribute this software and +its documentation for any purpose with or without fee is hereby +granted, provided that the above copyright notice and this permission +notice appear in all copies, regardless of form, including printed and +compiled.

+
+
+

THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

+
+
+
+
+

Appendix D: Acknowledgements

+
+
+

Thanks to

+
+
+ +
+
+

Thanks also go to @gardenia, @d-meiser, @stevemadsenblippar and others for their contributions.

+
+
+
+
+ + + + + \ No newline at end of file diff --git a/cgreen_asciidoc.conf b/cgreen_asciidoc.conf new file mode 100644 index 00000000..205fab20 --- /dev/null +++ b/cgreen_asciidoc.conf @@ -0,0 +1,3 @@ +[attributes] +toclevels=3 +numbered= diff --git a/cheat-sheet.md b/cheat-sheet.md new file mode 100644 index 00000000..0946d734 --- /dev/null +++ b/cheat-sheet.md @@ -0,0 +1,98 @@ +# Cgreen Cheat Sheet + +## Pre-amble + + #include + #include + + Describe( ); + BeforeEach( ) {} + AfterEach( ) {} + +## A test + + Ensure( , ) { + assert_that( ); + assert_that( , ); + assert_that_double(...); + } + +## Constraints + + is_true + is_false + is_null + is_non_null + is_equal_to( ) + is_equal_to_hex( ) + is_not_equal_to( ) + is_greater_than( ) + is_less_than( ) + +### Structs and general data + + is_equal_to_contents_of( , ) + is_not_equal_to_contents_of( , ) + +### Strings + + is_equal_to_string( ) + is_not_equal_to_string( ) + contains_string( ) + does_not_contain_string( ) + begins_with_string( ) + does_not_begin_with_string( ) + ends_with_string( ) + does_not_end_with_string( ) + +### Floating point values (Doubles) + + is_equal_to_double( ) + is_not_equal_to_double( ) + is_less_than_double( ) + is_greater_than_double( ) + + significant_figures_for_assert_double_are( ) + +## Mocks + + ( ) { + mock( ); + mock( box_double( ) ); + return ( ) mock( ); + return mock_double( ); + } + + Ensure( ) { + expect( {, when(, ) } + [, ] + [, times( ) ] ); + always_expect(...); + never_expect(...); + } + + cgreen_mocks_are( strict_mocks | loose_mocks | learning_mocks ); + +### Returns + + will_return( ) + will_return_double( ) + will_return_by_value( , ) + with_side_effect( , ) + will_set_contents_of_parameter( , , ) + +## C++ + + namespace cgreen; + assert_throws( , ); + +## Minimal Makefile + + all: $(UNIT)_tests.so + cgreen-runner $^ + + $(UNIT)_tests.so: $(UNIT)_tests.o $(UNIT).o + $(CC) -shared -o $@ $^ -lcgreen + + %.o: %.c + $(CC) $(CFLAGS) -fPIC -c -o $@ $^ diff --git a/doxy.config.in b/doxy.config.in new file mode 100644 index 00000000..6a30ba48 --- /dev/null +++ b/doxy.config.in @@ -0,0 +1,1539 @@ +# Doxyfile 1.5.8 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project +# +# All text after a hash (#) is considered a comment and will be ignored +# The format is: +# TAG = value [value, ...] +# For lists items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (" ") + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the config file +# that follow. The default is UTF-8 which is also the encoding used for all +# text before the first occurrence of this tag. Doxygen uses libiconv (or the +# iconv built into libc) for the transcoding. See +# http://www.gnu.org/software/libiconv for the list of possible encodings. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded +# by quotes) that should identify the project. + +PROJECT_NAME = @APPLICATION_NAME@ + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or +# if some version control system is used. + +PROJECT_NUMBER = @APPLICATION_VERSION@ + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + +OUTPUT_DIRECTORY = @CMAKE_CURRENT_BINARY_DIR@ + +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create +# 4096 sub-directories (in 2 levels) under the output directory of each output +# format and will distribute the generated files over these directories. +# Enabling this option can be useful when feeding doxygen a huge amount of +# source files, where putting all generated files in the same directory would +# otherwise cause performance problems for the file system. + +CREATE_SUBDIRS = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# The default language is English, other supported languages are: +# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, +# Croatian, Czech, Danish, Dutch, Farsi, Finnish, French, German, Greek, +# Hungarian, Italian, Japanese, Japanese-en (Japanese with English messages), +# Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, Polish, +# Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak, Slovene, +# Spanish, Swedish, and Ukrainian. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will +# include brief member descriptions after the members that are listed in +# the file and class documentation (similar to JavaDoc). +# Set to NO to disable this. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend +# the brief description of a member or function before the detailed description. +# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator +# that is used to form the text in various listings. Each string +# in this list, if found as the leading text of the brief description, will be +# stripped from the text and the result after processing the whole list, is +# used as the annotated text. Otherwise, the brief description is used as-is. +# If left blank, the following values are used ("$name" is automatically +# replaced with the name of the entity): "The $name class" "The $name widget" +# "The $name file" "is" "provides" "specifies" "contains" +# "represents" "a" "an" "the" + +ABBREVIATE_BRIEF = "The $name class" \ + "The $name widget" \ + "The $name file" \ + is \ + provides \ + specifies \ + contains \ + represents \ + a \ + an \ + the + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief +# description. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full +# path before files name in the file list and in the header files. If set +# to NO the shortest path that makes the file name unique will be used. + +FULL_PATH_NAMES = YES + +# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag +# can be used to strip a user-defined part of the path. Stripping is +# only done if one of the specified strings matches the left-hand part of +# the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the +# path to strip. + +STRIP_FROM_PATH = @PROJECT_SOURCE_DIR@ + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of +# the path mentioned in the documentation of a class, which tells +# the reader which header file to include in order to use a class. +# If left blank only the name of the header file containing the class +# definition is used. Otherwise one should specify the include paths that +# are normally passed to the compiler using the -I flag. + +STRIP_FROM_INC_PATH = @PROJECT_SOURCE_DIR@ + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter +# (but less readable) file names. This can be useful is your file systems +# doesn't support long names like on DOS, Mac, or CD-ROM. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen +# will interpret the first line (until the first dot) of a JavaDoc-style +# comment as the brief description. If set to NO, the JavaDoc +# comments will behave just like regular Qt-style comments +# (thus requiring an explicit @brief command for a brief description.) + +JAVADOC_AUTOBRIEF = YES + +# If the QT_AUTOBRIEF tag is set to YES then Doxygen will +# interpret the first line (until the first dot) of a Qt-style +# comment as the brief description. If set to NO, the comments +# will behave just like regular Qt-style comments (thus requiring +# an explicit \brief command for a brief description.) + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen +# treat a multi-line C++ special comment block (i.e. a block of //! or /// +# comments) as a brief description. This used to be the default behaviour. +# The new default is to treat a multi-line C++ comment block as a detailed +# description. Set this tag to YES if you prefer the old behaviour instead. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it +# re-implements. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce +# a new page for each member. If set to NO, the documentation of a member will +# be part of the file/class/namespace that contains it. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. +# Doxygen uses this value to replace tabs by spaces in code fragments. + +TAB_SIZE = 2 + +# This tag can be used to specify a number of aliases that acts +# as commands in the documentation. An alias has the form "name=value". +# For example adding "sideeffect=\par Side Effects:\n" will allow you to +# put the command \sideeffect (or @sideeffect) in the documentation, which +# will result in a user-defined paragraph with heading "Side Effects:". +# You can put \n's in the value part of an alias to insert newlines. + +ALIASES = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C +# sources only. Doxygen will then generate output that is more tailored for C. +# For instance, some of the names that are used will be different. The list +# of all members will be omitted, etc. + +OPTIMIZE_OUTPUT_FOR_C = YES + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java +# sources only. Doxygen will then generate output that is more tailored for +# Java. For instance, namespaces will be presented as packages, qualified +# scopes will look different, etc. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources only. Doxygen will then generate output that is more tailored for +# Fortran. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for +# VHDL. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Doxygen selects the parser to use depending on the extension of the files it parses. +# With this tag you can assign which parser to use for a given extension. +# Doxygen has a built-in mapping, but you can override or extend it using this tag. +# The format is ext=language, where ext is a file extension, and language is one of +# the parsers supported by doxygen: IDL, Java, Javascript, C#, C, C++, D, PHP, +# Objective-C, Python, Fortran, VHDL, C, C++. For instance to make doxygen treat +# .inc files as Fortran files (default is PHP), and .f files as C (default is Fortran), +# use: inc=Fortran f=C + +EXTENSION_MAPPING = + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should +# set this tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. +# func(std::string) {}). This also make the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. + +BUILTIN_STL_SUPPORT = NO + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. +# Doxygen will parse them like normal C++ but will assume all classes use public +# instead of private inheritance when no explicit protection keyword is present. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate getter +# and setter methods for a property. Setting this option to YES (the default) +# will make doxygen to replace the get and set methods by a property in the +# documentation. This will only work if the methods are indeed getting or +# setting a simple type. If this is not the case, or you want to show the +# methods anyway, you should set this option to NO. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. + +DISTRIBUTE_GROUP_DOC = NO + +# Set the SUBGROUPING tag to YES (the default) to allow class member groups of +# the same type (for instance a group of public functions) to be put as a +# subgroup of that type (e.g. under the Public Functions section). Set it to +# NO to prevent subgrouping. Alternatively, this can be done per class using +# the \nosubgrouping command. + +SUBGROUPING = YES + +# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum +# is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically +# be useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. + +TYPEDEF_HIDES_STRUCT = NO + +# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to +# determine which symbols to keep in memory and which to flush to disk. +# When the cache is full, less often used symbols will be written to disk. +# For small to medium size projects (<1000 input files) the default value is +# probably good enough. For larger projects a too small cache size can cause +# doxygen to be busy swapping symbols to and from disk most of the time +# causing a significant performance penality. +# If the system has enough physical memory increasing the cache will improve the +# performance by keeping more symbols in memory. Note that the value works on +# a logarithmic scale so increasing the size by one will rougly double the +# memory usage. The cache size is given by this formula: +# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, +# corresponding to a cache size of 2^16 = 65536 symbols + +SYMBOL_CACHE_SIZE = 0 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. +# Private class members and static file members will be hidden unless +# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES + +EXTRACT_ALL = NO + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_STATIC tag is set to YES all static members of a file +# will be included in the documentation. + +EXTRACT_STATIC = NO + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) +# defined locally in source files will be included in the documentation. +# If set to NO only classes defined in header files are included. + +EXTRACT_LOCAL_CLASSES = NO + +# This flag is only useful for Objective-C code. When set to YES local +# methods, which are defined in the implementation section but not in +# the interface are included in the documentation. +# If set to NO (the default) only methods in the interface are included. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base +# name of the file that contains the anonymous namespace. By default +# anonymous namespace are hidden. + +EXTRACT_ANON_NSPACES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) these members will be included in the +# various overviews, but no documentation section is generated. +# This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_MEMBERS = YES + +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. +# If set to NO (the default) these classes will be included in the various +# overviews. This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_CLASSES = YES + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all +# friend (class|struct|union) declarations. +# If set to NO (the default) these declarations will be included in the +# documentation. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any +# documentation blocks found inside the body of a function. +# If set to NO (the default) these blocks will be appended to the +# function's detailed documentation block. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation +# that is typed after a \internal command is included. If the tag is set +# to NO (the default) then the documentation will be excluded. +# Set it to YES to include the internal documentation. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate +# file names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. + +CASE_SENSE_NAMES = YES + +# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen +# will show members with their full class and namespace scopes in the +# documentation. If set to YES the scope will be hidden. + +HIDE_SCOPE_NAMES = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put a list of the files that are included by a file in the documentation +# of that file. + +SHOW_INCLUDE_FILES = YES + +# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] +# is inserted in the documentation for inline members. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen +# will sort the (detailed) documentation of file and class members +# alphabetically by member name. If set to NO the members will appear in +# declaration order. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the +# brief documentation of file, namespace and class members alphabetically +# by member name. If set to NO (the default) the members will appear in +# declaration order. + +SORT_BRIEF_DOCS = YES + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the +# hierarchy of group names into alphabetical order. If set to NO (the default) +# the group names will appear in their defined order. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be +# sorted by fully-qualified names, including namespaces. If set to +# NO (the default), the class list will be sorted only by class name, +# not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the +# alphabetical list. + +SORT_BY_SCOPE_NAME = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or +# disable (NO) the todo list. This list is created by putting \todo +# commands in the documentation. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or +# disable (NO) the test list. This list is created by putting \test +# commands in the documentation. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or +# disable (NO) the bug list. This list is created by putting \bug +# commands in the documentation. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or +# disable (NO) the deprecated list. This list is created by putting +# \deprecated commands in the documentation. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional +# documentation sections, marked by \if sectionname ... \endif. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines +# the initial value of a variable or define consists of for it to appear in +# the documentation. If the initializer consists of more lines than specified +# here it will be hidden. Use a value of 0 to hide initializers completely. +# The appearance of the initializer of individual variables and defines in the +# documentation can be controlled using \showinitializer or \hideinitializer +# command in the documentation regardless of this setting. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated +# at the bottom of the documentation of classes and structs. If set to YES the +# list will mention the files that were used to generate the documentation. + +SHOW_USED_FILES = YES + +# If the sources in your project are distributed over multiple directories +# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy +# in the documentation. The default is NO. + +SHOW_DIRECTORIES = NO + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. +# This will remove the Files entry from the Quick Index and from the +# Folder Tree View (if specified). The default is YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the +# Namespaces page. +# This will remove the Namespaces entry from the Quick Index +# and from the Folder Tree View (if specified). The default is YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command , where is the value of +# the FILE_VERSION_FILTER tag, and is the name of an input file +# provided by doxygen. Whatever the program writes to standard output +# is used as the file version. See the manual for examples. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed by +# doxygen. The layout file controls the global structure of the generated output files +# in an output format independent way. The create the layout file that represents +# doxygen's defaults, run doxygen with the -l option. You can optionally specify a +# file name after the option, if omitted DoxygenLayout.xml will be used as the name +# of the layout file. + +LAYOUT_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated +# by doxygen. Possible values are YES and NO. If left blank NO is used. + +QUIET = YES + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + +WARNINGS = YES + +# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings +# for undocumented members. If EXTRACT_ALL is set to YES then this flag will +# automatically be disabled. + +WARN_IF_UNDOCUMENTED = YES + +# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some +# parameters in a documented function, or documenting parameters that +# don't exist or using markup commands wrongly. + +WARN_IF_DOC_ERROR = YES + +# This WARN_NO_PARAMDOC option can be abled to get warnings for +# functions that are documented, but have no documentation for their parameters +# or return value. If set to NO (the default) doxygen will only warn about +# wrong or incomplete parameter documentation, but not about the absence of +# documentation. + +WARN_NO_PARAMDOC = NO + +# The WARN_FORMAT tag determines the format of the warning messages that +# doxygen can produce. The string should contain the $file, $line, and $text +# tags, which will be replaced by the file and line number from which the +# warning originated and the warning text. Optionally the format may contain +# $version, which will be replaced by the version of the file (if it could +# be obtained via FILE_VERSION_FILTER) + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning +# and error messages should be written. If left blank the output is written +# to stderr. + +WARN_LOGFILE = @CMAKE_CURRENT_BINARY_DIR@/doxy.log + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag can be used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories +# with spaces. + +INPUT = @PROJECT_SOURCE_DIR@/include \ + @PROJECT_SOURCE_DIR@/src + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is +# also the default input encoding. Doxygen uses libiconv (or the iconv built +# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for +# the list of possible encodings. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank the following patterns are tested: +# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx +# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90 + +FILE_PATTERNS = *.cpp \ + *.cc \ + *.c \ + *.h \ + *.hh \ + *.hpp \ + *.dox + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + +RECURSIVE = YES + +# The EXCLUDE tag can be used to specify files and/or directories that should +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used select whether or not files or +# directories that are symbolic links (a Unix filesystem feature) are excluded +# from the input. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. Note that the wildcards are matched +# against the file with absolute path, so to exclude all test directories +# for example use the pattern */test/* + +EXCLUDE_PATTERNS = */.git/* \ + */cmake/* \ + */doc/* \ + */build/* + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + +EXAMPLE_PATH = @PROJECT_SOURCE_DIR@/tests \ + @PROJECT_SOURCE_DIR@ + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + +EXAMPLE_PATTERNS = *.cpp \ + *.cc \ + *.h \ + *.hh \ + INSTALL \ + DEPENDENCIES \ + CHANGELOG \ + LICENSE \ + LGPL + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude +# commands irrespective of the value of the RECURSIVE tag. +# Possible values are YES and NO. If left blank NO is used. + +EXAMPLE_RECURSIVE = YES + +# The IMAGE_PATH tag can be used to specify one or more files or +# directories that contain image that are included in the documentation (see +# the \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command , where +# is the value of the INPUT_FILTER tag, and is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. +# If FILTER_PATTERNS is specified, this tag will be +# ignored. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. +# Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. +# The filters are a list of the form: +# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further +# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER +# is applied to all files. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will be used to filter the input files when producing source +# files to browse (i.e. when SOURCE_BROWSER is set to YES). + +FILTER_SOURCE_FILES = NO + +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will +# be generated. Documented entities will be cross-referenced with these sources. +# Note: To get rid of all source code in the generated output, make sure also +# VERBATIM_HEADERS is set to NO. + +SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body +# of functions and classes directly in the documentation. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct +# doxygen to hide any special comment blocks from generated source code +# fragments. Normal C and C++ comments will always remain visible. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES +# then for each documented function all documented +# functions referencing it will be listed. + +REFERENCED_BY_RELATION = YES + +# If the REFERENCES_RELATION tag is set to YES +# then for each documented function all documented entities +# called/used by that function will be listed. + +REFERENCES_RELATION = YES + +# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) +# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from +# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will +# link to the source code. +# Otherwise they will link to the documentation. + +REFERENCES_LINK_SOURCE = YES + +# If the USE_HTAGS tag is set to YES then the references to source code +# will point to the HTML generated by the htags(1) tool instead of doxygen +# built-in source browser. The htags tool is part of GNU's global source +# tagging system (see http://www.gnu.org/software/global/global.html). You +# will need version 4.8.6 or higher. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen +# will generate a verbatim copy of the header file for each class for +# which an include is specified. Set to NO to disable this. + +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index +# of all compounds will be generated. Enable this if the project +# contains a lot of classes, structs, unions or interfaces. + +ALPHABETICAL_INDEX = YES + +# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then +# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns +# in which this list will be split (can be a number in the range [1..20]) + +COLS_IN_ALPHA_INDEX = 2 + +# In case all classes in a project start with a common prefix, all +# classes will be put under the same header in the alphabetical index. +# The IGNORE_PREFIX tag can be used to specify one or more prefixes that +# should be ignored while generating the index headers. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `html' will be used as the default path. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for +# each generated HTML page (for example: .htm,.php,.asp). If it is left blank +# doxygen will generate files with .html extension. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading +# style sheet that is used by each HTML page. It can be used to +# fine-tune the look of the HTML output. If the tag is left blank doxygen +# will generate a default style sheet. Note that doxygen will try to copy +# the style sheet file to the HTML output directory, so don't put your own +# stylesheet in the HTML output directory as well, or it will be erased! + +HTML_STYLESHEET = + +# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, +# files or namespaces will be aligned in HTML using tables. If set to +# NO a bullet list will be used. + +HTML_ALIGN_MEMBERS = YES + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. For this to work a browser that supports +# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox +# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). + +HTML_DYNAMIC_SECTIONS = NO + +# If the GENERATE_DOCSET tag is set to YES, additional index files +# will be generated that can be used as input for Apple's Xcode 3 +# integrated development environment, introduced with OSX 10.5 (Leopard). +# To create a documentation set, doxygen will generate a Makefile in the +# HTML output directory. Running make will produce the docset in that +# directory and running "make install" will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find +# it at startup. +# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html for more information. + +GENERATE_DOCSET = NO + +# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the +# feed. A documentation feed provides an umbrella under which multiple +# documentation sets from a single provider (such as a company or product suite) +# can be grouped. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that +# should uniquely identify the documentation set bundle. This should be a +# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen +# will append .docset to the name. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) +# of the generated HTML documentation. + +GENERATE_HTMLHELP = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can +# be used to specify the file name of the resulting .chm file. You +# can add a path in front of the file if the result should not be +# written to the html output directory. + +CHM_FILE = + +# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can +# be used to specify the location (absolute path including file name) of +# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run +# the HTML help compiler on the generated index.hhp. + +HHC_LOCATION = + +# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag +# controls if a separate .chi index file is generated (YES) or that +# it should be included in the master .chm file (NO). + +GENERATE_CHI = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING +# is used to encode HtmlHelp index (hhk), content (hhc) and project file +# content. + +CHM_INDEX_ENCODING = + +# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag +# controls whether a binary table of contents is generated (YES) or a +# normal table of contents (NO) in the .chm file. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members +# to the contents of the HTML help documentation and to the tree view. + +TOC_EXPAND = NO + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and QHP_VIRTUAL_FOLDER +# are set, an additional index file will be generated that can be used as input for +# Qt's qhelpgenerator to generate a Qt Compressed Help (.qch) of the generated +# HTML documentation. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can +# be used to specify the file name of the resulting .qch file. +# The path specified is relative to the HTML output folder. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#namespace + +QHP_NAMESPACE = + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#virtual-folders + +QHP_VIRTUAL_FOLDER = doc + +# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to add. +# For more information please see +# http://doc.trolltech.com/qthelpproject.html#custom-filters + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the custom filter to add.For more information please see +# Qt Help Project / Custom Filters. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this project's +# filter section matches. +# Qt Help Project / Filter Attributes. + +QHP_SECT_FILTER_ATTRS = + +# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can +# be used to specify the location of Qt's qhelpgenerator. +# If non-empty doxygen will try to run qhelpgenerator on the generated +# .qhp file. + +QHG_LOCATION = + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index at +# top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. + +DISABLE_INDEX = NO + +# This tag can be used to set the number of enum values (range [1..20]) +# that doxygen will group on one line in the generated HTML documentation. + +ENUM_VALUES_PER_LINE = 4 + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. +# If the tag value is set to FRAME, a side panel will be generated +# containing a tree-like index structure (just like the one that +# is generated for HTML Help). For this to work a browser that supports +# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, +# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are +# probably better off using the HTML help feature. Other possible values +# for this tag are: HIERARCHIES, which will generate the Groups, Directories, +# and Class Hierarchy pages using a tree view instead of an ordered list; +# ALL, which combines the behavior of FRAME and HIERARCHIES; and NONE, which +# disables this behavior completely. For backwards compatibility with previous +# releases of Doxygen, the values YES and NO are equivalent to FRAME and NONE +# respectively. + +GENERATE_TREEVIEW = NO + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be +# used to set the initial width (in pixels) of the frame in which the tree +# is shown. + +TREEVIEW_WIDTH = 250 + +# Use this tag to change the font size of Latex formulas included +# as images in the HTML documentation. The default is 10. Note that +# when you change the font size after a successful doxygen run you need +# to manually remove any form_*.png images from the HTML output directory +# to force them to be regenerated. + +FORMULA_FONTSIZE = 10 + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = NO + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. If left blank `latex' will be used as the default command name. + +LATEX_CMD_NAME = @LATEX_COMPILER@ + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to +# generate index for LaTeX. If left blank `makeindex' will be used as the +# default command name. + +MAKEINDEX_CMD_NAME = @MAKEINDEX_COMPILER@ + +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, a4wide, letter, legal and +# executive. If left blank a4wide will be used. + +PAPER_TYPE = a4 + +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# packages that should be included in the LaTeX output. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a +# standard header. Notice: only use this tag if you know what you are doing! + +LATEX_HEADER = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. + +PDF_HYPERLINKS = YES + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. + +USE_PDFLATEX = YES + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. + +LATEX_BATCHMODE = YES + +# If LATEX_HIDE_INDICES is set to YES then doxygen will not +# include the index chapters (such as File Index, Compound Index, etc.) +# in the output. + +LATEX_HIDE_INDICES = NO + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output +# The RTF output is optimized for Word 97 and may not look very pretty with +# other RTF readers or editors. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `rtf' will be used as the default path. + +RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES Doxygen generates more compact +# RTF documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using WORD or other +# programs which support those fields. +# Note: wordpad (write) and others do not support links. + +RTF_HYPERLINKS = NO + +# Load stylesheet definitions from file. Syntax is similar to doxygen's +# config file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an rtf document. +# Syntax is similar to doxygen's config file. + +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# generate man pages + +GENERATE_MAN = YES + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `man' will be used as the default path. + +MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to +# the generated man pages (default is the subroutine's section .3) + +MAN_EXTENSION = .3 + +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, +# then it will generate one additional man file for each entity +# documented in the real man page(s). These additional files +# only source the real man page, but without them the man command +# would be unable to find the correct page. The default is NO. + +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES Doxygen will +# generate an XML file that captures the structure of +# the code including all documentation. + +GENERATE_XML = NO + +# The XML_OUTPUT tag is used to specify where the XML pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `xml' will be used as the default path. + +XML_OUTPUT = xml + +# The XML_SCHEMA tag can be used to specify an XML schema, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_SCHEMA = + +# The XML_DTD tag can be used to specify an XML DTD, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_DTD = + +# If the XML_PROGRAMLISTING tag is set to YES Doxygen will +# dump the program listings (including syntax highlighting +# and cross-referencing information) to the XML output. Note that +# enabling this will significantly increase the size of the XML output. + +XML_PROGRAMLISTING = YES + +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will +# generate an AutoGen Definitions (see autogen.sf.net) file +# that captures the structure of the code including all +# documentation. Note that this feature is still experimental +# and incomplete at the moment. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +# If the GENERATE_PERLMOD tag is set to YES Doxygen will +# generate a Perl module file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the +# moment. + +GENERATE_PERLMOD = NO + +# If the PERLMOD_LATEX tag is set to YES Doxygen will generate +# the necessary Makefile rules, Perl scripts and LaTeX code to be able +# to generate PDF and DVI output from the Perl module output. + +PERLMOD_LATEX = NO + +# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be +# nicely formatted so it can be parsed by a human reader. +# This is useful +# if you want to understand what is going on. +# On the other hand, if this +# tag is set to NO the size of the Perl module output will be much smaller +# and Perl will parse it just the same. + +PERLMOD_PRETTY = YES + +# The names of the make variables in the generated doxyrules.make file +# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. +# This is useful so different doxyrules.make files included by the same +# Makefile don't overwrite each other's variables. + +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled +# way by setting EXPAND_ONLY_PREDEF to YES. + +MACRO_EXPANSION = NO + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED and EXPAND_AS_DEFINED tags. + +EXPAND_ONLY_PREDEF = NO + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# in the INCLUDE_PATH (see below) will be search if a #include is found. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will +# be used. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). If the definition and the = are +# omitted =1 is assumed. To prevent a macro definition from being +# undefined via #undef or recursively expanded use the := operator +# instead of the = operator. + +PREDEFINED = + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. +# Use the PREDEFINED tag if you want to use a different macro definition. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all function-like macros that are alone +# on a line, have an all uppercase name, and do not end with a semicolon. Such +# function macros are typically used for boiler-plate code, and will confuse +# the parser if not removed. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES option can be used to specify one or more tagfiles. +# Optionally an initial location of the external documentation +# can be added for each tagfile. The format of a tag file without +# this location is as follows: +# +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where "loc1" and "loc2" can be relative or absolute paths or +# URLs. If a location is present for each tag, the installdox tool +# does not have to be run to correct the links. +# Note that each tag file must have a unique name +# (where the name does NOT include the path) +# If a tag file is not located in the directory in which doxygen +# is run, you must also specify the path to the tagfile here. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + +GENERATE_TAGFILE = @CMAKE_CURRENT_BINARY_DIR@/html/@PROJECT_NAME@.TAGFILE + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + +ALLEXTERNALS = YES + +# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will +# be listed. + +EXTERNAL_GROUPS = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of `which perl'). + +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will +# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base +# or super classes. Setting the tag to NO turns the diagrams off. Note that +# this option is superseded by the HAVE_DOT option below. This is only a +# fallback. It is recommended to install and use dot, since it yields more +# powerful graphs. + +CLASS_DIAGRAMS = NO + +# You can define message sequence charts within doxygen comments using the \msc +# command. Doxygen will then run the mscgen tool (see +# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the +# documentation. The MSCGEN_PATH tag allows you to specify the directory where +# the mscgen tool resides. If left empty the tool is assumed to be found in the +# default search path. + +MSCGEN_PATH = + +# If set to YES, the inheritance and collaboration graphs will hide +# inheritance and usage relations if the target is undocumented +# or is not a class. + +HIDE_UNDOC_RELATIONS = YES + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# have no effect if this option is set to NO (the default) + +HAVE_DOT = @DOXYGEN_DOT_FOUND@ + +# By default doxygen will write a font called FreeSans.ttf to the output +# directory and reference it in all dot files that doxygen generates. This +# font does not include all possible unicode characters however, so when you need +# these (or just want a differently looking font) you can specify the font name +# using DOT_FONTNAME. You need need to make sure dot is able to find the font, +# which can be done by putting it in a standard location or by setting the +# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory +# containing the font. + +DOT_FONTNAME = FreeSans + +# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. +# The default size is 10pt. + +DOT_FONTSIZE = 10 + +# By default doxygen will tell dot to use the output directory to look for the +# FreeSans.ttf font (which doxygen will put there itself). If you specify a +# different font using DOT_FONTNAME you can set the path where dot +# can find it using this tag. + +DOT_FONTPATH = + +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the +# the CLASS_DIAGRAMS tag to NO. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and +# class references variables) of the class with other documented classes. + +COLLABORATION_GRAPH = YES + +# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for groups, showing the direct groups dependencies + +GROUP_GRAPHS = YES + +# If the UML_LOOK tag is set to YES doxygen will generate inheritance and +# collaboration diagrams in a style similar to the OMG's Unified Modeling +# Language. + +UML_LOOK = NO + +# If set to YES, the inheritance and collaboration graphs will show the +# relations between templates and their instances. + +TEMPLATE_RELATIONS = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT +# tags are set to YES then doxygen will generate a graph for each documented +# file showing the direct and indirect include dependencies of the file with +# other documented files. + +INCLUDE_GRAPH = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and +# HAVE_DOT tags are set to YES then doxygen will generate a graph for each +# documented header file showing the documented files that directly or +# indirectly include this file. + +INCLUDED_BY_GRAPH = YES + +# If the CALL_GRAPH and HAVE_DOT options are set to YES then +# doxygen will generate a call dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable call graphs +# for selected functions only using the \callgraph command. + +CALL_GRAPH = YES + +# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then +# doxygen will generate a caller dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable caller +# graphs for selected functions only using the \callergraph command. + +CALLER_GRAPH = YES + +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# will graphical hierarchy of all classes instead of a textual one. + +GRAPHICAL_HIERARCHY = YES + +# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES +# then doxygen will show the dependencies a directory has on other directories +# in a graphical way. The dependency relations are determined by the #include +# relations between the files in the directories. + +DIRECTORY_GRAPH = YES + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. Possible values are png, jpg, or gif +# If left blank png will be used. + +DOT_IMAGE_FORMAT = png + +# The tag DOT_PATH can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found in the path. + +DOT_PATH = @DOXYGEN_DOT_EXECUTABLE_PATH@ + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the +# \dotfile command). + +DOTFILE_DIRS = + +# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of +# nodes that will be shown in the graph. If the number of nodes in a graph +# becomes larger than this value, doxygen will truncate the graph, which is +# visualized by representing a node as a red box. Note that doxygen if the +# number of direct children of the root node in a graph is already larger than +# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note +# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. + +DOT_GRAPH_MAX_NODES = 50 + +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the +# graphs generated by dot. A depth value of 3 means that only nodes reachable +# from the root by following a path via at most 3 edges will be shown. Nodes +# that lay further from the root node will be omitted. Note that setting this +# option to 1 or 2 may greatly reduce the computation time needed for large +# code bases. Also note that the size of a graph can be further restricted by +# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. + +MAX_DOT_GRAPH_DEPTH = 0 + +# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent +# background. This is disabled by default, because dot on Windows does not +# seem to support this out of the box. Warning: Depending on the platform used, +# enabling this option may lead to badly anti-aliased labels on the edges of +# a graph (i.e. they become hard to read). + +DOT_TRANSPARENT = NO + +# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output +# files in one run (i.e. multiple -o and -T options on the command line). This +# makes dot run faster, but since only newer versions of dot (>1.8.10) +# support this, this feature is disabled by default. + +DOT_MULTI_TARGETS = YES + +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and +# arrows in the dot generated graphs. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will +# remove the intermediate dot files that are used to generate +# the various graphs. + +DOT_CLEANUP = YES + +#--------------------------------------------------------------------------- +# Options related to the search engine +#--------------------------------------------------------------------------- + +# The SEARCHENGINE tag specifies whether or not a search engine should be +# used. If set to NO the values of all tags below this one will be ignored. + +SEARCHENGINE = NO diff --git a/index.html b/index.html new file mode 120000 index 00000000..b70e3e80 --- /dev/null +++ b/index.html @@ -0,0 +1 @@ +README.html \ No newline at end of file diff --git a/logo.odt b/logo.odt new file mode 100644 index 0000000000000000000000000000000000000000..6f4cb53b5fb30abcb606722150e76879572bd5fb GIT binary patch literal 18840 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

*-V0wKphJ zZ%&;dA$MTq#7x{<&@j?M|$nkg_l|L-JBYFqdzC7_&U=Lm|CF>Tw50OB5 zcrE*nC3fT^(`&jI$L!K<)9ePuy72AIjj|;y*YL^9?CK4h+uXr>+3YhD#tf(eF4fez zLHR{doRbm6!5^1CtkFXj-|5g#C4bQL_`M2+^%0t~FYFXZkntQ;qU7a;e-7VvklURQ z|0GA;>VK!8s?=U59_SA_onD=R%HggY$-`s(>45a1qiY-tV*&#ovuon2&`%0NZedKN zfxKcq0#BQ7zO9dy?L>AK9(JEpdIVE+au)+y`aN@io>rk#0f*@GlTq0sw z_(#-*R@4}1(`NcT zo=7Y@8^n12bJ2%j@j`S+StB1g0lMH56&R^$_d(TZKPm|(NZVohk{B1u&FQBSfZhx5 zf=4r_iC@t-1f0B8(Z;t>V#q5P3%T!wZ@0ZtH~I15<6(TWG=osft?$A?72|OtMe4lp zSk8rTg(xIHtZcz=lX)blVX&9h?C&>?a{4B*UcUtTl}??jb)l9*aWZ+soOfNRiJQoLO z_4pN+AsB{D!$jRS^RTm-!o3I`${RL89vm7-%zq{$$NY9#xdeJ8e;!q!}B@q zX)}?}JCA1Tif#z!yTN5dN0EJT876<(_&hR2dE|61W%c3f^6kwRJUIV?JUD-prN_ss=9o%(4L z`z^#+%J#&|Nn(%86;!X>x~iXenbKht>cdVGg{#IRsTI?KgV0jXz;5W~<;PEx31vgq zkW7vbh>H4H*OggyF&Y^}#gV0nN%c^ZR42{&BkHNu!w^Rdq?4aZdG3n`fXF^<8V%t{ z)m61~LaNt;GGEGR6gecohsC_w`mhqK6z=+ALrtLtz;GAJCe?`?7^uqiGQ!L zNus|$H!Pay#mN4cDCTn3SZ>6E!6Pm4+NN@LAUg~&^sd{4w7jfVXzX}Qer=?S{lI2H z5}A_4N9BYU#;V zi@4#aF_jm)LEodWIw&Idx(-Z568U(Vc=EzN;-e=@@|ptwgV|VN;1FIHbUkL)84aRV zs^ImKX!U{zOQwxdpI`KQBo_xmq~Oqqe^$OErN;aINm^mv*ntF!m1an)LI@OttVbm@ zZJ36-rIh?j0rUvhwoz{IM{kNF`0R;`<5^>_n9{EJ+&0w#`N2!7;>Div9-q3oM|kOC=-#a7^`|hC0}{p-#`z!tU23>zhl|s-kYW$b{^%T8^M# zKIb)FwUJj`OW2dlwCI(Cl?*qBixCn%ADty8S2wBM&My~DBy8i?q>h(*v2=f~A(Ga* z0E1gnI3a<$tHY5J2tpMkp&FGD=(()2#)N)imo{ld@AzJKb=cmuGV4h$l(d7+w?X| z4z4+I6cjya3SyrJ3+?l=olu3tOPD^m(ML-)V8wn{XHc9Uy!oILCB^M0xG+6^=w>r~ z=msuZ)oE&TCDl+|DrWl$T8cnWyifubxh>;;HPXogIk~m;w0=cJN2C;tje{+%33CWW z5!=RACQC=^(V(>p8J`5nF^F+1EkBw2EmswXzC7^(uT#TSd=!%DF@IPyxT5o72)!zb z*Th@25O$PRqcKrQ{$rt!b03Si{+V{|2 zGhr6#wVXk+E6D0tr}tvAbF3CjVrZ0M0t5ZamR6N zvL&e+d0I`5T5@4LD8iq-_ZDut>xR^BT?Ez%FOHN{^0V_^+Zx-5D9F#G5;>{hewg`& zo}9_(vLCM<8R{n=XDs8UF2*7HHg5I>ZQtY{$)n7|HM7->)~*q@7@q)MRP3fc(l@|n z!Xbsoi8ZvbT*4O9u^H5k4f^U9cB!F{iiubiq1AO_}aTQ>KBNTY7 zO%8*C)Z*-V+2<8=YDIQEhJzu!*GQFwFN0_X2Qm+N70XX~!fW$mK7yf*gb0E zYjVR1(vr`Bx!vTW4;cIFL5HU9D8Oeed7Fa416#AuyTXcGaovJ_cilBv8M_$T`67R&Y7%CM z_-s8_$DwYZB3yUPCiWpWrr9tfjxr?qBCStqTAg0=s#c9>Z)rK(;75v73XL*MNQ5Kb zpH$ywoBS&ibmIcTbzE=xiQwpq;Ii5J_4x=BtYAHa;z_I}T-h;=w=&mMlHDO^E(A@I z@gWGPyvZQCumWg2JVHzoCWS(84A@>uWWnnZHNNECCf$s5?AIBp`>}iB@SJXO^Nz_= zNFs6=$~W>-|HPEZF5><2nAQ-vyP*#*U8OR*NpeCwy*qG~i@-Aj{VD6`d8Z zTdu`dZ#6^idQVz0sOrr!vgW}?^7#lM4x%*A1*?esn<|;=wv5YdqT6tsJJ^>a_y(`@ zMkG`8m-9f%_8c94cVH3*ZHF@=A#z0(i}TY<~2~VIwJ&`LzkUU zaB`GfITkhTnzOv=ffW_B3&(Dy5~}dMi77EhT^95WI4n*w5gdeA=$_sa{Ug77YdX6Kp ztfMebx5fmPPrEJI#~PI_axYiC9W<>%^|d^n?G5U5@EB1;Jl?4CKP87lAg`|mKh=UQ z-h!%od%L^rKNLa+W2o0{A7I#Z2eG6j5lwl=zH{sQ`BUS>5C)1Xycx7im90p--H;>v z5XkS(2gbAW9X#L}bEll(=eKb;$VlPYJukpDLk1PK_1rU&pVtG6iFMpst0E ziMcI}-5)Hqm4#8TjFd1u4Cb$82zXHu0lByTwEzGJ0P1bk0?_%~w+{e-0c0c;1lhjO zv$6?rh^uo-f8~(T6=S90VPzB*6p;{9mXK3b5ffIIlF^VCQ&3b@P&3lhH8a+bHr7zk z(=xEo({wgbF*YzZF|#wbake)zwgK8Y+nU?j*xA^**}A&A+SxhVySq8tyScfVJNTQr zeY1D=w{!mXJ^_H7_zpW25kDP;GW#=gJ{4;Ltw1TeWTl{NwU|bOqHdF_X`8-X2hg?0 z-q|}C2 zqraD@W#^`57sur_@C= zlDz!toZ{--^5(*_>XNF4+S0H zt?SKYozsn-Lv_9LO(W}V6I;u^kxTyZD=|4giV}M(Qv2%jyIX6hVV*PPVk?^o5GHRhkURqxgH4R^v~d9kp$Kev0idUki%*L&1Evpe6vxj1<_)O|PCcR4kEGQNI3zjd%NcfCKq zxw)~ibFj0wyM1tcu(Nr%zjJhSw0nH9f4RSRaeuS1d%1CZb-ZzLeDrv{`EYn}b8vZm zwtaPe_;k7Xcy;)4e|&X$d3p12d3Skr|8#$Saew*v{BZg5^0I6X=?VbAp^6IdDmX11 zFTkoO*rNi@7Aze#tIb{E<^VgN!b)qW>);`sPXWX`6d97dQ%%O7A{7biMxy)3>kKkP z*L)X%AhQSq$^t~OO&Num6$A@E*a+r_8|HqPgKe;kI`z5UJe|S?1veWKchwtldApu2 zH9noZrn`Op0DguH%DK-#+aIW#_q@PL;i~kNDyLhI? zp-Am=>CgD$lNoyf(ldSo=21>&N@^Zh2pCBu9wSC%M(?=&*YwEJ@=I9oGO2ngLAe0+(Y}6_Zyg0j?g$2CE$Gv!NNVI zC}#LA$Ts4X=Yd-!lpf0ET&#FEEnnrK#Ze|yPI;fL7O6a=p}S@~}RIkGHg)#RAJ9*k8W>VdrtTG+G?(yFV)a?;l)LG9H8MRj~f zsuPk~QFiU{rc4Z}UEj+7wu#K7*#K636oulCi&yn|0wuL1#W2=roI(S{l6={TB_Zm8 z>&dqL&mrz8KR_h;N_k^~@O)AQW^Jq}y-g+gaKr`1`l!6I0j~4`rxSk2RD?$8D5B>2 z*SX(}Aa3~tlZWoyItR^C$|+D&l=U^6Sj)Tfi^CeoY6~Jq0*H&kb8rC%Hj2moalRAq z%=M$Q)4B~a5F~VFY*bP#`+==N>MNuyRMZvq8$ym0EQ@Z^z!v8yY7R*)UCdZgGfKQz zGOzW#nwylJnX<%`EFX3h>nd>P;8Q`778EQn;ZLZ0nyOg}L7OET$DdZh!_}*oVCZSX z&js!0*~|$lRF|JY54~lm)}-suR|(NRL9unY$pWutx67EQI+X-s8R=_7qxw*1AG@GH znnR`Vun%28!uD&wS_0GwrrJmA07rc(RbO*a-UFsO=Y3<#MI{dozt?FP&Vr)Ng|u~j zA1Mn}IwmPQ(%YUXmZRW!4%C~@m4|T7b;s*z1k`)IYXpVOr(;FBm24_-w(eKb zip`^bglTE2#P!bBy)xriEBAfR#4{>na+!bkjlxl_gW5~*>glWYL@nLH320Eo2EPtM z5PiXHBuIYCN$R#4MdCTIavXwDr}T`Gi4isb{ZiKfhvnKmQxy$KLz*E;g?5b*om~>d z-VYjE^)Z^))q{+&(4ivSiw&QS`wie_G{f;l>OwWWK&`jOa1w$OF)XM#K0+HNC{vk# z#uwKervUH04;9urkV*s;=rKR6Z1W8ITNdtCO~gjQx4>%*SJj%on1C?RCvV;N;+upI z?3=p`p(n-|!u4a!DA2JyqxN*lD0y7-?zhjrQYxY)l#wyzQp2^<*J5MiXF*nc@J0l@ zI4I##d1;Bvcx#{|21zv{AkJazKrt;xDH7MpIaU^e7l^ol%u#|ARd#O_V7<%F57R(R zrPeOi9_FL$459MIyJ0}c7~{HGLIaF3d2?m!GIHei7_OUt>Sh+7B+2X8hZXW^m38n( z^ywlF@R20)zq4Au0%d|%zEPIe-!&4Tr1D?t?CDAH(p;k-#y0{%ES*jmfW88}qC{C#L(4&}lSks0@rNuC z%g!w9-i1~LyjM+gnjE~K>wOyQ>?VXlsDJ>ceYSh<#e@FZ#H${_&Ge!IrOf-81-_n{ zPizLnQ%A+R!|hI4vf};p5orI-Ec4zo7L)2VP1ju3*wg&>rLR$_T{$BhQdMa)KUTaO z<_toaAn*q5uK0Y1R9m5akY)99ysAXfx4hzmj%J_^$`o2~$N?Wn(Itd+JDmh3vKL=R zhv5cHM}#!TjV`URwdLl;_E4JcH8}|j%-QmRay(iUhxbV6bLkTkZHpE-PPkPat@auA z^0QF!JtNMMBkwZ|&QyC5Sj>AkC6<0_T5PB6>G8E^W1aDiQw;-Zi4fc$YaW|{Gch=&_M+Olhu0S71^ZN{&LG7qEURE;;4BKDG`% zF1oYxd4%p^EFAP*6*4^G#@ zi67csK+E|P9EIiK#;hyN_iY)KvT>}$ilk+=uLGMPrr80wO5~@kaKb0N?(A+}q;;*) zG4#&UHKmj!7pc4NjiXSr)NtR;%}|(VIbK^+2B2eRFP<+JbcJ2xyNyl_qxr8op_%vU zWKb#+JaKVGZ`Pmnv8#@u*tg+LCEhv3moCyda?pwux}kqXW$s}JW3O{{C2!HpZY*f! zVCUr+**`)qYJgY7MjI$>elRK_Vti5^(8^xyKpYUVi$G?5bc)F6Pexh-8WN6exM20Q zzx}0QzJ}J=YJ8DNk=ZQ_yQRo|l697Vv1>u3lcL4ewN7OIq2=dNG>%<}_=+&*yW0ru z+PdqOHqBB+S$1xZK7QNu^$``dRhMwL$C9NBwMYfrb82};>V6`|tzpjnIU7|khwggE zbaregzws!w&%@!eQ4wli*sLq%IutU!&7OSdFQKc zKURdVH*o-c#8PZ1BR%QwDx=}A30sJr-lwcKpO^~QAj2%1(J?exvcQG0N~dsj3*#tB z=Ly*!1k)|WQs1TSW)TZtRNAT(F7-(Qf>;-5q#y;hV&9D@c8|3)bnuqgPr%MDX#4+FgA24S}P`DR!54 z5yI68DKF;+yQ0|Rkt0f;lQgDx`q(7f;iB3%?;S0ltpqNf?uLX;zC?HywHppEDN0MF zy}XQvZW{XBM&*&)Oq>tlRvIkDmc7W`vf^Jil#fZ>u&&eUtrgzhq+6oXno0zo!gJV5 zoU(DtOc}W@oPgx^hHPRxA=1w~%JnW zpdcW?!69JYy@P~;gMovCg@J{IfBz8y{ypM*SXcy91Vkic6ciM=4`}G9$mk!DQILOu zfPlTpfI~n-KtLnI!@?u~AJgj(03sCN3~&Ypf(QUb1OYa2?*tM zZG50n&=|gWM@3gw2`cE>e(%Vt9)APDenbCN#9zvQeD?+d0t)r^EjuE}zcAlHfq;U) z`&TXgRRkjb8#<(pwfFaq@sCKzpXsA2*Ut##P*558-b%{qoH&170KkF0K_h}80=NOS zZ-EW}pAxQ8*0d9lK!;*!MQIGt1bMO4RqIRkBQ_IBBkD|Sz4|&OkVx}ScA%$tWs-$Hpu|0O9 zmF$|n`!~N;|7q9jGjRHtH`5_%x^BQgiJ#`foe~}S^yE#3)-m^{*_qJ?oYb{5ww}S- z;#gqDtgCnA7_F*D%~H8qqtN{6I?l1+Qu>O^s^Va2U2=W>gwn@t@^2d($K@qk&6O|c zm)D=1Mq_o$&7^QSgC56LugWW1$$NDf^FJV3a9!Rm4_V@B1*}^zx(?@k zW;mxT&_5U~x%eoZv%LbmBfFuK+9=edR?{;Z}YO zV2^mwE5MI+uiaEq_`s%eJ{^lDUws<5j@agzX2GSCyl%5#->$TRh_%~1>hML{soBsB zI2`ASt*L6l0e2rcUt-E#qZs)twv4Ae9cb@*q2wGWQmN8vTzet^7Bb+DVxc#|p~bF7 zHP)ORYneWOH4eD1ds=jBFa8|Ez&6| zkF1kSh)&!uT1%9MZzd>2PelD3QNjc(gMT*jf7%3W+tyxaaHX`TM@8nl7_~jQLUfo8 zawD*Qoan?^QC31?_{eQqK^{p_K8Ih@@v&Z$T~uSOZbhb{q|slKR}SaE+N0gbcc1yu z=FUB~W$+cixc^1Av;=kj0rQ%5~gZ{LrX>ZYie2%oIMRUBN_17(RG|Y>4JZ@qCDSNjd?+FTGK) z(a9!RiFt_=__tgczv)Y0c(ZD!@9G{b4%rJV_czaH4oh}tfTpt}N|d zMo2N5U)je=QCkoU8`v|>{>Xa;EEC1ek!S1rBypUJXOTeUlmoZBPu zg{vG7*`rPlCDn*QRX;YVtmh6*l^mFAHig1l(F;0LKduWBZADp)3o;dkkcNLolWewA z%c5Eg|IEza;9m7VI}!kn!okrJq&iuy&X&`9IZ{P^VRC(@sko0kjq%`lHCj`3tWU3G z`^NBi|0sfikBebY~L96F!Ov{;^gnMP>z0^RGL9^kqLw3^e%WWJ_KVOnTYQy@PWeeu9 zSo^Vn>-7Cr*6;}y3T!6a(YOGn!RTzhm5>eEkXL~6!Q#Op@Y-O^bQ+a3)L~P^f7NiL zg|i8$7ETr(^+9BC_%}wO9hqA;#~m|%OntGbR#-oismq%UwN)$+NJT65co8LD6|g;@ zTU3E%a5;L}jb&|ka!+mQUTx)!>NScbKXkaY)5vo0d`TfaRm`0Lj|#1o z@uOm${Y$CcUaH*{^bTHvt1IKS>lrA52I7`gbmLsOI$~zIAgm zPMd!Cyt&^P%8 zYEc^7ySI(oGG%Czv^+vHvT!4hl^yFch9OLX(FYh{I<>sS$|x@Rassp3*Vde{lE8(7 zPSYx9IYFnXeM>Z*?fA~GF``v6MYhh3ZM)I0Q?pO&O{W>7t}>VJrtIuBPMV41@pIT5 z>=)Xp%`K^Nh4!lDAvx{iGy>7oPmqu^+J=-15%aqovt^nI^F!u>DVmqPMj4m3tZdYo zf3XMO^LOTI#iQ-O#dEcfd70fpFp5=0m(5c1*V_vD=#9DC=XR0WUxY6u`VhVH7we4{ z^3$7}^*mjeQ+AwE>oP2+BQ4Sy%KDFk!d-6}OFc>RD$ZG5l;5pCI!)id0&qRA_+J4d z_1CWeDGqm;_R9bY?W@AaA(9`Sq+8h8a4SKn1Y>Q3xISo=#26v4iy6uL}iq(CNAx8OfDBm$U#jK)Fg=F3GTE{;H&zciP(Xs0cU+)0` z*c3|stz^^1ER*@*+hikWSqUkYo-wl${yndvqxVOyCriN@HCp z28**BqHiu`qz7!CwkI;is-62ZZN?C!?61t~Iik&?7-9r7O(P7c#OGCWA3g0nJl zO0?)tnWJm_=F}Bh+M0?nzfkSSHCU8u9Q)YV-C0NWfjK>A`4Zse`cF; zpHHa1&g(P2zDb;Dn8twWb;fGAmc3T^`U;Tae#)Skrzj>F+#fRE=hisQpzhb4Czh2` z*4tUMDKB!%F5)mUHDura^p~~?T(|p#lZ11nXIO}Bvh7VNmJxtp zIZeI-%Jb)Q>`kP?MJEfPAwP41KVQ?EoRGYXHp*eB8!Y1X-2J8-MVF!YA(`N%XV$wPMyuW z)r!?T>l__zPE?foZUQHt_$00 z3dBo{N%qOgs-Dx)hIyLAlOL10-VZ-bUfT;(zXJM9oA;%FC;NA>40Rr51>38A#kE-m zTE$)f%XVFhQSIovLAF&;rehbf|oGl5p2Jdh__D z)JehaB6q}e@px*=l05q1`K9zEc6NsOov;~jVDQIZ+wF~?Zkxf0**n20ohhAAs>m)X z6mGXQk|gaTLT>L+6d!n6kuHblyBv;4)t4gpfTnxSu?%HO4!8MNYys` z<|tc!m(5#;#R9D2%vH^oHVv3kWDHAFugY+XZ2V@soUIzH zbFaf(#I(EclqA?VW1oPlo(^1MZbk`3+T%;vo2RaamCtM(MhW_s8CCrgvZB<%s0TY9 zUjNpBka0K5N9!1qO-t^~rm>snhR0;Wr!@D8xd&~Y_T8;SBhC{N*$ctFbK&hLk=)wt zjAj#VDIVM(@Gga4wAwv@L==j~AhE~s8wL)=YMb`dxSlx+(wkYU2n{(e&t&d|?Hy-N zdGdHI`^iy@Apc$59&zwrNP$cEvzAa;+R^q44u~dU& z(l*`MCWGU^b|La<=F-u@@18o=Oy>?J?WP`T*@_Mh{VR@5>Phk#k>wjEN8tHpmP?-S zrcdW;<+DX6B*(ZOx_SGkdgqB5_5x3_=Ev#u`47TLZXVRYac&|kf-kXutKx-Jv62MZ?~?Xus_vf~J(WAJnJZYt6A!QE>f7Ad78@vuhV37$ zZtBPUtkbuvj$izHfOX$`O@n5V?2&pts2an#J|kfVhYd< z6Qi!c^e|Bg4SFWu41H!w`S*qZxSf#7H+`22H?=Aj%h4c_%2CKBt?iQJ{xZJREnz}K z{JCJlk^HijsaM*78PcjMmu4U}-(?d^wk-DH$qwt**DTVh;aNXHVXA;&s1N11LaCjly{g%JdqapYT&@E-kR@3$k z>~76#D~U0EXaeB6hV=NPPD)a+aQ(FKc z{DyRHb#Is7Lw+0hm>kF{#fWU!5La8C#^xzZ9h0z$auNRu5Vl$M?Rq?nx}06dCKz>1 z_#kA{?%VYwICp<|mhb0yntmb~Lo|^2Z~gP_RG9s-#-Vwv?j9Rn5XKp*yhiUtzrC7l;+A{{qos_-d-YZvibV`b}i zcJ(3Cs%$NP!S2FQszSkMc}Zl&LGoISaSapN?`B!W_L3;QF<2zEvs`$e007D5=sLXz z&7}jRw(lDzOUw=zuYi^|%<_y8SboNaMGd#u;|$W%C)4OH49mL+ zYR-mx(nT)W)jR);oUCZgkJR>S&;L_10N`iD$|LhrMy4k2&lA=)4R$sNh6iej?X}eW@w0{2Jh3K*#rqU{ciXzM`Ss;SinMyS-)Ye6NWiCe z%fP?$Wk%8?^jqe>EW~zHOrF*2PyK|6z&pASAff{Ck+rl|AExZ_e zOU|sg9~KZg)yRRWl;9iHry;e_;?6$qVYW95&utijg{0fUxKOebUdhJItzD^wd8}k1 zJR-4ALpbvlFwfPE#eDDH5Gk)u>UfsyAX|*WcsA4QZqj$Sq>c4(%Q=uCRCOBM?Ivh_ zF@C>(vw9=ZM3>;EMX^B<8NC{=!L|4@ghiFx`#X&QAh|g0H@1)OH#Uu(9Vu-FVJ8Q} zja~sWt1}Re6RGVgZoQ$l;QFNA0n9qeUFV%`jo4kD}=Zx&qE#0<{ zYA?rfb)mb@C+Xasr(t^jXg;Pp;uIRYP>(r$WKeC|d`nhq!B|u{fjU;5qN7uT_GO zbKmQ?dN{jeOKx5Vr1h`U*Px8Dz4#p;PIy#Aub&@1%U_wGTdHL}H*@)?-WYc; z6pi%HBxj2%#2)}h`AzLl)Ft%g)7078lgs;HTG3|G1RuvY&rHhsg8SAEe{7IfYQryE zcPc=#ztaLk)WY4lJC8tGSy=heOhGQ&xMW`199oE>rn4#Q)~0Ay-WRdqG3c?in{{tl zjTGL9btAvarEPbaXzNwZD`0*C`ZlK&u};joE!$q%lhm8f#uy<# z6=mJ9sKLdx(A&F`R+?7JGM>#BtDqx=s3Al~Lm16gBP-dY`!*#Ms2f67FCi;ng4l!; zbNu^B!rz+w|M7=luk-)BGau!xa*~1yyi}r6LNx!K;ovPqyEInQ(uWo~;Pe?KryjO6 zIZ2ctd2i>Mdg;U78nT(#;#JpPyEc9nWoRg&4Zrw@)61)~N|iLzBB2XFSeS9x#un5n&w#z25A&LtOrC5qo;3FPq#1PgebADYwa zat=#KXay6D3)iQ%7eX9(HQp)G;}z3>>o&H?uZB$&cfN#~rx4*lVJB<@mpbC+riYIW7?Ee83J1Ok3@5be&pXu=E8c44_X1GY{ z+V}3Vy|aVou;P=a2lEi#uW-hTp2;|+w>kX6e{Bu}0YwJ(8z|23X{f+&1q(4)d{4>hm^}7BI z%3o8Q{4>tqYeV(c4*y=8KU1FkGs>@|Cx4ROZ&3b63Y32a`@5+#e*^aSlqmm<^mpYJ zeuMO9ij;pw`MaqRK!0zdKT@Xr7tSA9KmM8z1ny4@y>TLMIRAZvc}t=4M<@P6@~<0f ze+BCPNr7+cWq)Q5`FFX$?zQ~2?eYsmY!u@xtziwOkdp*z{j^&|J!}7>0fKa{|obqjdcJ3 literal 0 HcmV?d00001 diff --git a/logo.pdf b/logo.pdf new file mode 100644 index 0000000000000000000000000000000000000000..709ed07dfe02ae0c7703ccd4f3cd558807f158e7 GIT binary patch literal 22485 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 literal 0 HcmV?d00001 diff --git a/man/man1/cgreen-debug.1 b/man/man1/cgreen-debug.1 new file mode 100644 index 00000000..24f3ad2d --- /dev/null +++ b/man/man1/cgreen-debug.1 @@ -0,0 +1,44 @@ +.TH CGREEN-DEBUG 1 + +.SH NAME +cgreen-debug \- start cgreen-runner under a debugger and break at a specific test + +.SH SYNOPSIS +.B cgreen\-debug +[\fI\,OPTION\/\fR...] +\fILIBRARY\fR \fITEST\fR + + +.SH DESCRIPTION +.B cgreen-debug +is a script to start cgreen-runner under a debugger (primarily gdb), load a \fILIBRARY\fR +and break on a named \fITEST\fR. Where \fILIBRARY\fR is a filename of the shared library of +Cgreen tests, usually .so, .dll or .dylib, depending on your platform. \fITEST\fR is the +name of a test in that library in the format :. + +.SS OPTIONS +.TP +.B "\-h, \-\-help" +Print some usage information and exit. + +.TP +.B "\-d, \-\-debugger" debugger +Instead of default (gdb) use +.I debugger +as the debugger. Allowed values are "gdb", "cgdb", "lldb". + +.SH "SEE ALSO" +cgreen(5) +cgreen-runner(1) + +.PP +The full documentation for +.B cgreen-debug +and +.B the Cgreen framework +is in the +.B Cgreen +manual available at +.UR https://cgreen-devs.github.io/ +GitHub +.UE . diff --git a/man/man1/cgreen-runner.1 b/man/man1/cgreen-runner.1 new file mode 100644 index 00000000..8515b9ae --- /dev/null +++ b/man/man1/cgreen-runner.1 @@ -0,0 +1,89 @@ +.TH CGREEN-RUNNER 1 + + +.SH NAME +cgreen-runner \- an auto-discovering runner for the Cgreen unittest and mocking framework + + +.SH SYNOPSIS +.B cgreen\-runner +[\fB\-\-colour\fR] +[\fB\-\-xml\fR \fIprefix\fR] +[\fB\-\-suite\fR \fIname\fR] +[\fB\-\-verbose\fR] +[\fB\-\-no\-run\fR] +[\fB\-\-help\fR] +( \fILIBRARY\fR [\fItest\fR] )+ + + +.SH DESCRIPTION +.B Cgreen +is a framework for creating and running compact and easy-to-read +unittests for C and/or C++. After compiling your tests and linking +them together with the code/class/subject under test (CUT/SUT) into a +shared dynamically loadable \fILIBRARY\fR you can auto-discover and +run all tests using the +.B cgreen-runner +without having to manually add each and every test that you write to a +suite programmatically. +.PP +This makes the TDD cycle even faster and less error-prone, one less thing to remember. + + +.SS "Usage:" +Discover and run all, or a single named, cgreen test(s) from one or multiple +dynamically loadable library. +.PP +A single test can be run using the form [:] where can +be omitted if there is no context. + + +.SH OPTIONS + +.TP +.BR \-c ", " \-\-colours/colors +Use colours to emphasis result (requires ANSI\-capable terminal) + +.TP +.BR \-x ", " \-\-xml " " \fIprefix\fR +Instead of messages on stdout, write results into one XML\-file per +suite, compatible with Hudson/Jenkins CI. The filename(s) will +be '\fIprefix\fR\-.xml' + +.TP +.BI "\-s, \-\-suite " name +Give the top level suite +.I name +instead of the name in the \fILIBRARY\fR. + +.TP +.B "\-n, \-\-no\-run" +Don't run the tests. + +.TP +.B "\-v, \-\-verbose" +Show progress information during run. + +.TP +.B "\-V, \-\-version" +Show version information and exit. + +.TP +.B "\-h, \-\-help" +Print some usage information and exit. + +.SH "SEE ALSO" +cgreen(5) + +.PP +The full documentation for +.B cgreen-runner +and +.B the Cgreen framework +is in the +.B Cgreen +manual available at +.UR https://github.com/cgreen-devs/cgreen +GitHub +.UE . + diff --git a/man/man5/cgreen.5 b/man/man5/cgreen.5 new file mode 100644 index 00000000..85fdcaaf --- /dev/null +++ b/man/man5/cgreen.5 @@ -0,0 +1,44 @@ +.mso www.tmac +.TH CGREEN 5 +.SH NAME +cgreen \- a framework for C and C++ unit tests and mocking +.SH DESCRIPTION +.B Cgreen +is a framework for creating and running compact and easy-to-read +unittests for C and/or C++. + +Here are some of its features: + +.IP \[bu] 2 +Fluent API resulting in very readable tests +.IP \[bu] +Expressive and clear output using the default reporter +.IP \[bu] +Fully functional mocks, both strict and loose +.IP \[bu] +Each test runs in its own process for test suite robustness +.IP \[bu] +Automatic discovery and running of tests using dynamic library inspection +.IP \[bu] +Extensive and expressive constraints for many datatypes +.IP \[bu] +BDD-flavoured test declarations with Before and After declarations +.IP \[bu] +Extensible reporting mechanism +.IP \[bu] +Fully composable test suites +.IP \[bu] +An isolated test can be run in a single process for debugging + + +.SH "SEE ALSO" +cgreen-runner(1) +.PP +The full documentation for +.B cgreen-runner +and +.B the Cgreen framework +is in the +.B Cgreen +manual available at +.URL https://github.com/cgreen-devs/cgreen GitHub . diff --git a/tutorial_src/.gitignore b/tutorial_src/.gitignore new file mode 100644 index 00000000..7ff6641a --- /dev/null +++ b/tutorial_src/.gitignore @@ -0,0 +1,48 @@ +*.o +*.so +*.exe +*.stackdump +*.out +constraints +constraints1 +constraints2 +crash1 +crash2 +crash3 +double1 +first0 +first1 +schema1 +schema2 +strlen1 +strlen2 +strlen3 +strlen4 +strlen5 +strlen6 +strlen7 +side_effect +suite1 +test_as_xml0 +test_as_xml1 +test_as_xml2 +test_as_xml3 +test_as_xml4 +words0 +words1 +words2 +words3 +words4 +words5 +words6 +words7 +words8 +words9 +formatter3 +formatter4 +formatter5 +stream_test4 +formatter6 +custom_constraint1 +custom_constraint2 +custom_constraint3 diff --git a/tutorial_src/Makefile b/tutorial_src/Makefile new file mode 100644 index 00000000..95fe2f17 --- /dev/null +++ b/tutorial_src/Makefile @@ -0,0 +1,311 @@ +# This Makefile generates source and output that is part of Cgreen +# documentation. Files here are included in the asciidoc source so be +# careful with what you commit, it might thrash the documentation. In +# particular, be aware that non-UTF-8 characters in them (usually) +# makes generation of PDF impossible. 'stream2' and 'stream5' might +# generate random data as 'actual' values in the output file. That is +# the way it is supposed to be to work as documentation examples. +# Take care data for those examples is printable but looks random. + +CFLAGS = -Wall -g + +.IGNORE : +.SILENT : + +CGREEN_ROOT=../.. + +# Point to MySQL includes and linking for the MySQL examples +ifeq ($(shell uname),Darwin) +MYSQL_INCLUDE = /opt/local/include/mysql56/mysql +MYSQL_LIB = /opt/local/lib/mysql56/mysql +else +ifeq ($(shell uname -o),Cygwin) +MYSQL_INCLUDE = /usr/include/mysql +MYSQL_LIB = /usr/lib +else +MYSQLROOT = /usr +MYSQL_INCLUDE = $(MYSQLROOT)/include/mysql +MYSQL_LIB = $(MYSQLROOT)/lib +endif +endif + +CGREEN_BIN=$(CGREEN_ROOT)/build +CC = gcc -c -g -Wall -I$(CGREEN_ROOT)/include -I$(CGREEN_ROOT)/src -I$(MYSQL_INCLUDE) $(FPIC) +LINK = gcc -g +LIBDIRS = -L$(CGREEN_BIN)/src -L$(MYSQL_LIB) + +# Enviroment dependent fixup of the various paths to use bins & libs in this development tree +FPIC = -fPIC # required on Linux, Darwin, ... +ifeq ($(shell uname),Darwin) +LDPATH = DYLD_LIBRARY_PATH="$(CGREEN_BIN)/src:$(MYSQL_LIB)" +else +ifeq ($(shell uname -o),Cygwin) +FPIC= +LDPATH = PATH="$(CGREEN_BIN)/src:$(MYSQL_LIB)" +else +LDPATH = LD_LIBRARY_PATH="$(CGREEN_BIN)/src:$(MYSQL_LIB)" +endif +endif + +RUNNER = $(LDPATH) $(CGREEN_BIN)/tools/cgreen-runner + + +all: with_main with_runner special + echo + echo "NOTE: 'stream2.out' and 'stream5.out' should contain a simulation" + echo "NOTE: of random memory as the actual value. Don't commit changes that do" + echo "NOTE: not. But also note that PDF generation of the documentation" + echo "NOTE: will fail if there are non-UTF-8 characters in them." + echo "NOTE: Edit by hand to reasonable values if needed." + +# Since we have multiple files for the same tutorial example, distinguished by a number, +# we need to remove that number from all output +REMOVE_NUMBERS=-e 's/\([a-z]\)[0-9]\+\"/\1\"/' -e 's/\([a-z]\)[0-9]\+\.c/\1\.c/' + +# To minimize the diffs we also need to normalize execution times +NORMALIZE_DURATION=-e 's/\ in\ [0-9]\+ms/\ in\ 42ms/g' + +# And batch that together in one symbol +SED=sed $(REMOVE_NUMBERS) $(NORMALIZE_DURATION) + +with_main: first0 first1 \ + words0 words1 words2 words3 words4 words5 words6 words7 words8 words9 \ + strlen1 strlen2 strlen3 strlen4 strlen5 strlen6 strlen7 \ + double1 \ + schema1 schema2 \ + crash1 crash2 crash3 \ + suite1 \ + test_as_xml0 \ + test_as_xml1 \ + test_as_xml2 \ + test_as_xml3 \ + test_as_xml4 \ + custom_constraint1 \ + custom_constraint2 \ + custom_constraint3 + for f in $^ ; do echo $$f; $(LDPATH) ./$$f | $(SED) > $$f.out; done + +# Most tests built to run with the runner we can run here, but some need special care +# They have their output produced directly in their build rules below and are run using +# the 'special' rule +with_runner: set_content side_effect stream0 stream1 stream2 stream3 stream4 stream6 \ + multiple_streams1 multiple_streams2 \ + formatter0 formatter1 formatter2 formatter3 formatter4 formatter5 formatter6 \ + struct_parameters + for f in $^ ; do echo $$f; $(RUNNER) $$f.so | $(SED) > $$f.out; done + +special: runner1 runner2 runner3 stream5 learning_mocks + +clean: + rm -f *.exe *.o *.out *.so *.stackdump *~ + +first_tests0.o: first_tests0.c + $(CC) -o $@ -c $^ $(CFLAGS) -Wno-unused-variable + +first0: first_tests0.o + $(LINK) -o $@ $^ $(LIBDIRS) -lcgreen + +first1: first_tests1.o + $(LINK) -o $@ $^ $(LIBDIRS) -lcgreen + +words_tests0.o: words_tests0.c + $(CC) -o $@ -c $^ $(CFLAGS) -Wno-unused-variable + +words0: all_tests.o words_tests0.o + $(LINK) -o $@ $^ $(LIBDIRS) -lcgreen + +words1: all_tests.o words_tests1.o words1.o + $(LINK) -o $@ $^ $(LIBDIRS) -lcgreen + +words2: all_tests.o words_tests1.o words2.o + $(LINK) -o $@ $^ $(LIBDIRS) -lcgreen + +words3: all_tests.o words_tests2.o words2.o + $(LINK) -o $@ $^ $(LIBDIRS) -lcgreen + +words4: all_tests.o words_tests2.o words3.o + $(LINK) -o $@ $^ $(LIBDIRS) -lcgreen + +words5: all_tests.o words_tests2.o words4.o + $(LINK) -o $@ $^ $(LIBDIRS) -lcgreen + +words6: all_tests.o words_tests3.o words5.o + $(LINK) -o $@ $^ $(LIBDIRS) -lcgreen + +words7: all_tests.o words_tests3.o words6.o + $(LINK) -o $@ $^ $(LIBDIRS) -lcgreen + +words8: all_tests.o words_tests4.o words6.o + $(LINK) -o $@ $^ $(LIBDIRS) -lcgreen + +words9: all_tests.o words_tests4.o words7.o + $(LINK) -o $@ $^ $(LIBDIRS) -lcgreen + +strlen1: strlen_tests1.o + $(LINK) -o $@ $^ $(LIBDIRS) -lcgreen + +strlen2: strlen_tests2.o + $(LINK) -o $@ $^ $(LIBDIRS) -lcgreen + +strlen3: strlen_tests3.o + $(LINK) -o $@ $^ $(LIBDIRS) -lcgreen + +strlen4: strlen_tests4.o + $(LINK) -o $@ $^ $(LIBDIRS) -lcgreen + +strlen5: strlen_tests5.o + $(LINK) -o $@ $^ $(LIBDIRS) -lcgreen + +strlen6: strlen_tests6.o + $(LINK) -o $@ $^ $(LIBDIRS) -lcgreen + +strlen7: strlen_tests7.o + $(LINK) -o $@ $^ $(LIBDIRS) -lcgreen + +double1: double_tests1.o + $(LINK) -o $@ $^ $(LIBDIRS) -lcgreen + +schema1: schema_tests1.o person.h + $(LINK) -o $@ $^ $(LIBDIRS) -lcgreen -lmysqlclient + +schema2: schema_tests2.o person.h + $(LINK) -o $@ $^ $(LIBDIRS) -lcgreen -lmysqlclient + +crash1: crash_tests1.o + $(LINK) -o $@ $^ $(LIBDIRS) -lcgreen + +crash2: crash_tests2.o + $(LINK) -o $@ $^ $(LIBDIRS) -lcgreen + +crash3: crash_tests3.o + $(LINK) -o $@ $^ $(LIBDIRS) -lcgreen + +suite1: suite1.o suite_person_tests.o suite_strlen_tests.o + $(LINK) -o $@ $^ $(LIBDIRS) -lcgreen -lmysqlclient + +test_as_xml0: test_as_xml0.o + $(LINK) -o $@ $^ $(LIBDIRS) -lcgreen + +test_as_xml1: test_as_xml1.o xml_reporter1.o + $(LINK) -o $@ $^ $(LIBDIRS) -lcgreen + +test_as_xml2: test_as_xml1.o xml_reporter2.o + $(LINK) -o $@ $^ $(LIBDIRS) -lcgreen + +test_as_xml3: test_as_xml1.o xml_reporter3.o + $(LINK) -o $@ $^ $(LIBDIRS) -lcgreen + +test_as_xml4: test_as_xml1.o xml_reporter4.o + $(LINK) -o $@ $^ $(LIBDIRS) -lcgreen + +custom_constraint1: custom_constraint1.o + $(LINK) -o $@ $^ $(LIBDIRS) -lcgreen + +custom_constraint2: custom_constraint2.o + $(LINK) -o $@ $^ $(LIBDIRS) -lcgreen + +custom_constraint3: custom_constraint3.o + $(LINK) -o $@ $^ $(LIBDIRS) -lcgreen + + + +########################################################### +# With runner +# +# All these examples run using the runner which automatically outputs the file name +# Since that includes the example #, which we don't want in the tutorial, we use +# sed to remove that number from the file name in the output +.PHONY: set_content +set_content: stream_tests0.o read_paragraph1.o + $(LINK) -shared -o $@.so $^ $(LIBDIRS) -lcgreen + +.PHONY: side_effect +side_effect: stream_tests0.o read_paragraph1.o + $(LINK) -shared -o $@.so $^ $(LIBDIRS) -lcgreen + +.PHONY: stream0 +stream0: stream_tests0.o read_paragraph1.o + $(LINK) -shared -o $@.so $^ $(LIBDIRS) -lcgreen + +.PHONY: stream1 +stream1: stream_tests1.o read_paragraph1.o + $(LINK) -shared -o $@.so $^ $(LIBDIRS) -lcgreen + +.PHONY: stream2 +stream2: stream_tests2.o read_paragraph1.o + $(LINK) -shared -o $@.so $^ $(LIBDIRS) -lcgreen + +.PHONY: stream3 +stream3: stream_tests2.o read_paragraph2.o + $(LINK) -shared -o $@.so $^ $(LIBDIRS) -lcgreen + +.PHONY: stream4 +stream4: stream_tests3.o read_paragraph2.o + $(LINK) -shared -o $@.so $^ $(LIBDIRS) -lcgreen + +.PHONY: stream5 +# This test generates random characters read from memory so we need to tidy that up +stream5: stream_tests4.o read_paragraph2.o + $(LINK) -shared -o $@.so $^ $(LIBDIRS) -lcgreen + $(RUNNER) $@.so | $(SED) > $@.out + echo "Trying to remove non-characters from stream5.out" + echo "Check manually if the following sed fails:" + cp $@.out $@.bak && LANG='' sed -e 's/^[^[[:space:]].*"]/"]/g' $@.bak > $@.bak2 && mv $@.bak2 $@.out + rm $@.bak + +.PHONY: stream6 +stream6: stream_tests4.o read_paragraph3.o + $(LINK) -shared -o $@.so $^ $(LIBDIRS) -lcgreen + +.PHONY: multiple_streams1 +multiple_streams1: multiple_streams1.o + $(LINK) -shared -o $@.so $^ $(LIBDIRS) -lcgreen + +.PHONY: multiple_streams2 +multiple_streams2: multiple_streams2.o + $(LINK) -shared -o $@.so $^ $(LIBDIRS) -lcgreen + +.PHONY: runner1 +runner1: first_tests2.o + $(LINK) -shared -o first_tests.so $^ $(LIBDIRS) -lcgreen + $(RUNNER) first_tests.so | $(SED) > $@.out + +.PHONY: runner2 +runner2: first_tests2.o + $(LINK) -shared -o first_tests.so $^ $(LIBDIRS) -lcgreen + $(RUNNER) first_tests.so Cgreen:fails_this_test | $(SED) > $@.out + +.PHONY: runner3 +runner3: first_tests2.o + $(LINK) -shared -o first_tests.so $^ $(LIBDIRS) -lcgreen + $(RUNNER) -v first_tests.so Cgreen:fails_this_test | $(SED) > $@.out + +.PHONY: formatter0 +formatter0: stream1.o formatter_tests0.o + $(LINK) -shared -o formatter_tests.so $^ $(LIBDIRS) -lcgreen + +.PHONY: formatter1 +formatter1: stream1.o formatter_tests1.o + $(LINK) -shared -o $@.so $^ $(LIBDIRS) -lcgreen + +formatter2: stream1.o formatter_tests2.o + $(LINK) -shared -o $@.so $^ $(LIBDIRS) -lcgreen + +formatter3: stream1.o formatter_tests3.o + $(LINK) -shared -o $@.so $^ $(LIBDIRS) -lcgreen + +formatter4: stream1.o formatter_tests4.o + $(LINK) -shared -o $@.so $^ $(LIBDIRS) -lcgreen + +formatter5: stream1.o formatter_tests5.o + $(LINK) -shared -o $@.so $^ $(LIBDIRS) -lcgreen + +formatter6: stream2.o formatter_tests5.o + $(LINK) -shared -o $@.so $^ $(LIBDIRS) -lcgreen + +struct_parameters: struct_parameters.o + $(LINK) -shared -o $@.so $^ $(LIBDIRS) -lcgreen + +learning_mocks: learning_mocks.o + $(LINK) -shared -o $@.so $^ $(LIBDIRS) -lcgreen + $(RUNNER) $@.so diff --git a/tutorial_src/README b/tutorial_src/README new file mode 100644 index 00000000..fbbf5184 --- /dev/null +++ b/tutorial_src/README @@ -0,0 +1,12 @@ +This directory contains source files and output for the examples in +the tutorial documentataion. You should run these now and then and +make sure that they compile and work. + +Note however that the guide includes specific lines from certain of +these files, so if you change anything you need to be certain that the +guide is still correct. + +On the other hand we will always know that the code in the guide +actually works and is correct. + +You need mysql client header files to make some sources to compile. diff --git a/tutorial_src/all_tests.c b/tutorial_src/all_tests.c new file mode 100644 index 00000000..b15722ef --- /dev/null +++ b/tutorial_src/all_tests.c @@ -0,0 +1,12 @@ +#include + +TestSuite *words_tests(); + +int main(int argc, char **argv) { + TestSuite *suite = create_test_suite(); + add_suite(suite, words_tests()); + if (argc > 1) { + return run_single_test(suite, argv[1], create_text_reporter()); + } + return run_test_suite(suite, create_text_reporter()); +} diff --git a/tutorial_src/crash_tests.c b/tutorial_src/crash_tests.c new file mode 100644 index 00000000..52495bff --- /dev/null +++ b/tutorial_src/crash_tests.c @@ -0,0 +1,19 @@ +#include +#include + +Ensure(will_seg_fault) { + int *p = NULL; + (*p)++; +} + +Ensure(will_stall) { + die_in(1); + while(1) { } +} + +int main(int argc, char **argv) { + TestSuite *suite = create_test_suite(); + add_test(suite, will_seg_fault); + add_test(suite, will_stall); + run_test_suite(suite, create_text_reporter()); +} diff --git a/tutorial_src/crash_tests1.c b/tutorial_src/crash_tests1.c new file mode 100644 index 00000000..c4f9e6d9 --- /dev/null +++ b/tutorial_src/crash_tests1.c @@ -0,0 +1,17 @@ +#include +#include + +Describe(CrashExample); +BeforeEach(CrashExample) {} +AfterEach(CrashExample) {} + +Ensure(CrashExample, seg_faults_for_null_dereference) { + int *p = NULL; + (*p)++; +} + +int main(int argc, char **argv) { + TestSuite *suite = create_test_suite(); + add_test_with_context(suite, CrashExample, seg_faults_for_null_dereference); + return run_test_suite(suite, create_text_reporter()); +} diff --git a/tutorial_src/crash_tests2.c b/tutorial_src/crash_tests2.c new file mode 100644 index 00000000..62ce2539 --- /dev/null +++ b/tutorial_src/crash_tests2.c @@ -0,0 +1,17 @@ +#include +#include + +Describe(CrashExample); +BeforeEach(CrashExample) {} +AfterEach(CrashExample) {} + +Ensure(CrashExample, seg_faults_for_null_dereference) { + int *p = NULL; + (*p)++; +} + +int main(int argc, char **argv) { + TestSuite *suite = create_test_suite(); + add_test_with_context(suite, CrashExample, seg_faults_for_null_dereference); + return run_single_test(suite, "seg_faults_for_null_dereference", create_text_reporter()); +} diff --git a/tutorial_src/crash_tests3.c b/tutorial_src/crash_tests3.c new file mode 100644 index 00000000..9b85a1f4 --- /dev/null +++ b/tutorial_src/crash_tests3.c @@ -0,0 +1,23 @@ +#include +#include + +Describe(CrashExample); +BeforeEach(CrashExample) {} +AfterEach(CrashExample) {} + +Ensure(CrashExample, seg_faults_for_null_dereference) { + int *p = NULL; + (*p)++; +} + +Ensure(CrashExample, will_loop_forever) { + die_in(1); + while(0 == 0) { } +} + +int main(int argc, char **argv) { + TestSuite *suite = create_test_suite(); + add_test_with_context(suite, CrashExample, seg_faults_for_null_dereference); + add_test_with_context(suite, CrashExample, will_loop_forever); + return run_test_suite(suite, create_text_reporter()); +} diff --git a/tutorial_src/custom_constraint1.c b/tutorial_src/custom_constraint1.c new file mode 100644 index 00000000..fe3ee295 --- /dev/null +++ b/tutorial_src/custom_constraint1.c @@ -0,0 +1,37 @@ +#include +#include +#include "constraint_internal.h" + +Describe(TestConstraint); +BeforeEach(TestConstraint) {} +AfterEach(TestConstraint) {} + +bool compare_want_greater_than_5(Constraint *constraint, CgreenValue actual) { + return actual.value.integer_value > 5; +} + +Constraint static_is_bigger_than_5 = { + /* .type */ CGREEN_VALUE_COMPARER_CONSTRAINT, + /* .name */ "be bigger than 5", + /* .destroy */ destroy_static_constraint, + /* .compare */ compare_want_greater_than_5, + /* .test */ test_want, + /* .format_failure_message_for */ failure_message_for, + /* .actual_value_message */ "", + /* .expected_value_message */ "", + /* .expected_value */ {CGREEN_INTEGER, {5}}, + /* .stored_value_name */ "null", + /* .parameter_name */ NULL, + /* .size_of_stored_value */ 0 +}; + +Ensure(TestConstraint, custom_constraint_using_static_function) { + Constraint * is_bigger_than_5 = &static_is_bigger_than_5; + assert_that(10, is_bigger_than_5); +} + +int main(int argc, char **argv) { + TestSuite *suite = create_test_suite(); + add_test_with_context(suite, TestConstraint, custom_constraint_using_static_function); + run_test_suite(suite, create_text_reporter()); +} diff --git a/tutorial_src/custom_constraint2.c b/tutorial_src/custom_constraint2.c new file mode 100644 index 00000000..772a506a --- /dev/null +++ b/tutorial_src/custom_constraint2.c @@ -0,0 +1,39 @@ +#include +#include +#include "cgreen_value_internal.h" +#include "utils.h" + +Describe(TestConstraint); +BeforeEach(TestConstraint) {} +AfterEach(TestConstraint) {} + +bool compare_want_smaller_value(Constraint *constraint, CgreenValue actual) { + return actual.value.integer_value < constraint->expected_value.value.integer_value ; +} + +Constraint *create_smaller_than_constraint(intptr_t expected_value, const char *expected_value_name) { + Constraint *constraint = create_constraint(); + + constraint->expected_value = make_cgreen_integer_value(expected_value); + constraint->expected_value_name = string_dup(expected_value_name); + constraint->type = CGREEN_VALUE_COMPARER_CONSTRAINT; + + constraint->compare = &compare_want_smaller_value; + constraint->execute = &test_want; + constraint->name = "be smaller than"; + constraint->size_of_expected_value = sizeof(intptr_t); + + return constraint; +} +#define is_smaller_than(value) create_smaller_than_constraint(value, #value) + + +Ensure(TestConstraint, custom_constraint_using_a_function_with_arguments_function) { + assert_that(9, is_smaller_than(10)); +} + +int main(int argc, char **argv) { + TestSuite *suite = create_test_suite(); + add_test_with_context(suite, TestConstraint, custom_constraint_using_a_function_with_arguments_function); + run_test_suite(suite, create_text_reporter()); +} diff --git a/tutorial_src/custom_constraint3.c b/tutorial_src/custom_constraint3.c new file mode 100644 index 00000000..b825fc0a --- /dev/null +++ b/tutorial_src/custom_constraint3.c @@ -0,0 +1,64 @@ +#include +#include "cgreen_value_internal.h" +#include "utils.h" + +Describe(TestConstraint); +BeforeEach(TestConstraint) {} +AfterEach(TestConstraint) {} + +typedef struct Box { + int id; + int size; +} Box; + +typedef struct Piece { + int id; + int size; +} Piece; + +bool compare_piece_and_box_size(Constraint *constraint, CgreenValue actual) { + return ((Piece *)actual.value.pointer_value)->size + < ((Box*)constraint->expected_value.value.pointer_value)->size ; +} + +static void test_fit_piece(Constraint *constraint, const char *function_name, CgreenValue actual, + const char *test_file, int test_line, TestReporter *reporter) { + (*reporter->assert_true)( + reporter, + test_file, + test_line, + (*constraint->compare)(constraint, actual), + "Piece [%f], does not fit in [%f] in function [%s] parameter [%s]", + ((Piece *)constraint->expected_value.value.pointer_value)->id, + ((Box *)actual.value.pointer_value)->id, + function_name, + constraint->parameter_name); +} + +Constraint *create_piece_fit_in_box_constraint(intptr_t expected_value, const char *expected_value_name) { + Constraint *constraint = create_constraint(); + + constraint->expected_value = make_cgreen_pointer_value((void*)expected_value); + constraint->expected_value_name = string_dup(expected_value_name); + constraint->type = CGREEN_CONTENT_COMPARER_CONSTRAINT; + + constraint->compare = &compare_piece_and_box_size; + constraint->execute = &test_fit_piece; + constraint->name = "fit in box"; + constraint->size_of_expected_value = sizeof(intptr_t); + + return constraint; +} +#define can_fit_in_box(box) create_piece_fit_in_box_constraint((intptr_t)box, #box) + +Ensure(TestConstraint, more_complex_custom_constraint_function) { + Box box1 = {.id = 1, .size = 5}; + Piece piece99 = {.id = 99, .size = 6}; + assert_that(&piece99, can_fit_in_box(&box1)); +} + +int main(int argc, char **argv) { + TestSuite *suite = create_test_suite(); + add_test_with_context(suite, TestConstraint, more_complex_custom_constraint_function); + run_test_suite(suite, create_text_reporter()); +} diff --git a/tutorial_src/double_tests1.c b/tutorial_src/double_tests1.c new file mode 100644 index 00000000..55e25f87 --- /dev/null +++ b/tutorial_src/double_tests1.c @@ -0,0 +1,33 @@ +#include +#include +#include + +#define PI 3.1415926 + +Describe(Doubles); +BeforeEach(Doubles) {} +AfterEach(Doubles) {} + +Ensure(Doubles, can_assert_double_values) { + significant_figures_for_assert_double_are(3); + assert_that_double(3.14, is_equal_to_double(5.0)); +} + +static double double_out(int i, double d) { + return unbox_double(mock(i, box_double(d))); // <1> +} + +Ensure(Doubles, can_be_arguments_to_mock_and_returned) { + expect(double_out, + when(i, is_equal_to(15)), + when(d, is_equal_to_double(31.32)), // <2> + will_return_double(3.1415926)); // <3> + assert_that_double(double_out(15, 31.32), is_equal_to_double(3.1415926)); +} + +int main(int argc, char **argv) { + TestSuite *suite = create_test_suite(); + add_test_with_context(suite, Doubles, can_assert_double_values); + add_test_with_context(suite, Doubles, can_be_arguments_to_mock_and_returned); + return run_test_suite(suite, create_text_reporter()); +} diff --git a/tutorial_src/first_tests0.c b/tutorial_src/first_tests0.c new file mode 100644 index 00000000..8694ec8b --- /dev/null +++ b/tutorial_src/first_tests0.c @@ -0,0 +1,10 @@ +#include + +Describe(Cgreen); +BeforeEach(Cgreen) {} +AfterEach(Cgreen) {} + +int main(int argc, char **argv) { + TestSuite *suite = create_test_suite(); + return run_test_suite(suite, create_text_reporter()); +} diff --git a/tutorial_src/first_tests1.c b/tutorial_src/first_tests1.c new file mode 100644 index 00000000..adf881b9 --- /dev/null +++ b/tutorial_src/first_tests1.c @@ -0,0 +1,20 @@ +#include + +Describe(Cgreen); +BeforeEach(Cgreen) {} +AfterEach(Cgreen) {} + +Ensure(Cgreen, passes_this_test) { + assert_that(1 == 1); +} + +Ensure(Cgreen, fails_this_test) { + assert_that(0 == 1); +} + +int main(int argc, char **argv) { + TestSuite *suite = create_test_suite(); + add_test_with_context(suite, Cgreen, passes_this_test); + add_test_with_context(suite, Cgreen, fails_this_test); + return run_test_suite(suite, create_text_reporter()); +} diff --git a/tutorial_src/first_tests2.c b/tutorial_src/first_tests2.c new file mode 100644 index 00000000..adf881b9 --- /dev/null +++ b/tutorial_src/first_tests2.c @@ -0,0 +1,20 @@ +#include + +Describe(Cgreen); +BeforeEach(Cgreen) {} +AfterEach(Cgreen) {} + +Ensure(Cgreen, passes_this_test) { + assert_that(1 == 1); +} + +Ensure(Cgreen, fails_this_test) { + assert_that(0 == 1); +} + +int main(int argc, char **argv) { + TestSuite *suite = create_test_suite(); + add_test_with_context(suite, Cgreen, passes_this_test); + add_test_with_context(suite, Cgreen, fails_this_test); + return run_test_suite(suite, create_text_reporter()); +} diff --git a/tutorial_src/formatter_tests0.c b/tutorial_src/formatter_tests0.c new file mode 100644 index 00000000..f0d1701c --- /dev/null +++ b/tutorial_src/formatter_tests0.c @@ -0,0 +1,22 @@ +#include +#include + +#include "stream.h" + +Describe(Formatter); +BeforeEach(Formatter) {} +AfterEach(Formatter) {} + +int one_character_stream(void *stream) { return 'a'; } + +void expect_one_letter_paragraph(void *stream, char *paragraph) { + assert_that(paragraph, is_equal_to_string("a")); +} + +Ensure(Formatter, makes_one_letter_paragraph_from_one_character_input) { + by_paragraph( + &one_character_stream, + NULL, + &expect_one_letter_paragraph, + NULL); +} diff --git a/tutorial_src/formatter_tests1.c b/tutorial_src/formatter_tests1.c new file mode 100644 index 00000000..f1939fd0 --- /dev/null +++ b/tutorial_src/formatter_tests1.c @@ -0,0 +1,23 @@ +#include +#include + +#include "stream.h" + +Describe(Formatter); +BeforeEach(Formatter) {} +AfterEach(Formatter) {} + +static int reader(void *stream) { + return (int)mock(stream); +} + +static void writer(void *stream, char *paragraph) { + mock(stream, paragraph); +} + +Ensure(Formatter, makes_one_letter_paragraph_from_one_character_input) { + expect(reader, will_return('a')); + always_expect(reader, will_return(EOF)); + expect(writer, when(paragraph, is_equal_to_string("a"))); + by_paragraph(&reader, NULL, &writer, NULL); +} diff --git a/tutorial_src/formatter_tests2.c b/tutorial_src/formatter_tests2.c new file mode 100644 index 00000000..cf75b014 --- /dev/null +++ b/tutorial_src/formatter_tests2.c @@ -0,0 +1,34 @@ +#include +#include + +#include "stream.h" + +Describe(Formatter); +BeforeEach(Formatter) {} +AfterEach(Formatter) {} + +static int reader(void *stream) { + return (int)mock(stream); +} + +static void writer(void *stream, char *paragraph) { + mock(stream, paragraph); +} + +Ensure(Formatter, makes_one_letter_paragraph_from_one_character_input) { + expect(reader, will_return('a')); + always_expect(reader, will_return(EOF)); + expect(writer, when(paragraph, is_equal_to_string("a"))); + by_paragraph(&reader, NULL, &writer, NULL); +} + +Ensure(Formatter, makes_one_paragraph_if_no_line_endings) { + expect(reader, will_return('a')); + expect(reader, will_return(' ')); + expect(reader, will_return('b')); + expect(reader, will_return(' ')); + expect(reader, will_return('c')); + always_expect(reader, will_return(EOF)); + expect(writer, when(paragraph, is_equal_to_string("a b c"))); + by_paragraph(&reader, NULL, &writer, NULL); +} diff --git a/tutorial_src/formatter_tests3.c b/tutorial_src/formatter_tests3.c new file mode 100644 index 00000000..bf87d09a --- /dev/null +++ b/tutorial_src/formatter_tests3.c @@ -0,0 +1,47 @@ +#include +#include + +#include "stream.h" + +Describe(Formatter); +BeforeEach(Formatter) {} +AfterEach(Formatter) {} + +static int reader(void *stream) { + return (int)mock(stream); +} + +static void writer(void *stream, char *paragraph) { + mock(stream, paragraph); +} + +Ensure(Formatter, makes_one_letter_paragraph_from_one_character_input) { + expect(reader, will_return('a')); + always_expect(reader, will_return(EOF)); + expect(writer, when(paragraph, is_equal_to_string("a"))); + by_paragraph(&reader, NULL, &writer, NULL); +} + +Ensure(Formatter, makes_one_paragraph_if_no_line_endings) { + expect(reader, will_return('a')); + expect(reader, will_return(' ')); + expect(reader, will_return('b')); + expect(reader, will_return(' ')); + expect(reader, will_return('c')); + always_expect(reader, will_return(EOF)); + expect(writer, when(paragraph, is_equal_to_string("a b c"))); + by_paragraph(&reader, NULL, &writer, NULL); +} + +Ensure(Formatter, generates_separate_paragraphs_for_line_endings) { + expect(reader, will_return('a')); + expect(reader, will_return('\n')); + expect(reader, will_return('b')); + expect(reader, will_return('\n')); + expect(reader, will_return('c')); + always_expect(reader, will_return(EOF)); + expect(writer, when(paragraph, is_equal_to_string("a"))); + expect(writer, when(paragraph, is_equal_to_string("b"))); + expect(writer, when(paragraph, is_equal_to_string("c"))); + by_paragraph(&reader, NULL, &writer, NULL); +} diff --git a/tutorial_src/formatter_tests4.c b/tutorial_src/formatter_tests4.c new file mode 100644 index 00000000..4b616165 --- /dev/null +++ b/tutorial_src/formatter_tests4.c @@ -0,0 +1,55 @@ +#include +#include + +#include "stream.h" + +Describe(Formatter); +BeforeEach(Formatter) {} +AfterEach(Formatter) {} + +static int reader(void *stream) { + return (int)mock(stream); +} + +static void writer(void *stream, char *paragraph) { + mock(stream, paragraph); +} + +Ensure(Formatter, makes_one_letter_paragraph_from_one_character_input) { + expect(reader, will_return('a')); + always_expect(reader, will_return(EOF)); + expect(writer, when(paragraph, is_equal_to_string("a"))); + by_paragraph(&reader, NULL, &writer, NULL); +} + +Ensure(Formatter, makes_one_paragraph_if_no_line_endings) { + expect(reader, will_return('a')); + expect(reader, will_return(' ')); + expect(reader, will_return('b')); + expect(reader, will_return(' ')); + expect(reader, will_return('c')); + always_expect(reader, will_return(EOF)); + expect(writer, when(paragraph, is_equal_to_string("a b c"))); + by_paragraph(&reader, NULL, &writer, NULL); +} + +Ensure(Formatter, generates_separate_paragraphs_for_line_endings) { + expect(reader, will_return('a')); + expect(reader, will_return('\n')); + expect(reader, will_return('b')); + expect(reader, will_return('\n')); + expect(reader, will_return('c')); + always_expect(reader, will_return(EOF)); + expect(writer, when(paragraph, is_equal_to_string("a"))); + expect(writer, when(paragraph, is_equal_to_string("b"))); + expect(writer, when(paragraph, is_equal_to_string("c"))); + by_paragraph(&reader, NULL, &writer, NULL); +} + +Ensure(Formatter, pairs_the_functions_with_the_resources) { + expect(reader, when(stream, is_equal_to(1)), will_return('a')); + always_expect(reader, when(stream, is_equal_to(1)), will_return(EOF)); + expect(writer, when(stream, is_equal_to(2))); + by_paragraph(&reader, (void *)1, &writer, (void *)2); +} + diff --git a/tutorial_src/formatter_tests5.c b/tutorial_src/formatter_tests5.c new file mode 100644 index 00000000..e823f676 --- /dev/null +++ b/tutorial_src/formatter_tests5.c @@ -0,0 +1,61 @@ +#include +#include + +#include "stream.h" + +Describe(Formatter); +BeforeEach(Formatter) {} +AfterEach(Formatter) {} + +static int reader(void *stream) { + return (int)mock(stream); +} + +static void writer(void *stream, char *paragraph) { + mock(stream, paragraph); +} + +Ensure(Formatter, makes_one_letter_paragraph_from_one_character_input) { + expect(reader, will_return('a')); + always_expect(reader, will_return(EOF)); + expect(writer, when(paragraph, is_equal_to_string("a"))); + by_paragraph(&reader, NULL, &writer, NULL); +} + +Ensure(Formatter, makes_one_paragraph_if_no_line_endings) { + expect(reader, will_return('a')); + expect(reader, will_return(' ')); + expect(reader, will_return('b')); + expect(reader, will_return(' ')); + expect(reader, will_return('c')); + always_expect(reader, will_return(EOF)); + expect(writer, when(paragraph, is_equal_to_string("a b c"))); + by_paragraph(&reader, NULL, &writer, NULL); +} + +Ensure(Formatter, generates_separate_paragraphs_for_line_endings) { + expect(reader, will_return('a')); + expect(reader, will_return('\n')); + expect(reader, will_return('b')); + expect(reader, will_return('\n')); + expect(reader, will_return('c')); + always_expect(reader, will_return(EOF)); + expect(writer, when(paragraph, is_equal_to_string("a"))); + expect(writer, when(paragraph, is_equal_to_string("b"))); + expect(writer, when(paragraph, is_equal_to_string("c"))); + by_paragraph(&reader, NULL, &writer, NULL); +} + +Ensure(Formatter, pairs_the_functions_with_the_resources) { + expect(reader, when(stream, is_equal_to(1)), will_return('a')); + always_expect(reader, when(stream, is_equal_to(1)), will_return(EOF)); + expect(writer, when(stream, is_equal_to(2))); + by_paragraph(&reader, (void *)1, &writer, (void *)2); +} + +Ensure(Formatter, ignores_empty_paragraphs) { + expect(reader, will_return('\n')); + always_expect(reader, will_return(EOF)); + never_expect(writer); + by_paragraph(&reader, NULL, &writer, NULL); +} diff --git a/tutorial_src/learning_mocks.c b/tutorial_src/learning_mocks.c new file mode 100644 index 00000000..5f12b05b --- /dev/null +++ b/tutorial_src/learning_mocks.c @@ -0,0 +1,24 @@ +#include +#include + +Describe(LearningMocks); +BeforeEach(LearningMocks) {} +AfterEach(LearningMocks) {} + +static int integer_out() { + return (int)mock(); +} + +static char *string_out(int p1) { + return (char *)mock(p1); +} + +Ensure(LearningMocks, emit_pastable_code) { + cgreen_mocks_are(learning_mocks); + string_out(1); + string_out(2); + integer_out(); + integer_out(); + string_out(3); + integer_out(); +} diff --git a/tutorial_src/multiple_streams1.c b/tutorial_src/multiple_streams1.c new file mode 100644 index 00000000..4c70c2ab --- /dev/null +++ b/tutorial_src/multiple_streams1.c @@ -0,0 +1,17 @@ +#include +#include + +void do_stuff(void *stream1, void *stream2) {} + +Describe(Streams); +BeforeEach(Streams) {} +AfterEach(Streams) {} + +static int stream_stub(void *stream) { + return (int)mock(stream); +} + +Ensure(Streams, bad_test) { + expect(stream_stub, will_return('a')); + do_stuff(&stream_stub, &stream_stub); +} diff --git a/tutorial_src/multiple_streams2.c b/tutorial_src/multiple_streams2.c new file mode 100644 index 00000000..c3a8b32c --- /dev/null +++ b/tutorial_src/multiple_streams2.c @@ -0,0 +1,22 @@ +#include +#include + +void do_stuff(void *stream1, void *stream2) {} + +Describe(Streams); +BeforeEach(Streams) {} +AfterEach(Streams) {} + +static int first_stream_stub(void *stream) { + return (int)mock(stream); +} + +static int second_stream_stub(void *stream) { + return (int)mock(stream); +} + +Ensure(Streams, good_test) { + expect(first_stream_stub, will_return('a')); + expect(second_stream_stub, will_return('a')); + do_stuff(&first_stream_stub, &second_stream_stub); +} diff --git a/tutorial_src/person.h b/tutorial_src/person.h new file mode 100644 index 00000000..7f6380b6 --- /dev/null +++ b/tutorial_src/person.h @@ -0,0 +1,11 @@ +#include + +typedef struct _Person { + char *name; +} Person; + +Person *create_person() {return NULL;} +Person *find_person_by_name(const char *name) {return NULL;} +void set_person_name(Person *person, const char *name) {} +char *get_person_name(Person *person) {return NULL;} +int save_person(Person *person) { return 1;} diff --git a/tutorial_src/person_tests.c b/tutorial_src/person_tests.c new file mode 100644 index 00000000..db2a9d7d --- /dev/null +++ b/tutorial_src/person_tests.c @@ -0,0 +1,58 @@ +#include +#include +#include +#include "person.h" + +static MYSQL *connection; + +static void create_schema() { + mysql_query(connection, "create table people (name, varchar(255) unique)"); +} + +static void drop_schema() { + mysql_query(connection, "drop table people"); +} + +Ensure(can_add_person_to_database) { + Person *person = create_person(); + set_person_name(person, "Fred"); + save_person(person); + Person *found = find_person_by_name("Fred"); + assert_that(get_person_name(person), is_equal_to_string("Fred")); +} + +Ensure(cannot_add_duplicate_person) { + Person *person = create_person(); + set_person_name(person, "Fred"); + assert_that(save_person(person), is_null); + Person *duplicate = create_person(); + set_person_name(duplicate, "Fred"); + assert_that(save_person(duplicate), is_null); +} + +void open_connection() { + connection = mysql_init(NULL); + mysql_real_connect(connection, "localhost", "me", "secret", "test", 0, NULL, 0); +} + +void close_connection() { + mysql_close(connection); +} + +TestSuite *person_tests() { + TestSuite *suite = create_test_suite(); + set_setup(suite, create_schema); + set_teardown(suite, drop_schema); + add_test(suite, can_add_person_to_database); + add_test(suite, cannot_add_duplicate_person); + + TestSuite *fixture = create_named_test_suite("Mysql"); + add_suite(fixture, suite); + set_setup(fixture, open_connection); + set_teardown(fixture, close_connection); + return fixture; +} + +int main(int argc, char **argv) { + return run_test_suite(person_tests(), create_text_reporter()); +} diff --git a/tutorial_src/read_paragraph1.c b/tutorial_src/read_paragraph1.c new file mode 100644 index 00000000..680ff234 --- /dev/null +++ b/tutorial_src/read_paragraph1.c @@ -0,0 +1,19 @@ +#include +#include + +char *read_paragraph(int (*read)(void *), void *stream) { + int buffer_size = 0, length = 0; + char *buffer = NULL; + int ch; + while ((ch = (*read)(stream)) != EOF) { + if (++length > buffer_size) { + buffer_size += 100; + buffer = (char *)realloc(buffer, buffer_size + 1); + } + if ((buffer[length] = ch) == '\n') { + break; + } + buffer[length + 1] = '\0'; + } + return buffer; +} diff --git a/tutorial_src/read_paragraph2.c b/tutorial_src/read_paragraph2.c new file mode 100644 index 00000000..070bacfa --- /dev/null +++ b/tutorial_src/read_paragraph2.c @@ -0,0 +1,19 @@ +#include +#include + +char *read_paragraph(int (*read)(void *), void *stream) { + int buffer_size = 0, length = 0; + char *buffer = NULL; + int ch; + while ((ch = (*read)(stream)) != EOF) { + if (++length > buffer_size) { + buffer_size += 100; + buffer = (char *)realloc(buffer, buffer_size + 1); + } + if ((buffer[length - 1] = ch) == '\n') { // <1> + break; + } + buffer[length] = '\0'; // <2> + } + return buffer; +} diff --git a/tutorial_src/read_paragraph3.c b/tutorial_src/read_paragraph3.c new file mode 100644 index 00000000..9a6caf91 --- /dev/null +++ b/tutorial_src/read_paragraph3.c @@ -0,0 +1,20 @@ +#include +#include + +char *read_paragraph(int (*read)(void *), void *stream) { + int buffer_size = 0, length = 0; + char *buffer = NULL; + int ch; + while ((ch = (*read)(stream)) != EOF) { + if (++length > buffer_size) { + buffer_size += 100; + buffer = (char *)realloc(buffer, buffer_size + 1); + } + if ((buffer[length - 1] = ch) == '\n') { + buffer[--length] = '\0'; + break; + } + buffer[length] = '\0'; + } + return buffer; +} diff --git a/tutorial_src/roman_tests.c b/tutorial_src/roman_tests.c new file mode 100644 index 00000000..fb157b42 --- /dev/null +++ b/tutorial_src/roman_tests.c @@ -0,0 +1,13 @@ +#include + +static int convert_roman_to_decimal(char *roman) { + return 0; +} + +Describe(Converter); +BeforeEach(Converter) {} +AfterEach(Converter) {} + +Ensure(Converter, converts_XIV_to_14) { + assert_that(convert_roman_to_decimal("XIV"), is_equal_to(14)); +} diff --git a/tutorial_src/schema_tests1.c b/tutorial_src/schema_tests1.c new file mode 100644 index 00000000..c5b49850 --- /dev/null +++ b/tutorial_src/schema_tests1.c @@ -0,0 +1,54 @@ +#include +#include +#include +#include "person.h" + +Describe(Person); +BeforeEach(Person) {} +AfterEach(Person) {} + +static void create_schema() { + MYSQL *connection = mysql_init(NULL); + mysql_real_connect(connection, "localhost", "me", "secret", "test", 0, NULL, 0); + mysql_query(connection, "create table people (name, varchar(255) unique)"); + mysql_close(connection); +} + +static void drop_schema() { + MYSQL *connection = mysql_init(NULL); + mysql_real_connect(connection, "localhost", "me", "secret", "test", 0, NULL, 0); + mysql_query(connection, "drop table people"); + mysql_close(connection); +} + +Ensure(Person, can_add_person_to_database) { + create_schema(); + Person *person = create_person(); + set_person_name(person, "Fred"); + save_person(person); + Person *found = find_person_by_name("Fred"); + assert_that(get_person_name(found), is_equal_to_string("Fred")); + drop_schema(); +} + +Ensure(Person, cannot_add_duplicate_person) { + create_schema(); + Person *person = create_person(); + set_person_name(person, "Fred"); + assert_that(save_person(person), is_true); + Person *duplicate = create_person(); + set_person_name(duplicate, "Fred"); + assert_that(save_person(duplicate), is_false); + drop_schema(); +} + +TestSuite *person_tests() { + TestSuite *suite = create_test_suite(); + add_test_with_context(suite, Person, can_add_person_to_database); + add_test_with_context(suite, Person, cannot_add_duplicate_person); + return suite; +} + +int main(int argc, char **argv) { + return run_test_suite(person_tests(), create_text_reporter()); +} diff --git a/tutorial_src/schema_tests2.c b/tutorial_src/schema_tests2.c new file mode 100644 index 00000000..af8259e2 --- /dev/null +++ b/tutorial_src/schema_tests2.c @@ -0,0 +1,50 @@ +#include +#include +#include +#include "person.h" + +static void create_schema() { + MYSQL *connection = mysql_init(NULL); + mysql_real_connect(connection, "localhost", "me", "secret", "test", 0, NULL, 0); + mysql_query(connection, "create table people (name, varchar(255) unique)"); + mysql_close(connection); +} + +static void drop_schema() { + MYSQL *connection = mysql_init(NULL); + mysql_real_connect(connection, "localhost", "me", "secret", "test", 0, NULL, 0); + mysql_query(connection, "drop table people"); + mysql_close(connection); +} + +Describe(Person); +BeforeEach(Person) { create_schema(); } +AfterEach(Person) { drop_schema(); } + +Ensure(Person, can_add_person_to_database) { + Person *person = create_person(); + set_person_name(person, "Fred"); + save_person(person); + Person *found = find_person_by_name("Fred"); + assert_that(get_person_name(found), is_equal_to_string("Fred")); +} + +Ensure(Person, cannot_add_duplicate_person) { + Person *person = create_person(); + set_person_name(person, "Fred"); + assert_that(save_person(person), is_true); + Person *duplicate = create_person(); + set_person_name(duplicate, "Fred"); + assert_that(save_person(duplicate), is_false); +} + +TestSuite *person_tests() { + TestSuite *suite = create_test_suite(); + add_test_with_context(suite, Person, can_add_person_to_database); + add_test_with_context(suite, Person, cannot_add_duplicate_person); + return suite; +} + +int main(int argc, char **argv) { + return run_test_suite(person_tests(), create_text_reporter()); +} diff --git a/tutorial_src/set_contents.c b/tutorial_src/set_contents.c new file mode 100644 index 00000000..3877c253 --- /dev/null +++ b/tutorial_src/set_contents.c @@ -0,0 +1,14 @@ +#include +#include + +void convert_to_uppercase(char *converted_string, const char *original_string) { + mock(converted_string, original_string); +} + +Ensure(setting_content_of_out_parameter) { + expect(convert_to_uppercase, + when(original_string, is_equal_to_string("upper case")), + will_set_contents_of_parameter(converted_string, + "UPPER CASE", 11)); + +} diff --git a/tutorial_src/set_field.c b/tutorial_src/set_field.c new file mode 100644 index 00000000..4df3e46b --- /dev/null +++ b/tutorial_src/set_field.c @@ -0,0 +1,18 @@ +#include +#include + +struct structure { + int field; + char *string; +}; + +void update_field(struct structure *struct_to_update) { + int *field = &struct_to_update->field; + mock(struct_to_update, field); +} + +Ensure(setting_field_of_parameter) { + int fourty_two = 42; + expect(update_field, + will_set_contents_of_parameter(field, &fourty_two, sizeof(int))); +} diff --git a/tutorial_src/side_effect.c b/tutorial_src/side_effect.c new file mode 100644 index 00000000..e608fb01 --- /dev/null +++ b/tutorial_src/side_effect.c @@ -0,0 +1,44 @@ +#include +#include + +static int reader(void *stream) { + return (int)mock(stream); +} + +static void writer(void *stream, char *paragraph) { + mock(stream, paragraph); +} + +char *read_paragraph(int (*read)(void *), void *stream); + +void by_paragraph(int (*read)(void *), void *in, void (*write)(void *, char *), void *out) { + while (1) { + char *line = read_paragraph(read, in); + if ((line == NULL) || (strlen(line) == 0)) { + return; + } + (*write)(out, line); + free(line); + } +} + +static int stub_stream(void *stream) { + return (int)mock(stream); +} + +static void update_counter(void * counter) { + *(int*)counter = *(int*)counter + 1; +} + +Ensure(using_side_effect) { + int number_of_times_reader_was_called = 0; + expect(reader, will_return('\n')); + always_expect(reader, + will_return(EOF), + with_side_effect(&update_counter, + &number_of_times_reader_was_called)); + never_expect(writer); + by_paragraph(&reader, NULL, &writer, NULL); + + assert_that(number_of_times_reader_was_called, is_equal_to(1)); +} diff --git a/tutorial_src/stream.c b/tutorial_src/stream.c new file mode 100644 index 00000000..49104ee2 --- /dev/null +++ b/tutorial_src/stream.c @@ -0,0 +1,32 @@ +#include +#include +#include + +char *read_paragraph(int (*read)(void *), void *stream) { + int buffer_size = 0, length = 0; + char *buffer = NULL; + int ch; + while ((ch = (*read)(stream)) != EOF) { + if (++length > buffer_size) { + buffer_size += 100; + buffer = realloc(buffer, buffer_size + 1); + } + if ((buffer[length - 1] = ch) == '\n') { + buffer[--length] = '\0'; + break; + } + buffer[length] = '\0'; + } + return buffer; +} + +void by_paragraph(int (*read)(void *), void *in, void (*write)(void *, char *), void *out) { + while (1) { + char *line = read_paragraph(read, in); + if ((line == NULL) || (strlen(line) == 0)) { + return; + } + (*write)(out, line); + free(line); + } +} diff --git a/tutorial_src/stream.h b/tutorial_src/stream.h new file mode 100644 index 00000000..4bfd80b3 --- /dev/null +++ b/tutorial_src/stream.h @@ -0,0 +1,2 @@ +char *read_paragraph(int (*read)(void *), void *stream); +void by_paragraph(int (*read)(void *), void *in, void (*write)(void *, char *), void *out); diff --git a/tutorial_src/stream1.c b/tutorial_src/stream1.c new file mode 100644 index 00000000..168252f2 --- /dev/null +++ b/tutorial_src/stream1.c @@ -0,0 +1,32 @@ +#include +#include +#include + +char *read_paragraph(int (*read)(void *), void *stream) { + int buffer_size = 0, length = 0; + char *buffer = NULL; + int ch; + while ((ch = (*read)(stream)) != EOF) { + if (++length > buffer_size) { + buffer_size += 100; + buffer = realloc(buffer, buffer_size + 1); + } + if ((buffer[length - 1] = ch) == '\n') { + buffer[--length] = '\0'; + break; + } + buffer[length] = '\0'; + } + return buffer; +} + +void by_paragraph(int (*read)(void *), void *in, void (*write)(void *, char *), void *out) { + while (1) { + char *line = read_paragraph(read, in); + if (line == NULL) { + return; + } + (*write)(out, line); + free(line); + } +} diff --git a/tutorial_src/stream2.c b/tutorial_src/stream2.c new file mode 100644 index 00000000..49104ee2 --- /dev/null +++ b/tutorial_src/stream2.c @@ -0,0 +1,32 @@ +#include +#include +#include + +char *read_paragraph(int (*read)(void *), void *stream) { + int buffer_size = 0, length = 0; + char *buffer = NULL; + int ch; + while ((ch = (*read)(stream)) != EOF) { + if (++length > buffer_size) { + buffer_size += 100; + buffer = realloc(buffer, buffer_size + 1); + } + if ((buffer[length - 1] = ch) == '\n') { + buffer[--length] = '\0'; + break; + } + buffer[length] = '\0'; + } + return buffer; +} + +void by_paragraph(int (*read)(void *), void *in, void (*write)(void *, char *), void *out) { + while (1) { + char *line = read_paragraph(read, in); + if ((line == NULL) || (strlen(line) == 0)) { + return; + } + (*write)(out, line); + free(line); + } +} diff --git a/tutorial_src/stream_tests0.c b/tutorial_src/stream_tests0.c new file mode 100644 index 00000000..bc492c56 --- /dev/null +++ b/tutorial_src/stream_tests0.c @@ -0,0 +1,16 @@ +#include +#include + +char *read_paragraph(int (*read)(void *), void *stream); + +static int empty_stream(void *stream) { + return EOF; +} + +Describe(ParagraphReader); +BeforeEach(ParagraphReader) {} +AfterEach(ParagraphReader) {} + +Ensure(ParagraphReader, gives_null_when_reading_empty_stream) { + assert_that(read_paragraph(&empty_stream, NULL), is_null); +} diff --git a/tutorial_src/stream_tests1.c b/tutorial_src/stream_tests1.c new file mode 100644 index 00000000..461eeef4 --- /dev/null +++ b/tutorial_src/stream_tests1.c @@ -0,0 +1,17 @@ +#include +#include + +char *read_paragraph(int (*read)(void *), void *stream); + +static int stream_stub(void *stream) { + return (int)mock(stream); +} + +Describe(ParagraphReader); +BeforeEach(ParagraphReader) {} +AfterEach(ParagraphReader) {} + +Ensure(ParagraphReader, gives_null_when_reading_empty_stream) { + always_expect(stream_stub, will_return(EOF)); // <1> + assert_that(read_paragraph(&stream_stub, NULL), is_null); +} diff --git a/tutorial_src/stream_tests2.c b/tutorial_src/stream_tests2.c new file mode 100644 index 00000000..ad3b0829 --- /dev/null +++ b/tutorial_src/stream_tests2.c @@ -0,0 +1,25 @@ +#include +#include + +char *read_paragraph(int (*read)(void *), void *stream); + +static int stream_stub(void *stream) { + return (int)mock(stream); +} + +Describe(ParagraphReader); +BeforeEach(ParagraphReader) {} +AfterEach(ParagraphReader) {} + +Ensure(ParagraphReader, gives_null_when_reading_empty_stream) { + always_expect(stream_stub, will_return(EOF)); // <1> + assert_that(read_paragraph(&stream_stub, NULL), is_null); +} + +Ensure(ParagraphReader, gives_one_character_line_for_one_character_stream) { + expect(stream_stub, will_return('a')); + expect(stream_stub, will_return(EOF)); + char *line = read_paragraph(&stream_stub, NULL); + assert_that(line, is_equal_to_string("a")); + free(line); +} diff --git a/tutorial_src/stream_tests3.c b/tutorial_src/stream_tests3.c new file mode 100644 index 00000000..da6ab769 --- /dev/null +++ b/tutorial_src/stream_tests3.c @@ -0,0 +1,33 @@ +#include +#include + +char *read_paragraph(int (*read)(void *), void *stream); + +static int stream_stub(void *stream) { + return (int)mock(stream); +} + +Describe(ParagraphReader); +BeforeEach(ParagraphReader) {} +AfterEach(ParagraphReader) {} + +Ensure(ParagraphReader, gives_null_when_reading_empty_stream) { + always_expect(stream_stub, will_return(EOF)); // <1> + assert_that(read_paragraph(&stream_stub, NULL), is_null); +} + +Ensure(ParagraphReader, gives_one_character_line_for_one_character_stream) { + expect(stream_stub, will_return('a')); + expect(stream_stub, will_return(EOF)); + char *line = read_paragraph(&stream_stub, NULL); + assert_that(line, is_equal_to_string("a")); + free(line); +} + +Ensure(ParagraphReader, gives_one_word_line_for_one_word_stream) { + expect(stream_stub, will_return('t')); + expect(stream_stub, will_return('h')); + expect(stream_stub, will_return('e')); + always_expect(stream_stub, will_return(EOF)); + assert_that(read_paragraph(&stream_stub, NULL), is_equal_to_string("the")); +} diff --git a/tutorial_src/stream_tests4.c b/tutorial_src/stream_tests4.c new file mode 100644 index 00000000..6d346b79 --- /dev/null +++ b/tutorial_src/stream_tests4.c @@ -0,0 +1,46 @@ +#include +#include + +char *read_paragraph(int (*read)(void *), void *stream); + +static int stream_stub(void *stream) { + return (int)mock(stream); +} + +Describe(ParagraphReader); +BeforeEach(ParagraphReader) {} +AfterEach(ParagraphReader) {} + +Ensure(ParagraphReader, gives_null_when_reading_empty_stream) { + always_expect(stream_stub, will_return(EOF)); // <1> + assert_that(read_paragraph(&stream_stub, NULL), is_null); +} + +Ensure(ParagraphReader, gives_one_character_line_for_one_character_stream) { + expect(stream_stub, will_return('a')); + expect(stream_stub, will_return(EOF)); + char *line = read_paragraph(&stream_stub, NULL); + assert_that(line, is_equal_to_string("a")); + free(line); +} + +Ensure(ParagraphReader, gives_one_word_line_for_one_word_stream) { + expect(stream_stub, will_return('t')); + expect(stream_stub, will_return('h')); + expect(stream_stub, will_return('e')); + always_expect(stream_stub, will_return(EOF)); + assert_that(read_paragraph(&stream_stub, NULL), is_equal_to_string("the")); +} + +Ensure(ParagraphReader, drops_line_ending_from_word_and_stops) { + expect(stream_stub, will_return('t')); + expect(stream_stub, will_return('h')); + expect(stream_stub, will_return('e')); + expect(stream_stub, will_return('\n')); + assert_that(read_paragraph(&stream_stub, NULL), is_equal_to_string("the")); +} + +Ensure(ParagraphReader, gives_empty_line_for_single_line_ending) { + expect(stream_stub, will_return('\n')); + assert_that(read_paragraph(&stream_stub, NULL), is_equal_to_string("")); +} diff --git a/tutorial_src/strlen_tests1.c b/tutorial_src/strlen_tests1.c new file mode 100644 index 00000000..7f9b692f --- /dev/null +++ b/tutorial_src/strlen_tests1.c @@ -0,0 +1,20 @@ +#include +#include + +Describe(Strlen); +BeforeEach(Strlen) {} +AfterEach(Strlen) {} + +Ensure(Strlen, returns_five_for_hello) { + assert_that(strlen("Hello"), is_equal_to(5)); +} + +TestSuite *our_tests() { + TestSuite *suite = create_test_suite(); + add_test_with_context(suite, Strlen, returns_five_for_hello); + return suite; +} + +int main(int argc, char **argv) { + return run_test_suite(our_tests(), create_text_reporter()); +} diff --git a/tutorial_src/strlen_tests2.c b/tutorial_src/strlen_tests2.c new file mode 100644 index 00000000..ae723315 --- /dev/null +++ b/tutorial_src/strlen_tests2.c @@ -0,0 +1,20 @@ +#include +#include + +Describe(Strlen); // <1> +BeforeEach(Strlen) {} // <2> +AfterEach(Strlen) {} // <3> + +Ensure(Strlen, returns_five_for_hello) { // <4> + assert_that(strlen("Hello"), is_equal_to(5)); +} + +TestSuite *our_tests() { + TestSuite *suite = create_test_suite(); + add_test_with_context(suite, Strlen, returns_five_for_hello); // <5> + return suite; +} + +int main(int argc, char **argv) { + return run_test_suite(our_tests(), create_text_reporter()); +} diff --git a/tutorial_src/strlen_tests3.c b/tutorial_src/strlen_tests3.c new file mode 100644 index 00000000..90ca6e92 --- /dev/null +++ b/tutorial_src/strlen_tests3.c @@ -0,0 +1,16 @@ +#include +#include + // <1> +Ensure(strlen_returns_five_for_hello) { // <2> + assert_that(strlen("Hello"), is_equal_to(5)); +} + +TestSuite *our_tests() { + TestSuite *suite = create_test_suite(); + add_test(suite, strlen_returns_five_for_hello); // <3> + return suite; +} + +int main(int argc, char **argv) { + return run_test_suite(our_tests(), create_text_reporter()); +} diff --git a/tutorial_src/strlen_tests4.c b/tutorial_src/strlen_tests4.c new file mode 100644 index 00000000..26630d93 --- /dev/null +++ b/tutorial_src/strlen_tests4.c @@ -0,0 +1,18 @@ +#include +#include + +Ensure(strlen_of_hello_is_five) { + const char *greeting = "Hello"; + int length = strlen(greeting); + assert_equal_with_message(length, 5, "[%s] should be 5, but was %d", greeting, length); +} + + TestSuite *our_tests() { + TestSuite *suite = create_test_suite(); + add_test(suite, strlen_of_hello_is_five); + return suite; +} + +int main(int argc, char **argv) { + return run_test_suite(our_tests(), create_text_reporter()); +} diff --git a/tutorial_src/strlen_tests5.c b/tutorial_src/strlen_tests5.c new file mode 100644 index 00000000..7f9b692f --- /dev/null +++ b/tutorial_src/strlen_tests5.c @@ -0,0 +1,20 @@ +#include +#include + +Describe(Strlen); +BeforeEach(Strlen) {} +AfterEach(Strlen) {} + +Ensure(Strlen, returns_five_for_hello) { + assert_that(strlen("Hello"), is_equal_to(5)); +} + +TestSuite *our_tests() { + TestSuite *suite = create_test_suite(); + add_test_with_context(suite, Strlen, returns_five_for_hello); + return suite; +} + +int main(int argc, char **argv) { + return run_test_suite(our_tests(), create_text_reporter()); +} diff --git a/tutorial_src/strlen_tests6.c b/tutorial_src/strlen_tests6.c new file mode 100644 index 00000000..22d359e0 --- /dev/null +++ b/tutorial_src/strlen_tests6.c @@ -0,0 +1,20 @@ +#include +#include + +Describe(Strlen); +BeforeEach(Strlen) {} +AfterEach(Strlen) {} + +Ensure(Strlen, returns_five_for_hello) { + assert_that(strlen("Hiya"), is_equal_to(5)); +} + +TestSuite *our_tests() { + TestSuite *suite = create_test_suite(); + add_test_with_context(suite, Strlen, returns_five_for_hello); + return suite; +} + +int main(int argc, char **argv) { + return run_test_suite(our_tests(), create_text_reporter()); +} diff --git a/tutorial_src/strlen_tests7.c b/tutorial_src/strlen_tests7.c new file mode 100644 index 00000000..c29100ec --- /dev/null +++ b/tutorial_src/strlen_tests7.c @@ -0,0 +1,25 @@ +#include +#include + +Describe(Strlen); +BeforeEach(Strlen) {} +AfterEach(Strlen) {} + +Ensure(Strlen, returns_five_for_hello) { + assert_that(strlen("Hiya"), is_equal_to(5)); +} + +Ensure(Strlen, returns_zero_for_empty_string) { + assert_equal(strlen("\0"), 0); +} + +TestSuite *our_tests() { + TestSuite *suite = create_test_suite(); + add_test_with_context(suite, Strlen, returns_five_for_hello); + add_test_with_context(suite, Strlen, returns_zero_for_empty_string); + return suite; +} + +int main(int argc, char **argv) { + return run_test_suite(our_tests(), create_text_reporter()); +} diff --git a/tutorial_src/struct_parameters.c b/tutorial_src/struct_parameters.c new file mode 100644 index 00000000..18ea6825 --- /dev/null +++ b/tutorial_src/struct_parameters.c @@ -0,0 +1,55 @@ +#include +#include +#include + +Describe(StructParameters); +BeforeEach(StructParameters) {} +AfterEach(StructParameters) {} + +/* + This is uncompilable code that is inserted as a non-example + +typedef struct { + int i; + char *string; +} Struct; + +int function_taking_struct(Struct s) { + return (int)mock(?); +} +*/ + +typedef struct { + int i; + char *string; +} Struct; + +int function_checking_a_field(Struct s) { + return (int)mock(s.i); +} + + +Ensure(StructParameters, can_mock_field_in_parameter) { + Struct struct_to_send = { .i = 12, .string = "hello" }; + + expect(function_checking_a_field, when(s.i, is_equal_to(12)), + will_return(12)); + + assert_that(function_checking_a_field(struct_to_send), is_equal_to(12)); + +} + +int function_checking_multiple_fields(Struct s) { + return (int)mock(s.i, s.string); +} + +Ensure(StructParameters, can_mock_muultiple_fields_in_parameter) { + Struct struct_to_send = { .i = 13, .string = "hello world!" }; + + expect(function_checking_multiple_fields, + when(s.i, is_equal_to(13)), + when(s.string, begins_with_string("hello")), + will_return(13)); + + assert_that(function_checking_multiple_fields(struct_to_send), is_equal_to(13)); +} diff --git a/tutorial_src/suite1.c b/tutorial_src/suite1.c new file mode 100644 index 00000000..c86f5719 --- /dev/null +++ b/tutorial_src/suite1.c @@ -0,0 +1,14 @@ +#include + +TestSuite *our_tests(); +TestSuite *person_tests(); + +int main(int argc, char **argv) { + TestSuite *suite = create_test_suite(); + add_suite(suite, our_tests()); + add_suite(suite, person_tests()); + if (argc > 1) { + return run_single_test(suite, argv[1], create_text_reporter()); + } + return run_test_suite(suite, create_text_reporter()); +} diff --git a/tutorial_src/suite_person_tests.c b/tutorial_src/suite_person_tests.c new file mode 100644 index 00000000..1a408a0d --- /dev/null +++ b/tutorial_src/suite_person_tests.c @@ -0,0 +1,46 @@ +#include +#include +#include +#include "person.h" + +static void create_schema() { + MYSQL *connection = mysql_init(NULL); + mysql_real_connect(connection, "localhost", "me", "secret", "test", 0, NULL, 0); + mysql_query(connection, "create table people (name, varchar(255) unique)"); + mysql_close(connection); +} + +static void drop_schema() { + MYSQL *connection = mysql_init(NULL); + mysql_real_connect(connection, "localhost", "me", "secret", "test", 0, NULL, 0); + mysql_query(connection, "drop table people"); + mysql_close(connection); +} + +Describe(Person); +BeforeEach(Person) { create_schema(); } +AfterEach(Person) { drop_schema(); } + +Ensure(Person, can_add_person_to_database) { + Person *person = create_person(); + set_person_name(person, "Fred"); + save_person(person); + Person *found = find_person_by_name("Fred"); + assert_that(get_person_name(found), is_equal_to_string("Fred")); +} + +Ensure(Person, cannot_add_duplicate_person) { + Person *person = create_person(); + set_person_name(person, "Fred"); + assert_that(save_person(person), is_true); + Person *duplicate = create_person(); + set_person_name(duplicate, "Fred"); + assert_that(save_person(duplicate), is_false); +} + +TestSuite *person_tests() { + TestSuite *suite = create_test_suite(); + add_test_with_context(suite, Person, can_add_person_to_database); + add_test_with_context(suite, Person, cannot_add_duplicate_person); + return suite; +} diff --git a/tutorial_src/suite_strlen_tests.c b/tutorial_src/suite_strlen_tests.c new file mode 100644 index 00000000..440ef65b --- /dev/null +++ b/tutorial_src/suite_strlen_tests.c @@ -0,0 +1,22 @@ +#include +#include + +Describe(Strlen); +BeforeEach(Strlen) {} +AfterEach(Strlen) {} + +Ensure(Strlen, returns_five_for_hello) { + assert_that(strlen("Hiya"), is_equal_to(5)); +} + +Ensure(Strlen, returns_zero_for_empty_string) { + assert_equal(strlen("\0"), 0); +} + +TestSuite *our_tests() { + TestSuite *suite = create_test_suite(); + add_test_with_context(suite, Strlen, returns_five_for_hello); + add_test_with_context(suite, Strlen, returns_zero_for_empty_string); + return suite; +} + diff --git a/tutorial_src/test_as_xml0.c b/tutorial_src/test_as_xml0.c new file mode 100644 index 00000000..045fd73e --- /dev/null +++ b/tutorial_src/test_as_xml0.c @@ -0,0 +1,26 @@ +#include + +Describe(XML_reporter); +BeforeEach(XML_reporter) {} +AfterEach(XML_reporter) {} + +Ensure(XML_reporter, reports_a_test_that_passes) { + assert_that(1 == 1); +} + +Ensure(XML_reporter, reports_a_test_that_fails) { + fail_test("A failure"); +} + +TestSuite *create_test_group() { + TestSuite *suite = create_named_test_suite("A Group"); + add_test_with_context(suite, XML_reporter, reports_a_test_that_passes); + add_test_with_context(suite, XML_reporter, reports_a_test_that_fails); + return suite; +} + +int main(int argc, char **argv) { + TestSuite *suite = create_named_test_suite("Top Level"); + add_suite(suite, create_test_group()); + return run_test_suite(suite, create_text_reporter()); +} diff --git a/tutorial_src/test_as_xml1.c b/tutorial_src/test_as_xml1.c new file mode 100644 index 00000000..66190286 --- /dev/null +++ b/tutorial_src/test_as_xml1.c @@ -0,0 +1,29 @@ +#include + +#include +#include "xml_reporter.h" + +Describe(XML_reporter); +BeforeEach(XML_reporter) {} +AfterEach(XML_reporter) {} + +Ensure(XML_reporter, reports_a_test_that_passes) { + assert_that(1 == 1); +} + +Ensure(XML_reporter, reports_a_test_that_fails) { + fail_test("A failure"); +} + +TestSuite *create_test_group() { + TestSuite *suite = create_named_test_suite("A Group"); + add_test_with_context(suite, XML_reporter, reports_a_test_that_passes); + add_test_with_context(suite, XML_reporter, reports_a_test_that_fails); + return suite; +} + +int main(int argc, char **argv) { + TestSuite *suite = create_named_test_suite("Top Level"); + add_suite(suite, create_test_group()); + return run_test_suite(suite, create_xml_reporter()); +} diff --git a/tutorial_src/words.c b/tutorial_src/words.c new file mode 100644 index 00000000..04db82c0 --- /dev/null +++ b/tutorial_src/words.c @@ -0,0 +1,23 @@ +#include + +int split_words(char *sentence) { + int i, count = 1, length = strlen(sentence); + for (i = 0; i < length; i++) { + if (sentence[i] == ' ') { + sentence[i] = '\0'; + count++; + } + } + return count; +} + +void words(const char *sentence, void (*walker)(const char *, void *), void *memo) { + char *words = strdup(sentence); + int word_count = split_words(words); + char *word = words; + while (word_count-- > 0) { + (*walker)(word, memo); + word = word + strlen(word) + 1; + } + free(words); +} diff --git a/tutorial_src/words.h b/tutorial_src/words.h new file mode 100644 index 00000000..f1716a4d --- /dev/null +++ b/tutorial_src/words.h @@ -0,0 +1,2 @@ +int split_words(char *sentence); +void words(const char *sentence, void (*callback)(const char *, void *), void *memo); diff --git a/tutorial_src/words1.c b/tutorial_src/words1.c new file mode 100644 index 00000000..eb288dd3 --- /dev/null +++ b/tutorial_src/words1.c @@ -0,0 +1,5 @@ +#include + +int split_words(char *sentence) { + return 0; +} diff --git a/tutorial_src/words2.c b/tutorial_src/words2.c new file mode 100644 index 00000000..4d72a032 --- /dev/null +++ b/tutorial_src/words2.c @@ -0,0 +1,11 @@ +#include + +int split_words(char *sentence) { + int i, count = 1; + for (i = 0; i < strlen(sentence); i++) { + if (sentence[i] == ' ') { + count++; + } + } + return count; +} diff --git a/tutorial_src/words3.c b/tutorial_src/words3.c new file mode 100644 index 00000000..e9890129 --- /dev/null +++ b/tutorial_src/words3.c @@ -0,0 +1,12 @@ +#include + +int split_words(char *sentence) { + int i, count = 1; + for (i = 0; i < strlen(sentence); i++) { + if (sentence[i] == ' ') { + sentence[i] = '\0'; + count++; + } + } + return count; +} diff --git a/tutorial_src/words4.c b/tutorial_src/words4.c new file mode 100644 index 00000000..37a16434 --- /dev/null +++ b/tutorial_src/words4.c @@ -0,0 +1,12 @@ +#include + +int split_words(char *sentence) { + int i, count = 1, length = strlen(sentence); + for (i = 0; i < length; i++) { + if (sentence[i] == ' ') { + sentence[i] = '\0'; + count++; + } + } + return count; +} diff --git a/tutorial_src/words5.c b/tutorial_src/words5.c new file mode 100644 index 00000000..e248457f --- /dev/null +++ b/tutorial_src/words5.c @@ -0,0 +1,15 @@ +#include + +int split_words(char *sentence) { + int i, count = 1, length = strlen(sentence); + for (i = 0; i < length; i++) { + if (sentence[i] == ' ') { + sentence[i] = '\0'; + count++; + } + } + return count; +} + +void words(const char *sentence, void (*callback)(const char *, void *), void *memo) { +} diff --git a/tutorial_src/words6.c b/tutorial_src/words6.c new file mode 100644 index 00000000..6468cff4 --- /dev/null +++ b/tutorial_src/words6.c @@ -0,0 +1,16 @@ +#include + +int split_words(char *sentence) { + int i, count = 1, length = strlen(sentence); + for (i = 0; i < length; i++) { + if (sentence[i] == ' ') { + sentence[i] = '\0'; + count++; + } + } + return count; +} + +void words(const char *sentence, void (*callback)(const char *, void *), void *memo) { + (*callback)(sentence, memo); +} diff --git a/tutorial_src/words7.c b/tutorial_src/words7.c new file mode 100644 index 00000000..d8eddd34 --- /dev/null +++ b/tutorial_src/words7.c @@ -0,0 +1,24 @@ +#include +#include + +int split_words(char *sentence) { + int i, count = 1, length = strlen(sentence); + for (i = 0; i < length; i++) { + if (sentence[i] == ' ') { + sentence[i] = '\0'; + count++; + } + } + return count; +} + +void words(const char *sentence, void (*callback)(const char *, void *), void *memo) { + char *words = strdup(sentence); + int word_count = split_words(words); + char *word = words; + while (word_count-- > 0) { + (*callback)(word, memo); + word = word + strlen(word) + 1; + } + free(words); +} diff --git a/tutorial_src/words_tests0.c b/tutorial_src/words_tests0.c new file mode 100644 index 00000000..19a6eaaa --- /dev/null +++ b/tutorial_src/words_tests0.c @@ -0,0 +1,14 @@ +#include +#include + +#include "words.h" +#include + +Describe(Words); +BeforeEach(Words) {} +AfterEach(Words) {} + +TestSuite *words_tests() { + TestSuite *suite = create_test_suite(); + return suite; +} diff --git a/tutorial_src/words_tests1.c b/tutorial_src/words_tests1.c new file mode 100644 index 00000000..4df9e086 --- /dev/null +++ b/tutorial_src/words_tests1.c @@ -0,0 +1,21 @@ +#include + +#include "words.h" +#include + +Describe(Words); +BeforeEach(Words) {} +AfterEach(Words) {} + +Ensure(Words, returns_word_count) { + char *sentence = strdup("Birds of a feather"); + int word_count = split_words(sentence); + assert_that(word_count, is_equal_to(4)); + free(sentence); +} + +TestSuite *words_tests() { + TestSuite *suite = create_test_suite(); + add_test_with_context(suite, Words, returns_word_count); + return suite; +} diff --git a/tutorial_src/words_tests2.c b/tutorial_src/words_tests2.c new file mode 100644 index 00000000..623e73b0 --- /dev/null +++ b/tutorial_src/words_tests2.c @@ -0,0 +1,30 @@ +#include + +#include "words.h" +#include + +Describe(Words); +BeforeEach(Words) {} +AfterEach(Words) {} + +Ensure(Words, returns_word_count) { + char *sentence = strdup("Birds of a feather"); + int word_count = split_words(sentence); + assert_that(word_count, is_equal_to(4)); + free(sentence); +} + +Ensure(Words, converts_spaces_to_zeroes) { + char *sentence = strdup("Birds of a feather"); + split_words(sentence); + int comparison = memcmp("Birds\0of\0a\0feather", sentence, strlen(sentence)); + assert_that(comparison, is_equal_to(0)); + free(sentence); +} + +TestSuite *words_tests() { + TestSuite *suite = create_test_suite(); + add_test_with_context(suite, Words, returns_word_count); + add_test_with_context(suite, Words, converts_spaces_to_zeroes); + return suite; +} diff --git a/tutorial_src/words_tests3.c b/tutorial_src/words_tests3.c new file mode 100644 index 00000000..dc5c9504 --- /dev/null +++ b/tutorial_src/words_tests3.c @@ -0,0 +1,44 @@ +#include +#include + +#include "words.h" +#include + +Describe(Words); +BeforeEach(Words) {} +AfterEach(Words) {} + +Ensure(Words, returns_word_count) { + char *sentence = strdup("Birds of a feather"); + int word_count = split_words(sentence); + assert_that(word_count, is_equal_to(4)); + free(sentence); +} + +Ensure(Words, converts_spaces_to_zeroes) { + char *sentence = strdup("Birds of a feather"); + split_words(sentence); + int comparison = memcmp("Birds\0of\0a\0feather", sentence, strlen(sentence)); + assert_that(comparison, is_equal_to(0)); + free(sentence); +} + + +void mocked_callback(const char *word, void *memo) { + mock(word, memo); +} + +Ensure(Words, invokes_callback_once_for_single_word_sentence) { + expect(mocked_callback, + when(word, is_equal_to_string("Word")), when(memo, is_null)); + words("Word", &mocked_callback, NULL); +} + +TestSuite *words_tests() { + TestSuite *suite = create_test_suite(); + add_test_with_context(suite, Words, returns_word_count); + add_test_with_context(suite, Words, converts_spaces_to_zeroes); + add_test_with_context(suite, Words, invokes_callback_once_for_single_word_sentence); + return suite; +} + diff --git a/tutorial_src/words_tests4.c b/tutorial_src/words_tests4.c new file mode 100644 index 00000000..5436cb20 --- /dev/null +++ b/tutorial_src/words_tests4.c @@ -0,0 +1,52 @@ +#include +#include + +#include "words.h" +#include + +Describe(Words); +BeforeEach(Words) {} +AfterEach(Words) {} + +Ensure(Words, returns_word_count) { + char *sentence = strdup("Birds of a feather"); + int word_count = split_words(sentence); + assert_that(word_count, is_equal_to(4)); + free(sentence); +} + +Ensure(Words, converts_spaces_to_zeroes) { + char *sentence = strdup("Birds of a feather"); + split_words(sentence); + int comparison = memcmp("Birds\0of\0a\0feather", sentence, strlen(sentence)); + assert_that(comparison, is_equal_to(0)); + free(sentence); +} + + +void mocked_callback(const char *word, void *memo) { + mock(word, memo); +} + +Ensure(Words, invokes_callback_once_for_single_word_sentence) { + expect(mocked_callback, + when(word, is_equal_to_string("Word")), when(memo, is_null)); + words("Word", &mocked_callback, NULL); +} + +Ensure(Words, invokes_callback_for_each_word_in_a_phrase) { + expect(mocked_callback, when(word, is_equal_to_string("Birds"))); + expect(mocked_callback, when(word, is_equal_to_string("of"))); + expect(mocked_callback, when(word, is_equal_to_string("a"))); + expect(mocked_callback, when(word, is_equal_to_string("feather"))); + words("Birds of a feather", &mocked_callback, NULL); +} + +TestSuite *words_tests() { + TestSuite *suite = create_test_suite(); + add_test_with_context(suite, Words, returns_word_count); + add_test_with_context(suite, Words, converts_spaces_to_zeroes); + add_test_with_context(suite, Words, invokes_callback_once_for_single_word_sentence); + add_test_with_context(suite, Words, invokes_callback_for_each_word_in_a_phrase); + return suite; +} diff --git a/tutorial_src/xml_reporter.c b/tutorial_src/xml_reporter.c new file mode 100644 index 00000000..01d6aff6 --- /dev/null +++ b/tutorial_src/xml_reporter.c @@ -0,0 +1,77 @@ +#include "xml_reporter.h" +#include "cgreen/reporter.h" +#include "cgreen/breadcrumb.h" + +#include + +static void xml_reporter_start_suite(TestReporter *reporter, const char *name, int count); +static void xml_reporter_start_test(TestReporter *reporter, const char *name); +static void xml_reporter_finish_test(TestReporter *reporter, const char *filename, int line); +static void xml_reporter_finish_suite(TestReporter *reporter, const char *filename, int line); +static void xml_show_fail(TestReporter *reporter, const char *file, int line, const char *message, va_list arguments); + +TestReporter *create_xml_reporter() { + TestReporter *reporter = create_reporter(); + reporter->start_suite = &xml_reporter_start_suite; + reporter->start_test = &xml_reporter_start_test; + reporter->show_fail = &xml_show_fail; + reporter->finish_test = &xml_reporter_finish_test; + reporter->finish_suite = &xml_reporter_finish_suite; + return reporter; +} + +static indent(TestReporter *reporter) { + int depth = get_breadcrumb_depth((CgreenBreadcrumb *)reporter->breadcrumb); + while (depth-- > 0) { + printf("\t"); + } +} + +static void xml_reporter_start_suite(TestReporter *reporter, const char *name, int count) { + if (get_breadcrumb_depth((CgreenBreadcrumb *)reporter->breadcrumb) == 0) { + printf("\n"); + } + indent(reporter); + printf("\n", name); + reporter_start(reporter, name); +} + +static void xml_reporter_start_test(TestReporter *reporter, const char *name) { + indent(reporter); + printf("\n", name); + reporter_start(reporter, name); +} + +static void xml_show_fail(TestReporter *reporter, const char *file, int line, const char *message, va_list arguments) { + indent(reporter); + printf("\n"); + indent(reporter); + printf("\t"); + vprintf(message, arguments); + printf("\n"); + indent(reporter); + printf("\t\n", file, line); + indent(reporter); + printf("\n"); +} + +static void xml_show_incomplete(TestReporter *reporter, const char *name) { + indent(reporter); + printf("\n"); + indent(reporter); + printf("\tFailed to complete]]>\n"); + indent(reporter); + printf("\n"); +} + +static void xml_reporter_finish_test(TestReporter *reporter, const char *filename, int line) { + reporter_finish_test(reporter, filename, line); + indent(reporter); + printf("\n"); +} + +static void xml_reporter_finish_suite(TestReporter *reporter, const char *filename, int line) { + reporter_finish_suite(reporter, filename, line); + indent(reporter); + printf("\n"); +} diff --git a/tutorial_src/xml_reporter.h b/tutorial_src/xml_reporter.h new file mode 100644 index 00000000..1eb59cae --- /dev/null +++ b/tutorial_src/xml_reporter.h @@ -0,0 +1,8 @@ +#ifndef _XML_REPORTER_HEADER_ +#define _XML_REPORTER_HEADER_ + +#include + +TestReporter *create_xml_reporter(); + +#endif diff --git a/tutorial_src/xml_reporter0.c b/tutorial_src/xml_reporter0.c new file mode 100644 index 00000000..71d54129 --- /dev/null +++ b/tutorial_src/xml_reporter0.c @@ -0,0 +1,8 @@ +#include + +#include "xml_reporter.h" + +TestReporter *create_xml_reporter() { + TestReporter *reporter = create_reporter(); + return reporter; +} diff --git a/tutorial_src/xml_reporter1.c b/tutorial_src/xml_reporter1.c new file mode 100644 index 00000000..72e21061 --- /dev/null +++ b/tutorial_src/xml_reporter1.c @@ -0,0 +1,35 @@ +#include +#include + +#include +#include "xml_reporter.h" + + +static void xml_reporter_start_suite(TestReporter *reporter, const char *name, int count) { + printf("\n", name); + reporter_start_suite(reporter, name, count); +} + +static void xml_reporter_start_test(TestReporter *reporter, const char *name) { + printf("\n", name); + reporter_start_test(reporter, name); +} + +static void xml_reporter_finish_test(TestReporter *reporter, const char *filename, int line, const char *message) { + reporter_finish_test(reporter, filename, line, message); + printf("\n"); +} + +static void xml_reporter_finish_suite(TestReporter *reporter, const char *filename, int line) { + reporter_finish_suite(reporter, filename, line); + printf("\n"); +} + +TestReporter *create_xml_reporter() { + TestReporter *reporter = create_reporter(); + reporter->start_suite = &xml_reporter_start_suite; + reporter->start_test = &xml_reporter_start_test; + reporter->finish_test = &xml_reporter_finish_test; + reporter->finish_suite = &xml_reporter_finish_suite; + return reporter; +} diff --git a/tutorial_src/xml_reporter2.c b/tutorial_src/xml_reporter2.c new file mode 100644 index 00000000..2084605c --- /dev/null +++ b/tutorial_src/xml_reporter2.c @@ -0,0 +1,46 @@ +#include +#include + +#include +#include +#include "xml_reporter.h" + + +static void xml_reporter_start_suite(TestReporter *reporter, const char *name, int count) { + printf("\n", name); + reporter_start_suite(reporter, name, count); +} + +static void xml_reporter_start_test(TestReporter *reporter, const char *name) { + printf("\n", name); + reporter_start_test(reporter, name); +} + +static void xml_show_fail(TestReporter *reporter, const char *file, int line, const char *message, va_list arguments) { + printf("\n"); + printf("\t"); + vprintf(message, arguments); + printf("\n"); + printf("\t\n", file, line); + printf("\n"); +} + +static void xml_reporter_finish_test(TestReporter *reporter, const char *filename, int line, const char *message) { + reporter_finish_test(reporter, filename, line, message); + printf("\n"); +} + +static void xml_reporter_finish_suite(TestReporter *reporter, const char *filename, int line) { + reporter_finish_suite(reporter, filename, line); + printf("\n"); +} + +TestReporter *create_xml_reporter() { + TestReporter *reporter = create_reporter(); + reporter->start_suite = &xml_reporter_start_suite; + reporter->start_test = &xml_reporter_start_test; + reporter->show_fail = &xml_show_fail; + reporter->finish_test = &xml_reporter_finish_test; + reporter->finish_suite = &xml_reporter_finish_suite; + return reporter; +} diff --git a/tutorial_src/xml_reporter3.c b/tutorial_src/xml_reporter3.c new file mode 100644 index 00000000..b175ba60 --- /dev/null +++ b/tutorial_src/xml_reporter3.c @@ -0,0 +1,53 @@ +#include +#include + +#include +#include "xml_reporter.h" + + +static void xml_reporter_start_suite(TestReporter *reporter, const char *name, int count) { + printf("\n", name); + reporter_start_suite(reporter, name, count); +} + +static void xml_reporter_start_test(TestReporter *reporter, const char *name) { + printf("\n", name); + reporter_start_test(reporter, name); +} + +static void xml_show_fail(TestReporter *reporter, const char *file, int line, const char *message, va_list arguments) { + printf("\n"); + printf("\t"); + vprintf(message, arguments); + printf("\n"); + printf("\t\n", file, line); + printf("\n"); +} + +static void xml_show_incomplete(TestReporter *reporter, const char *file, int line, const char *message, va_list arguments) { + printf("\n"); + printf("\tFailed to complete\n"); + printf("\n"); +} + + +static void xml_reporter_finish_test(TestReporter *reporter, const char *filename, int line, const char *message) { + reporter_finish_test(reporter, filename, line, message); + printf("\n"); +} + +static void xml_reporter_finish_suite(TestReporter *reporter, const char *filename, int line) { + reporter_finish_suite(reporter, filename, line); + printf("\n"); +} + +TestReporter *create_xml_reporter() { + TestReporter *reporter = create_reporter(); + reporter->start_suite = &xml_reporter_start_suite; + reporter->start_test = &xml_reporter_start_test; + reporter->show_fail = &xml_show_fail; + reporter->show_incomplete = &xml_show_incomplete; + reporter->finish_test = &xml_reporter_finish_test; + reporter->finish_suite = &xml_reporter_finish_suite; + return reporter; +} diff --git a/tutorial_src/xml_reporter4.c b/tutorial_src/xml_reporter4.c new file mode 100644 index 00000000..440597b4 --- /dev/null +++ b/tutorial_src/xml_reporter4.c @@ -0,0 +1,73 @@ +#include +#include + +#include +#include "xml_reporter.h" + +static void indent(TestReporter *reporter) { + int depth = get_breadcrumb_depth((CgreenBreadcrumb *)reporter->breadcrumb); + while (depth-- > 0) { + printf("\t"); + } +} + +static void xml_reporter_start_suite(TestReporter *reporter, const char *name, int count) { + if (get_breadcrumb_depth((CgreenBreadcrumb *)reporter->breadcrumb) == 0) { + printf("\n"); + } + indent(reporter); + printf("\n", name); + reporter_start_suite(reporter, name, count); +} + +static void xml_reporter_start_test(TestReporter *reporter, const char *name) { + indent(reporter); + printf("\n", name); + reporter_start_test(reporter, name); +} + +static void xml_show_fail(TestReporter *reporter, const char *file, int line, const char *message, va_list arguments) { + indent(reporter); + printf("\n"); + indent(reporter); + printf("\t"); + vprintf(message, arguments); + printf("\n"); + indent(reporter); + printf("\t\n", file, line); + indent(reporter); + printf("\n"); +} + +static void xml_show_incomplete(TestReporter *reporter, const char *file, int line, const char *message, va_list arguments) { + indent(reporter); + printf("\n"); + indent(reporter); + printf("\tFailed to complete\n"); + indent(reporter); + printf("\n"); +} + + +static void xml_reporter_finish_test(TestReporter *reporter, const char *filename, int line, const char *message) { + reporter_finish_test(reporter, filename, line, message); + indent(reporter); + printf("\n"); +} + +static void xml_reporter_finish_suite(TestReporter *reporter, const char *filename, int line) { + reporter_finish_suite(reporter, filename, line); + indent(reporter); + printf("\n"); +} + +TestReporter *create_xml_reporter() { + TestReporter *reporter = create_reporter(); + reporter->start_suite = &xml_reporter_start_suite; + reporter->start_test = &xml_reporter_start_test; + reporter->show_fail = &xml_show_fail; + reporter->show_incomplete = &xml_show_incomplete; + reporter->finish_test = &xml_reporter_finish_test; + reporter->finish_suite = &xml_reporter_finish_suite; + return reporter; +}