/usr/share/gtk-doc/html/cutter/tutorial.html is in cutter-testing-framework-doc 1.1.7-1.1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 | <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Tutorial</title>
<meta name="generator" content="DocBook XSL Stylesheets V1.75.2">
<link rel="home" href="index.html" title="Cutter Reference Manual">
<link rel="up" href="start.html" title="Getting start">
<link rel="prev" href="start.html" title="Getting start">
<link rel="next" href="reference.html" title="Part II. Reference">
<meta name="generator" content="GTK-Doc V1.15 (XML mode)">
<link rel="stylesheet" href="style.css" type="text/css">
</head>
<body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF">
<table class="navigation" id="top" width="100%" summary="Navigation header" cellpadding="2" cellspacing="2"><tr valign="middle">
<td><a accesskey="p" href="start.html"><img src="left.png" width="24" height="24" border="0" alt="Prev"></a></td>
<td><a accesskey="u" href="start.html"><img src="up.png" width="24" height="24" border="0" alt="Up"></a></td>
<td><a accesskey="h" href="index.html"><img src="home.png" width="24" height="24" border="0" alt="Home"></a></td>
<th width="100%" align="center">Cutter Reference Manual</th>
<td><a accesskey="n" href="reference.html"><img src="right.png" width="24" height="24" border="0" alt="Next"></a></td>
</tr></table>
<div class="refentry" title="Tutorial">
<a name="tutorial"></a><div class="titlepage"></div>
<div class="refnamediv"><table width="100%"><tr>
<td valign="top">
<h2><span class="refentrytitle"><a name="tutorial.top_of_page"></a>Tutorial</span></h2>
<p>Tutorial — How to use Cutter</p>
</td>
<td valign="top" align="right"></td>
</tr></table></div>
<div class="refsect1" title="Introduction">
<a name="id391111"></a><h2>Introduction</h2>
<p>We write a program (library) that implements a stack in C. We write a program with writing tests. To write tests, we use Cutter that is a unit testing framework for C.</p>
<p>We use GNU build system (GNU Autoconf/GNU Automake/GNU Libtool) for build system. GNU build system lessens disparities on build environment. For this reason, we can build our program and tests on several environment easily.</p>
<p>It's better that a program works on several environment without many costs. If tests of the program works on the environment too, we can verify the program works well on the environment easily. It's important that both a program and tests are works well on several environment easily.</p>
<p>Cutter requires only GLib. GLib is a very portable library that works on not only UNIX-like system but also Windows and Mac OS X. Cutter provides many useful test support features with portability due to GLib. Cutter is a testing framework and respects to xUnit style.</p>
<p>We will learn how to use Cutter with writing a stack implementation. We assume that Cutter is already installed into your system.</p>
<p>There are source codes of this program in sample/stack/.</p>
</div>
<div class="refsect1" title="Directory hierarchy">
<a name="id396085"></a><h2>Directory hierarchy</h2>
<p>First, we need to setup a directory for our stack program. We use 'stack' as the directory name.</p>
<pre class="programlisting">% mkdir -p /tmp/stack
% cd /tmp/stack</pre>
<p>Next, we make some directories: config/ that is for build auxiliary files, src/ that is for our source files and test/ that is for tests.</p>
<pre class="programlisting">[stack]% mkdir config src test</pre>
<p>After the above, we get the following directory hierarchy:</p>
<pre class="programlisting">stack/ -+- config/ for build auxiliary files
|
+- src/ for source files
|
+- test/ for tests</pre>
</div>
<div class="refsect1" title="Use GNU build system">
<a name="id419168"></a><h2>Use GNU build system</h2>
<p>In GNU build system start-up, some commands are ran and they generates some files automatically. They usually are run from an authgen.sh shell script. We follow the convention.</p>
<p>autogen.sh:</p>
<pre class="programlisting">#!/bin/sh
run()
{
$@
if test $? -ne 0; then
echo "Failed $@"
exit 1
fi
}
run aclocal ${ACLOCAL_ARGS}
run libtoolize --copy --force
run autoheader
run automake --add-missing --foreign --copy
run autoconf</pre>
<p>Don't forget to make the autogen.sh executable.</p>
<pre class="programlisting">[stack]% chmod +x autogen.sh</pre>
<p>run() is a convenience function to confirm a result of ran command. The following list shows what is done by them:</p>
<div class="itemizedlist"><ul class="itemizedlist" type="disc">
<li class="listitem"><p>aclocal: collects macros that is used by Automake into aclocal.m4.</p></li>
<li class="listitem"><p>libtoolize: prepares files that is needed by libtool.</p></li>
<li class="listitem"><p>autoheader: generates config.h.in that is used by configure script.</p></li>
<li class="listitem"><p>automake: generates Makefile.in that is used by configure script.</p></li>
<li class="listitem"><p>autoconf: generates configure scripts.</p></li>
</ul></div>
<p>If we installed Cutter into different prefix with aclocal's install prefix, you need to set ACLOCAL_ARGS environment variable. The environment variable is referred from autogen.sh. If we installed Cutter with $HOME/local prefix, here is an example command to set the environment variable:</p>
<pre class="programlisting">[stack]% export ACLOCAL_ARGS="-I $HOME/local/share/aclocal"</pre>
<p>The following is a result of autogen.sh at this point:</p>
<pre class="programlisting">[stack]% ./autogen.sh
aclocal: `configure.ac' or `configure.in' is required
Failed aclocal</pre>
<p>We need to prepare configure.ac that is for Autoconf.</p>
<div class="refsect2" title="configure.ac">
<a name="id435712"></a><h3>configure.ac</h3>
<p>The following is a minimum configure.ac for our autogen.sh.</p>
<p>configure.ac:</p>
<pre class="programlisting">AC_PREREQ(2.59)
AC_INIT(stack, 0.0.1, you@example.com)
AC_CONFIG_AUX_DIR([config])
AC_CONFIG_HEADER([src/config.h])
AM_INIT_AUTOMAKE($PACKAGE_NAME, $PACKAGE_VERSION)
AC_PROG_LIBTOOL
AC_CONFIG_FILES([Makefile])
AC_OUTPUT</pre>
<p>The following is a result of autogen after preparing configure.ac.</p>
<pre class="programlisting">[stack]% ./autogen.sh
Putting files in AC_CONFIG_AUX_DIR, `config'.
configure.ac:7: installing `config/install-sh'
configure.ac:7: installing `config/missing'
automake: no `Makefile.am' found for any configure output
Failed automake --add-missing --foreign --copy</pre>
<p>We need to prepare Makefile.am for Automake.</p>
</div>
<hr>
<div class="refsect2" title="Makefile.am">
<a name="id435750"></a><h3>Makefile.am</h3>
<p>An empty Makefile.am is enough if the Makefile.am is just only for autogen.sh.</p>
<pre class="programlisting">[stack]% touch Makefile.am
[stack]% ./autogen.sh
Putting files in AC_CONFIG_AUX_DIR, `config'.</pre>
<p>A configure script can be generated. We can do 'configure; make; make install' like many popular softwares at this point:</p>
<pre class="programlisting">[stack]% ./configure
...
[stack]% make
[stack]% make install</pre>
<p>But for now, nothing is to happen because we doesn't have any items that are needed to build or install.</p>
</div>
</div>
<div class="refsect1" title="First test writing">
<a name="id435785"></a><h2>First test writing</h2>
<p>We can write a test because we got a minimal build environment. First, we test that a newly created statck should be empty. The following code representes this test in C:</p>
<pre class="programlisting">void
test_new_stack (void)
{
Stack *stack;
stack = stack_new();
if (stack_is_empty(stack))
PASS;
else
FAIL;
}</pre>
<p>We change this test code to be able to run as a test code for Cutter.</p>
<div class="refsect2" title="Write a test program">
<a name="id435807"></a><h3>Write a test program</h3>
<p>A test program is put into test/. In this tutorial, we make a test program as test/test-stack.c.</p>
<p>First, we need to include cutter.h to use Cutter.</p>
<p>test/test-stack.c:</p>
<pre class="programlisting">#include <cutter.h></pre>
<p>And we need to include stack.h that declares API for test target stack implementation. (stack.h will be made later.)</p>
<p>test/test-stack.c:</p>
<pre class="programlisting">#include <stack.h></pre>
<p>Next, we write a test with the stack API:</p>
<p>test/test-stack.c:</p>
<pre class="programlisting">void test_new_stack (void);
void
test_new_stack (void)
{
Stack *stack;
stack = stack_new();
cut_assert(stack_is_empty(stack));
}</pre>
<p>cut_assert() is a macro that fails if the first argument is 0, passes otherwise. Writing tests with Cutter means that writing a program that verifies a target program works as we expected at the specific situation.</p>
<p>The following test code is a whole test code to test "a newly created stack should be empty".</p>
<p>test/test-stack.c:</p>
<pre class="programlisting">#include <cutter.h>
#include <stack.h>
void test_new_stack (void);
void
test_new_stack (void)
{
Stack *stack;
stack = stack_new();
cut_assert(stack_is_empty(stack));
}</pre>
</div>
<hr>
<div class="refsect2" title="Build a test">
<a name="id402693"></a><h3>Build a test</h3>
<p>Each test programs for Cutter are shared libraries. To build the above test program as shared library, we change Makefile.am.</p>
<div class="refsect3" title="Build configuration in test/">
<a name="id402704"></a><h4>Build configuration in test/</h4>
<p>Makefile.am is empty for now.</p>
<p>First, put the following configuration to use ACLOCAL_ARGS environment variable for autogen.sh with aclocal invoked via make:</p>
<p>Makefile.am:</p>
<pre class="programlisting">ACLOCAL_AMFLAGS = $$ACLOCAL_ARGS</pre>
<p>Next, to build test/test-stack.c in test/ directory, we need to specify that there is test/ directory as sub directory in Makefile.am.</p>
<p>Makefile.am:</p>
<pre class="programlisting">...
SUBDIRS = test</pre>
<p>make will detect Makefile.am is changed and update Makefile and so on automatically after we change Makefile.am and run make.</p>
<pre class="programlisting">[stack]% make
cd . && /bin/sh /tmp/stack/config/missing --run automake-1.10 --foreign Makefile
cd . && /bin/sh ./config.status Makefile
config.status: creating Makefile
Making all in test
config.status: creating Makefile
Making all in test
make[1]: Entering directory `/tmp/stack/test'
make[1]: *** No rule to make target `all'. Stop.
make[1]: Leaving directory `/tmp/stack/test'
make: *** [all-recursive] Error 1</pre>
<p>We find that make go down to test/ to build. But make is failed in test/ because test/Makefile doesn't exist.</p>
<p>To build in test/, we will make test/Makefile.am and indicate configure.ac to generate test/Makefile.</p>
<p>An empty test/Makefile.am is OK for just protecting make failure in test/.</p>
<pre class="programlisting">[stack]% touch test/Makefile.am</pre>
<p>Next, we indicate configure.ac to generate test/Makefile. Now, make will be done successfully.</p>
<p>configure.ac:</p>
<pre class="programlisting">...
AC_CONFIG_FILES([Makefile
test/Makefile])
...</pre>
<p>If we run make again, make re-runs configure and test/Makefile is generated. Now make doesn't fail in test/.</p>
<pre class="programlisting">[stack]% make
...
config.status: creating test/Makefile
config.status: creating src/config.h
config.status: src/config.h is unchanged
config.status: executing depfiles commands
Making all in test
make[1]: Entering directory `/tmp/stack/test'
make[1]: Nothing to be done for `all'.
make[1]: Leaving directory `/tmp/stack/test'
make[1]: Entering directory `/tmp/stack'
make[1]: Nothing to be done for `all-am'.
make[1]: Leaving directory `/tmp/stack'</pre>
</div>
<div class="refsect3" title="Build test/test_stack.so">
<a name="id426773"></a><h4>Build test/test_stack.so</h4>
<p>We will edit test/Makefile.am to build test/test-stack.c as a shared library. A shared library for test should be named as "test_" prefix. (It's OK if "lib" is prepended to "test_" prefix.) We use "noinst_" because a test program isn't needed to be installed.</p>
<p>test/Makefile.am:</p>
<pre class="programlisting">noinst_LTLIBRARIES = test_stack.la</pre>
<p>Shared libraries for test are loaded dynamically by cutter that is a command included in Cutter to run test. Shared libraries that are loaded dynamically should be builded libtool with -module option. -rpath option is also required by -module option. Because of them LDFLAGS becomes the following. The reason why -avoid-version is specified is that shared libraries for test aren't needed to have version number. -no-undefined option tells libtool that it reports a error when there is any undefined symbol. On some environments, shared library isn't generated without -no-undefined option. (e.g. a case that generating DLL on Windows.)</p>
<p>test/Makefile.am:</p>
<pre class="programlisting">...
LDFLAGS = -module -rpath $(libdir) -avoid-version -no-undefined</pre>
<p>To build test/test_stack.la, test/test-stack.c is used. (test_stack.so is generated into test/.libs/.) We need to specify this.</p>
<p>test/Makefile.am:</p>
<pre class="programlisting">...
test_stack_la_SOURCES = test-stack.c</pre>
<p>Now, we can build test/test_stack.la.</p>
<pre class="programlisting">[stack]% make
...
cd .. && /bin/sh /tmp/stack/config/missing --run automake-1.10 --foreign test/Makefile
test/Makefile.am: required file `config/depcomp' not found
test/Makefile.am: `automake --add-missing' can install `depcomp'
make[1]: *** [Makefile.in] Error 1
make[1]: Leaving directory `/tmp/stack/test'
make: *** [all-recursive] Error 1</pre>
<p>To generate config/depcomp, we need to run automake with --add-missing option. To do this, we can use autogen.sh. Don't forget to re-run configure.</p>
<pre class="programlisting">[stack]% ./autogen.sh
[stack]% ./configure</pre>
<p>Now, we can build test/test_stack.la with make.</p>
<pre class="programlisting">[stack]% make
...
test-stack.c:1:20: error: cutter.h: No such file or directory
test-stack.c:2:19: error: stack.h: No such file or directory
test-stack.c: In function 'test_new_stack':
test-stack.c:9: error: 'Stack' undeclared (first use in this function)
test-stack.c:9: error: (Each undeclared identifier is reported only once
test-stack.c:9: error: for each function it appears in.)
test-stack.c:9: error: 'stack' undeclared (first use in this function)
make[1]: *** [test-stack.lo] Error 1
make[1]: Leaving directory `/tmp/stack/test'
make: *** [all-recursive] Error 1</pre>
<p>But there are the above errors because we don't setup to use Cutter yet. And we can't include stack.h because we don't have a stack implementation yet.</p>
</div>
<div class="refsect3" title="Use Cutter">
<a name="id426871"></a><h4>Use Cutter</h4>
<p>We will support cutter.h including. Cutter provides a macro file for aclocal. Because of this, we can use Cutter with GNU build system.</p>
<p>First, we add a code to detect Cutter into configure.ac.</p>
<p>configure.ac:</p>
<pre class="programlisting">...
AC_CHECK_CUTTER
AC_CONFIG_FILES([Makefile
test/Makefile])
...</pre>
<p>We use detected Cutter information in test/Makefile.am:</p>
<p>test/Makefile.am:</p>
<pre class="programlisting">...
INCLUDES = $(CUTTER_CFLAGS)
LIBS = $(CUTTER_LIBS)
...</pre>
<p>The followings are the current whole configure.ac, Makefile.am and test/Makefile.am:</p>
<p>configure.ac:</p>
<pre class="programlisting">AC_PREREQ(2.59)
AC_INIT(stack, 0.0.1, you@example.com)
AC_CONFIG_AUX_DIR([config])
AC_CONFIG_HEADER([src/config.h])
AM_INIT_AUTOMAKE($PACKAGE_NAME, $PACKAGE_VERSION)
AC_PROG_LIBTOOL
AC_CHECK_CUTTER
AC_CONFIG_FILES([Makefile
test/Makefile])
AC_OUTPUT</pre>
<p>Makefile.am:</p>
<pre class="programlisting">ACLOCAL_AMFLAGS = $$ACLOCAL_ARGS
SUBDIRS = test</pre>
<p>test/Makefile.am:</p>
<pre class="programlisting">noinst_LTLIBRARIES = test_stack.la
INCLUDES = $(CUTTER_CFLAGS)
LIBS = $(CUTTER_LIBS)
LDFLAGS = -module -rpath $(libdir) -avoid-version -no-undefined
test_stack_la_SOURCES = test-stack.c</pre>
<p>AC_CHECK_CUTTER macro uses pkg-config which is a popular package information management tool. If we installed Cutter with different prefix of pkg-config, we need to set PKG_CONFIG_PATH environment variable. The environment variable is referred by pkg-config to find .pc file. If we installed Cutter with $HOME/local prefix, here is an example command to set the environment variable:</p>
<pre class="programlisting">[stack]% export PKG_CONFIG_PATH=$HOME/local/lib/pkgconfig</pre>
<p>We run make again and make runs configure automatically and builds with Cutter configuration after the above changes.</p>
<pre class="programlisting">[stack]% make
...
test-stack.c:2:19: error: stack.h: No such file or directory
test-stack.c: In function 'test_new_stack':
test-stack.c:9: error: 'Stack' undeclared (first use in this function)
test-stack.c:9: error: (Each undeclared identifier is reported only once
test-stack.c:9: error: for each function it appears in.)
test-stack.c:9: error: 'stack' undeclared (first use in this function)
make[1]: *** [test-stack.lo] Error 1
make[1]: Leaving directory `/tmp/stack/test'
make: *** [all-recursive] Error 1</pre>
<p>An error that reports "cutter.h can't be included" is gone away.</p>
</div>
<div class="refsect3" title="Make stack API">
<a name="id426978"></a><h4>Make stack API</h4>
<p>We will fix an error that stack.h can't be included.</p>
<p>We put stack.h into src/stack.h because we make a stack implementation in src/.</p>
<pre class="programlisting">[stack]% touch src/stack.h</pre>
<p>To include stack.h from test program, we configure include path:</p>
<p>test/Makefile.am:</p>
<pre class="programlisting">...
INCLUDES = $(CUTTER_CFLAGS) -I$(top_srcdir)/src
...</pre>
<p>We will find that an error that stack.h can't be included is gone away if we run make again.</p>
<pre class="programlisting">[stack]% make
...
test-stack.c: In function 'test_new_stack':
test-stack.c:9: error: 'Stack' undeclared (first use in this function)
test-stack.c:9: error: (Each undeclared identifier is reported only once
test-stack.c:9: error: for each function it appears in.)
test-stack.c:9: error: 'stack' undeclared (first use in this function)
make[1]: *** [test-stack.lo] Error 1
make[1]: Leaving directory `/tmp/stack/test'
make: *** [all-recursive] Error 1</pre>
<p>There is only an error that Stack type isn't declared.</p>
</div>
<div class="refsect3" title="Declare Stack type">
<a name="id427030"></a><h4>Declare Stack type</h4>
<p>To build our test program, we declare Stack type in src/stack.h.</p>
<p>src/stack.h:</p>
<pre class="programlisting">#ifndef __STACK_H__
#define __STACK_H__
typedef struct _Stack Stack;
#endif</pre>
<p>We get a warning because stack_new() isn't declared but we can build a shared library.</p>
<pre class="programlisting">[stack]% make
...
test-stack.c: In function 'test_new_stack':
test-stack.c:10: warning: assignment makes pointer from integer without a cast
...
[stack]% file test/.libs/test_stack.so
test/.libs/test_stack.so: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, not stripped</pre>
<p>NOTE: We can't generate a shared library (DLL) on Cygwin when we have unresolved symbols. We can go to the next step on Cygwin without caring the command result.</p>
</div>
<div class="refsect3" title="Declare stack_new()/stack_is_empty()">
<a name="id427068"></a><h4>Declare stack_new()/stack_is_empty()</h4>
<p>To suppress a warning, we declare stack_new() and stack_is_empty().</p>
<p>src/stack.h:</p>
<pre class="programlisting">...
Stack *stack_new (void);
int stack_is_empty (Stack *stack);
...</pre>
<p>We can confirm that make don't report any warnings now.</p>
<pre class="programlisting">[stack]% make</pre>
</div>
</div>
<hr>
<div class="refsect2" title="Run test">
<a name="id427100"></a><h3>Run test</h3>
<p>Now, we can run a test because we got a shared library.</p>
<pre class="programlisting">[stack]% cutter test/
cutter: symbol lookup error: test/.libs/test_stack.so: undefined symbol: stack_new</pre>
<p>Loading our test is failed due to undefined stack_new() but we can confirm that our test is loaded.</p>
<p>NOTE: We get a "0 tests are ran and no failure" result report on Cygwin because we can't generate a DLL on Cygwin when we have unresolved symbols. We will implement stack and resolve all symbols. We can generate a DLL and run test after implementing stack. We can go to the next step on Cygwin without caring the command result.</p>
<div class="refsect3" title="Automate running test">
<a name="id427127"></a><h4>Automate running test</h4>
<p>GNU build system use 'make check' to run test. We follow the convention in our stack implementation.</p>
<p>First, we make a script test/run-test.sh that runs our test. A path of cutter command is passed from environment variable CUTTER.</p>
<p>test/run-test.sh:</p>
<pre class="programlisting">#!/bin/sh
export BASE_DIR="`dirname $0`"
$CUTTER -s $BASE_DIR "$@" $BASE_DIR</pre>
<p>Don't forget to make the test/run-test.sh executable.</p>
<pre class="programlisting">[stack]% chmod +x test/run-test.sh</pre>
<p>We need to specify that we use test/run-test.sh as a test runner script to test/Makefile.am.</p>
<p>test/Makefile.am:</p>
<pre class="programlisting">TESTS = run-test.sh
TESTS_ENVIRONMENT = CUTTER="$(CUTTER)"
...</pre>
<p>We pass a path of cutter command via environment variable CUTTER in TESTS_ENVIRONMENT. A path of cutter command is detected by AC_CHECK_CUTTER in configure.ac.</p>
<p>We can confirm that 'make -s check' runs our test. -s option is for silence mode. A test result can be confirmed more easier.</p>
<pre class="programlisting">[stack]% make -s check
Making check in test
cutter: symbol lookup error: ./.libs/test_stack.so: undefined symbol: stack_new
FAIL: run-test.sh
================================
1 of 1 tests failed
Please report to you@example.com
================================
...</pre>
<p>NOTE: As mentioned the above, we doesn't get an error on Cygwin because we can't generate a DLL for now. We doesn't need to care it. We can go to the next.</p>
</div>
<div class="refsect3" title="Make test/run-test.sh workable alone">
<a name="id427200"></a><h4>Make test/run-test.sh workable alone</h4>
<p>In 'make -s check', there are outputs that isn't test result like build logs. They hid test result that is interested by us. So we want test/run-test.sh to work without invoking from 'make -s check'.</p>
<p>test/run-test.sh needs to detect a path of cutter command automatically if environment variable CUTTER isn't set. And test/run-test.sh needs to run make to rebuild necessary files if test/run-test.sh isn't invoked from 'make check'.</p>
<p>test/run-test.sh:</p>
<pre class="programlisting">#!/bin/sh
export BASE_DIR="`dirname $0`"
top_dir="$BASE_DIR/.."
if test -z "$NO_MAKE"; then
make -C $top_dir > /dev/null || exit 1
fi
if test -z "$CUTTER"; then
CUTTER="`make -s -C $BASE_DIR echo-cutter`"
fi
$CUTTER -s $BASE_DIR "$@" $BASE_DIR</pre>
<p>To support the test/run-test.sh, test/Makefile.am has some works.</p>
<p>test/Makefile.am:</p>
<pre class="programlisting">...
TESTS_ENVIRONMENT = NO_MAKE=yes CUTTER="$(CUTTER)"
...
echo-cutter:
@echo $(CUTTER)</pre>
<p>The following is the whole of test/Makefile.am.</p>
<p>test/Makefile.am:</p>
<pre class="programlisting">TESTS = run-test.sh
TESTS_ENVIRONMENT = NO_MAKE=yes CUTTER="$(CUTTER)"
noinst_LTLIBRARIES = test_stack.la
INCLUDES = $(CUTTER_CFLAGS) -I$(top_srcdir)/src
LIBS = $(CUTTER_LIBS)
LDFLAGS = -module -rpath $(libdir) -avoid-version -no-undefined
test_stack_la_SOURCES = test-stack.c
echo-cutter:
@echo $(CUTTER)</pre>
<p>We can confirm that test/run-test.sh runs test even if it's not invoked from 'make -s check'.</p>
<pre class="programlisting">[stack]% test/run-test.sh
cutter: symbol lookup error: test/.libs/test_stack.so: undefined symbol: stack_new</pre>
<p>NOTE: We doesn't get the error on Cygwin.</p>
<p>We will use test/run-test.sh instead of 'make -s check' from now. Test result that is what we are interested in will not be hid because test/run-test.sh just outputs build errors and/or warnings and test result.</p>
<p>We spent some times to build testing environment before we implement stack. It reduces costs to run test. If costs to run test isn't low, we will not run test gradually. It may cause quality loss.</p>
<p>Building testing environment at first delays start time of implementing a main program. But we need to keep quality of a main program by running test until a main program is developed and maintained. We will be able to collect costs that is spent for building testing environment. It's important that building testing environment at first to be developing a high-quality program comfortably.</p>
</div>
</div>
<hr>
<div class="refsect2" title="Implement stack">
<a name="id427294"></a><h3>Implement stack</h3>
<p>We will start implementing stack because we built testing environment.</p>
</div>
<div class="refsect3" title="A straightforward stack_new() implementation">
<a name="id427305"></a><h4>A straightforward stack_new() implementation</h4>
<p>We will define stack_new() and resolve run-time error.</p>
<p>We implement stack in src/stack.c. It's a straightforward stack_new() implementation:</p>
<p>src/stack.c:</p>
<pre class="programlisting">#include <stdlib.h>
#include "stack.h"
Stack *
stack_new (void)
{
return NULL;
}</pre>
</div>
<div class="refsect3" title="Build src/libstack.la">
<a name="id427329"></a><h4>Build src/libstack.la</h4>
<p>We will build src/stack.c with make. src/ should be included into build targets like test/.</p>
<p>Makefile.am:</p>
<pre class="programlisting">ACLOCAL_AMFLAGS = $$ACLOCAL_ARGS
SUBDIRS = src test</pre>
<p>configure.ac:</p>
<pre class="programlisting">...
AC_CONFIG_FILES([Makefile
src/Makefile
test/Makefile])
...</pre>
<p>The above configurations are for what we want to do.</p>
<pre class="programlisting">[stack]% test/run-test.sh
configure.ac:19: required file `src/Makefile.in' not found
make: *** [Makefile.in] Error 1</pre>
<p>To resolve the above error, we need to make src/Makefile.am.</p>
<pre class="programlisting">[stack]% touch src/Makefile.am
[stack]% test/run-test.sh
cutter: symbol lookup error: test/.libs/test_stack.so: undefined symbol: stack_new</pre>
<p>NOTE: We doesn't get the error on Cygwin.</p>
<p>make doesn't report error but we still have an error that stack_new() is undefined. Because we don't build src/stack.c and test program also doesn't link libstack.so yet.</p>
<p>The following configurations in src/Makefile.am are for build libstack.so from src/stack.c.</p>
<p>src/Makefile.am:</p>
<pre class="programlisting">lib_LTLIBRARIES = libstack.la
LDFLAGS = -no-undefined
libstack_la_SOURCES = stack.c</pre>
<p>make will generate libstack.so.</p>
<pre class="programlisting">[stack]% make
...
make[1]: Entering directory `/tmp/stack/src'
Makefile:275: .deps/stack.Plo: No such file or directory
make[1]: *** No rule to make target `.deps/stack.Plo'. Stop.
...</pre>
<p>To resolve the above error, we need to re-run configure.</p>
<pre class="programlisting">[stack]% ./configure</pre>
<p>make will generate src/.libs/libstack.so.0.0.0 now.</p>
<pre class="programlisting">[stack]% make
[stack]% file src/.libs/libstack.so.0.0.0
src/.libs/libstack.so.0.0.0: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, not stripped</pre>
<p>NOTE: We will generate src/.libs/cyglibstack.dll on Cygwin.</p>
</div>
<div class="refsect3" title="Link src/libstack.la">
<a name="id427442"></a><h4>Link src/libstack.la</h4>
<p>libstack.so is generated but it's not linked into test program. So there is still run-time error.</p>
<pre class="programlisting">[stack]% test/run-test.sh
cutter: symbol lookup error: test/.libs/test_stack.so: undefined symbol: stack_new</pre>
<p>NOTE: We doesn't get the error on Cygwin.</p>
<p>To link libstack.so, we will change test/Makefile.am like the following.</p>
<p>test/Makefile.am:</p>
<pre class="programlisting">...
LIBS = $(CUTTER_LIBS) $(top_builddir)/src/libstack.la
...</pre>
<p>We need to add src/.libs/ to PATH environment variable before run cutter to find DLL generated under src/.libs/ on Cygwin:</p>
<p>test/run-test.sh:</p>
<pre class="programlisting">...
case `uname` in
CYGWIN*)
PATH="$top_dir/src/.libs:$PATH"
;;
Darwin)
DYLD_LIBRARY_PATH="$top_dir/src/.libs:$DYLD_LIBRARY_PATH"
export DYLD_LIBRARY_PATH
;;
*BSD)
LD_LIBRARY_PATH="$top_dir/src.libs:$LD_LIBRARY_PATH"
export LD_LIBRARY_PATH
;;
*)
:
;;
esac
$CUTTER -s $BASE_DIR "$@" $BASE_DIR</pre>
<p>We need to run 'make clean' to re-link our test program.</p>
<pre class="programlisting">[stack]% make clean
[stack]% make
[stack]% test/run-test.sh
cutter: symbol lookup error: test/.libs/test_stack.so: undefined symbol: stack_is_empty</pre>
<p>An error message is changed to stack_is_empty() isn't found from stack_new() isn't found. We can confirm that libstack.so is linked correctly by this change.</p>
<p>NOTE: We doesn't get the error on Cygwin.</p>
</div>
<div class="refsect3" title="Implement stack_is_empty()">
<a name="id427515"></a><h4>Implement stack_is_empty()</h4>
<p>We test a result of stack_is_empty() in our test program:</p>
<p>test/test-stack.c:</p>
<pre class="programlisting">...
cut_assert(stack_is_empty(stack));
...</pre>
<p>That means that stack_is_empty() should return true. So stack_is_empty() implementation in src/stack.c should return true.</p>
<p>src/stack.c:</p>
<pre class="programlisting">...
#define TRUE 1
#define FALSE 0
...
int
stack_is_empty (Stack *stack)
{
return TRUE;
}</pre>
<p>The following is the whole of src/stack.c.</p>
<p>src/stack.c:</p>
<pre class="programlisting">#include <stdlib.h>
#include "stack.h"
#define TRUE 1
#define FALSE 0
Stack *
stack_new (void)
{
return NULL;
}
int
stack_is_empty (Stack *stack)
{
return TRUE;
}</pre>
<p>Our test should pass because the stack_is_empty() implementation always returns true.</p>
<pre class="programlisting">[stack]% test/run-test.sh
.
Finished in 0.000028 seconds
1 test(s), 1 assertion(s), 0 failure(s), 0 error(s), 0 pending(s), 0 omission(s), 0 notification(s)
100% passed</pre>
<p>Great! This is the first success!!!</p>
<p>Displayed a "." means that a test is passed. The current number of tests is just one. So one "." means all tests are passed.</p>
<p>The above result may be displayed in green. This means that we may go to the next step because our all tests are passed.</p>
<p>We confirmed that test is worked. We will complete stack implementation with writing tests.</p>
</div>
</div>
<div class="refsect1" title="Implement push">
<a name="id427596"></a><h2>Implement push</h2>
<p>We will implement push. We only accept integer for values in stack in this implementation.</p>
<div class="refsect2" title="Test for push">
<a name="id427607"></a><h3>Test for push</h3>
<p>A stack should have 1 item and not be empty after we push a value. The following is a test for this.</p>
<p>test/test-stack.c:</p>
<pre class="programlisting">...
void test_push (void);
...
void
test_push (void)
{
Stack *stack;
stack = stack_new();
cut_assert_equal_int(0, stack_get_size(stack));
stack_push(stack, 100);
cut_assert_equal_int(1, stack_get_size(stack));
cut_assert(!stack_is_empty(stack));
}</pre>
<p>We will get an error that says stack_get_size() isn't undefined if we run test.</p>
<pre class="programlisting">[stack]% test/run-test.sh
cutter: symbol lookup error: ./test/.libs/test_stack.so: undefined symbol: stack_get_size</pre>
<p>We will implement push to pass this test.</p>
<p>NOTE: We doesn't get the error on Cygwin.</p>
</div>
<hr>
<div class="refsect2" title="Implement cut_stack_push()">
<a name="id427649"></a><h3>Implement cut_stack_push()</h3>
<p>We will implement stack_get_size() and stack_push() to be able to run test even if tests aren't passed.</p>
<p>First, we add declarations to src/stack.h.</p>
<p>src/stack.h:</p>
<pre class="programlisting">...
int stack_get_size (Stack *stack);
void stack_push (Stack *stack, int value);
...</pre>
<p>And we add definitions to src/stack.c.</p>
<p>src/stack.c:</p>
<pre class="programlisting">...
int
stack_get_size (Stack *stack)
{
return 0;
}
void
stack_push (Stack *stack, int value)
{
}</pre>
<p>The reason why stack_get_size() returns 0 is the first stack_get_size() call is expected to return 0 like the following.</p>
<p>test/test-stack.c:</p>
<pre class="programlisting">...
stack = stack_new();
cut_assert_equal_int(0, stack_get_size(stack));
...</pre>
<p>We run test because push is implemented.</p>
<pre class="programlisting">[stack]% test/run-test.sh
.F
1) Failure: test_push
<1 == stack_get_size(stack)>
expected: <1>
but was: <0>
test/test-stack.c:23: test_push()
Finished in 0.000113 seconds
2 test(s), 2 assertion(s), 1 failure(s), 0 error(s), 0 pending(s), 0 omission(s), 0 notification(s)
50% passed</pre>
<p>"F" means that a test is Failed. The result may be showed in red. It indicates that it's dangerous to go to next stage because all of the current tests aren't passed. In other words, we should improve push implementation to pass the current tests before we implement pop.</p>
<p>The message form cutter command shows that the test is failed because return value of stack_get_size(stack) is 0 not 1 in test_push() function at the 23th line in test/test-stack.c. The target line is the following.</p>
<p>test/test-stack.c:23:</p>
<pre class="programlisting">cut_assert_equal_int(1, stack_get_size(stack));</pre>
<p>It's failed because our stack_get_size() implementation always return 0. We should increment an internal counter after stack_push() is called.</p>
</div>
<hr>
<div class="refsect2" title="Free memory">
<a name="id427741"></a><h3>Free memory</h3>
<p>stack_new() always returns NULL for now. Stack needs to allocate memory to have an internal counter. Stack should free memory that is unused if stack allocate memory.</p>
<p>For example, test_new_stack() should do like the following.</p>
<pre class="programlisting">void
test_new_stack (void)
{
Stack *stack;
stack = stack_new();
cut_assert(stack_is_empty(stack));
stack_free(stack);
}</pre>
<p>But stack_free() will never be called if cut_assert() where it's the above of the stack_free() fails. Because cut_assert() returns the test function immediately if the expression (stack_is_empty(stack)) is false. (It will not cause big harm because most test programs are short-lived.)</p>
<p>Cutter supports registering functions that are surely called before/after test. They are cut_setup() and cut_teardown(). They are called even if test is failed. We can use them for freeing memory allocated in test surely.</p>
<p>To freeing allocated memory for test_new_stack() surely, we can use cut_setup() and cut_teardown() like the following.</p>
<p>test/test-stack.c:</p>
<pre class="programlisting">...
static Stack *stack;
void
cut_setup (void)
{
stack = NULL;
}
void
cut_teardown (void)
{
if (stack)
stack_free(stack);
}
void
test_new_stack (void)
{
stack = stack_new();
cut_assert(stack_is_empty(stack));
}
...</pre>
<p>We can also modify test_push() to freeing allocated memory in tests by using static stack variable instead of local stack variable.</p>
<p>test/test-stack.c:</p>
<pre class="programlisting">...
void
test_push (void)
{
stack = stack_new();
cut_assert_equal_int(0, stack_get_size(stack));
stack_push(stack, 100);
cut_assert_equal_int(1, stack_get_size(stack));
cut_assert(!stack_is_empty(stack));
}
...</pre>
<p>Here is whole of the test/test-stack.c that uses cut_setup()/cut_teardown().</p>
<p>test/test-stack.c:</p>
<pre class="programlisting">#include <cutter.h>
#include <stack.h>
void test_new_stack (void);
void test_push (void);
static Stack *stack;
void
cut_setup (void)
{
stack = NULL;
}
void
cut_teardown (void)
{
if (stack)
stack_free(stack);
}
void
test_new_stack (void)
{
stack = stack_new();
cut_assert(stack_is_empty(stack));
}
void
test_push (void)
{
stack = stack_new();
cut_assert_equal_int(0, stack_get_size(stack));
stack_push(stack, 100);
cut_assert_equal_int(1, stack_get_size(stack));
cut_assert(!stack_is_empty(stack));
}</pre>
<p>We can confirm that a result of test isn't changed after this change.</p>
<pre class="programlisting">[stack]% test/run-test.sh
.F
1) Failure: test_push
<1 == stack_get_size(stack)>
expected: <1>
but was: <0>
test/test-stack.c:35: test_push()
Finished in 0.000084 seconds
2 test(s), 2 assertion(s), 1 failure(s), 0 error(s), 0 pending(s), 0 omission(s), 0 notification(s)
50% passed</pre>
</div>
<hr>
<div class="refsect2" title="Implement stack_new() and stack_free()">
<a name="id427839"></a><h3>Implement stack_new() and stack_free()</h3>
<p>We will implement stack_new() that allocate memory and stack_free() that free allocated memory.</p>
<p>First, we will declares stack_free() in src/stack.h.</p>
<p>src/stack.h:</p>
<pre class="programlisting">...
void stack_free (Stack *stack);
...</pre>
<p>Next, we will define Stack type in src/stack.c. Stack type has a field that hold stack size.</p>
<p>src/stack.c:</p>
<pre class="programlisting">...
struct _Stack {
int size;
};
...</pre>
<p>stack_new() allocates memory for Stack and stack_free() frees memory allocated by stack_new().</p>
<p>src/stack.c:</p>
<pre class="programlisting">...
Stack *
stack_new (void)
{
Stack *stack;
stack = malloc(sizeof(Stack));
if (!stack)
return NULL;
stack->size = 0;
return stack;
}
void
stack_free (Stack *stack)
{
free(stack);
}
...</pre>
<p>We can confirm that test works same as before the changes.</p>
<pre class="programlisting">[stack]% test/run-test.sh
.F
1) Failure: test_push
<1 == stack_get_size(stack)>
expected: <1>
but was: <0>
test/test-stack.c:35: test_push()
Finished in 0.000113 seconds
2 test(s), 2 assertion(s), 1 failure(s), 0 error(s), 0 pending(s), 0 omission(s), 0 notification(s)
50% passed</pre>
</div>
<hr>
<div class="refsect2" title="Really implement stack_push()">
<a name="id427906"></a><h3>Really implement stack_push()</h3>
<p>We will really implement stack_push() and stack_get_size() to pass our tests because a stack can have a stack size.</p>
<p>src/stack.c:</p>
<pre class="programlisting">...
int
stack_get_size (Stack *stack)
{
return stack->size;
}
void
stack_push (Stack *stack, int value)
{
stack->size++;
}</pre>
<p>Stack increments it's size each push and returns the size. A test for stack_get_size() that is failed until now will be passed.</p>
<pre class="programlisting">[stack]% test/run-test.sh
.F
1) Failure: test_push
expected: <!stack_is_empty(stack)> is not FALSE/NULL
test/test-stack.c:36: test_push()
Finished in 0.000113 seconds
2 test(s), 3 assertion(s), 1 failure(s), 0 error(s), 0 pending(s), 0 omission(s), 0 notification(s)
50% passed</pre>
<p>The test for stack_get_size() is passed as our expectation but there is still a failure. It's a test for stack_is_empty() in test/test-stack.c at the 36th line.</p>
<p>test/test-stack.c:36:</p>
<pre class="programlisting">cut_assert(!stack_is_empty(stack));</pre>
<p>A stack should not be empty after push.</p>
</div>
<hr>
<div class="refsect2" title="Really implement stack_is_empty()">
<a name="id427960"></a><h3>Really implement stack_is_empty()</h3>
<p>A stack should be empty only when a stack size is 0. So stack_is_empty() is changed to the following.</p>
<p>src/stack.c:</p>
<pre class="programlisting">...
int
stack_is_empty (Stack *stack)
{
return stack->size == 0;
}
...</pre>
<p>We can run test again and confirm that all of tests are passed.</p>
<pre class="programlisting">% test/run-test.sh
..
Finished in 0.000036 seconds
2 test(s), 4 assertion(s), 0 failure(s), 0 error(s), 0 pending(s), 0 omission(s), 0 notification(s)
100% passed</pre>
<p>A test for push is passed and the rest of tests are kept to pass. A result message is back to green because all of tests are passed. We can feel safe to go to the next stage; We will implement pop.</p>
</div>
</div>
<div class="refsect1" title="Implement pop">
<a name="id428001"></a><h2>Implement pop</h2>
<p>We will implement pop that retrieve a value that is inserted by push.</p>
<div class="refsect2" title="Test for pop">
<a name="id428011"></a><h3>Test for pop</h3>
<p>Pop returns a value that is inserted by the last push. Pop reduces stack size and finally a stack is empty. The following test represents expected push/pop behavior.</p>
<p>test/test-stack.c:</p>
<pre class="programlisting">...
void test_pop (void);
...
void
test_pop (void)
{
stack = stack_new();
stack_push(stack, 10);
stack_push(stack, 20);
stack_push(stack, 30);
cut_assert_equal_int(3, stack_get_size(stack));
cut_assert_equal_int(30, stack_pop(stack));
cut_assert_equal_int(2, stack_get_size(stack));
cut_assert_equal_int(20, stack_pop(stack));
cut_assert_equal_int(1, stack_get_size(stack));
stack_push(stack, 40);
cut_assert_equal_int(2, stack_get_size(stack));
cut_assert_equal_int(40, stack_pop(stack));
cut_assert_equal_int(1, stack_get_size(stack));
cut_assert_equal_int(10, stack_pop(stack));
cut_assert_equal_int(0, stack_get_size(stack));
cut_assert(stack_is_empty(stack));
}</pre>
<p>We can run test.</p>
<pre class="programlisting">[stack]% test/run-test.sh
..cutter: symbol lookup error: test/.libs/test_stack.so: undefined symbol: stack_pop</pre>
<p>There is an error that reports stack_pop() isn't defined. We can confirm that existed two tests are passed because there are two "." before the error message.</p>
<p>NOTE: We doesn't get the error on Cygwin.</p>
</div>
<hr>
<div class="refsect2" title="Implement stack_pop()">
<a name="id428058"></a><h3>Implement stack_pop()</h3>
<p>First, we declare stack_pop() in src/stack.h.</p>
<p>src/stack.h:</p>
<pre class="programlisting">...
int stack_pop (Stack *stack);
...</pre>
<p>Next, we define stack_pop() in src/stack.c.</p>
<p>src/stack.c:</p>
<pre class="programlisting">...
int
stack_pop (Stack *stack)
{
return 30;
}</pre>
<p>stack_pop() always returns 30 because the first stack_pop() call is required to return 30:</p>
<p>test/test-stack.c:50:</p>
<pre class="programlisting">cut_assert_equal_int(30, stack_pop(stack));</pre>
<p>We can confirm that test can be run and a test for pop doesn't report any error.</p>
<pre class="programlisting">[stack]% test/run-test.sh
..F
1) Failure: test_pop
<2 == stack_get_size(stack)>
expected: <2>
but was: <3>
test/test-stack.c:51: test_pop()
Finished in 0.000307 seconds
3 test(s), 6 assertion(s), 1 failure(s), 0 error(s), 0 pending(s), 0 omission(s), 0 notification(s)
66.6667% passed</pre>
<p>A test for pop is run but failed because the current stack_pop() implementation doesn't change stack size. The failure is occurred in test/test-stack.c at the 50th line and the reason is stack_get_size() in the target line returns 3 not expected 2.</p>
<p>test/test-stack.c:51:</p>
<pre class="programlisting">cut_assert_equal_int(2, stack_get_size(stack));</pre>
</div>
<hr>
<div class="refsect2" title="Allocate memory for data">
<a name="id428133"></a><h3>Allocate memory for data</h3>
<p>We can confirm that the test can be run. We will implement stack_pop() to pass the test.</p>
<p>A stack needs to save pushed data to retrieve by pop. A stack needs to have a new field to hold pushed data and stack_push()/stack_pop() allocates/frees memory for pushed data dynamically.</p>
<p>First, we will add a new field in Stack. stack_new() initializes the field and stack_free() frees the field.</p>
<p>src/stack.c:</p>
<pre class="programlisting">...
struct _Stack {
int size;
int *data;
};
Stack *
stack_new (void)
{
...
stack->data = NULL;
...
}
void
stack_free (Stack *stack)
{
free(stack->data);
free(stack);
}
...</pre>
<p>At this point, we don't change any process that effects external program. So we can confirm that the test should be failed the same as before.</p>
<pre class="programlisting">[stack]% test/run-test.sh
..F
1) Failure: test_pop
<2 == stack_get_size(stack)>
expected: <2>
but was: <3>
test/test-stack.c:51: test_pop()
Finished in 0.000097 seconds
3 test(s), 6 assertion(s), 1 failure(s), 0 error(s), 0 pending(s), 0 omission(s), 0 notification(s)
66.6667% passed</pre>
</div>
<hr>
<div class="refsect2" title="Really implement stack_pop()">
<a name="id428179"></a><h3>Really implement stack_pop()</h3>
<p>We added a new field to hold pushed data. stack_push()/stack_pop() can allocate needed memory to the field and save data.</p>
<p>src/stack.c:</p>
<pre class="programlisting">...
void
stack_push (Stack *stack, int value)
{
int *new_data;
stack->size++;
new_data = realloc(stack->data, sizeof(*stack->data) * stack->size);
if (!new_data) {
free(stack->data);
stack->data = NULL;
stack->size = 0;
return;
}
stack->data = new_data;
stack->data[stack->size - 1] = value;
}
int
stack_pop (Stack *stack)
{
int value;
int *new_data;
stack->size--;
value = stack->data[stack->size];
new_data = realloc(stack->data, sizeof(*stack->data) * stack->size);
if (stack->size > 0 && !new_data) {
free(stack->data);
stack->data = NULL;
stack->size = 0;
return value;
}
stack->data = new_data;
return value;
}</pre>
<p>We can confirm that the test for pop is passed.</p>
<pre class="programlisting">[stack]% test/run-test.sh
...
Finished in 0.000076 seconds
3 test(s), 15 assertion(s), 0 failure(s), 0 error(s), 0 pending(s), 0 omission(s), 0 notification(s)
100% passed</pre>
</div>
</div>
<div class="refsect1" title="Eliminate duplications">
<a name="id428219"></a><h2>Eliminate duplications</h2>
<p>stack_push() and stack_pop() implementations has duplications that are dynamic memory allocation process and error handling process when memory allocation is failed. It's generally not good that duplications exist because they may increase maintenance cost and so on.</p>
<p>In this section, we will eliminate duplications without changing existing semantics. We can confirm that existing semantics aren't changed by running our tests.</p>
<div class="refsect2" title="Eliminate a duplication in memory allocation process">
<a name="id428236"></a><h3>Eliminate a duplication in memory allocation process</h3>
<p>First, we will eliminate a duplication in memory allocation process like the following:</p>
<p>src/stack.c:</p>
<pre class="programlisting">new_data = realloc(stack->data, sizeof(*stack->data) * stack->size);</pre>
<p>We will extract the above part as stack_realloc().</p>
<p>src/stack.c:</p>
<pre class="programlisting">...
static int *
stack_realloc (Stack *stack)
{
return realloc(stack->data, sizeof(*stack->data) * stack->size);
}
void
stack_push (Stack *stack, int value)
{
...
new_data = stack_realloc(stack);
...
}
int
stack_pop (Stack *stack)
{
...
new_data = stack_realloc(stack);
...
}</pre>
<p>We can confirm that existing semantics aren't changed by running tests.</p>
<pre class="programlisting">[stack]% test/run-test.sh
...
Finished in 0.000078 seconds
3 test(s), 15 assertion(s), 0 failure(s), 0 error(s), 0 pending(s), 0 omission(s), 0 notification(s)
100% passed</pre>
<p>We can go to the next because the result is green.</p>
</div>
<hr>
<div class="refsect2" title="Eliminate a duplication in error handling process">
<a name="id428288"></a><h3>Eliminate a duplication in error handling process</h3>
<p>Next, we will eliminate a duplication in error handling process for memory allocation failure. The current implementation is the following:</p>
<p>src/stack.c:</p>
<pre class="programlisting">...
void
stack_push (Stack *stack, int value)
{
...
new_data = stack_realloc(stack);
if (!new_data) {
free(stack->data);
stack->data = NULL;
stack->size = 0;
return;
}
...
}
int
stack_pop (Stack *stack)
{
...
new_data = stack_realloc(stack);
if (stack->size > 0 && !new_data) {
free(stack->data);
stack->data = NULL;
stack->size = 0;
return value;
}
...
}</pre>
<p>We will move the above error handling process to stack_realloc() and stack_realloc() returns whether memory allocation is succeeded or failed instead of allocated memory.</p>
<p>src/stack.c:</p>
<pre class="programlisting">...
static int
stack_realloc (Stack *stack)
{
int *new_data;
new_data = realloc(stack->data, sizeof(*stack->data) * stack->size);
if (stack->size > 0 && !new_data) {
free(stack->data);
stack->data = NULL;
stack->size = 0;
return FALSE;
}
stack->data = new_data;
return TRUE;
}
void
stack_push (Stack *stack, int value)
{
stack->size++;
if (!stack_realloc(stack))
return;
stack->data[stack->size - 1] = value;
}
int
stack_pop (Stack *stack)
{
int value;
stack->size--;
value = stack->data[stack->size];
stack_realloc(stack);
return value;
}</pre>
<p>We should confirm that the changes doesn't change existing semantics.</p>
<pre class="programlisting">[stack]% test/run-test.sh
...
Finished in 0.000076 seconds
3 test(s), 15 assertion(s), 0 failure(s), 0 error(s), 0 pending(s), 0 omission(s), 0 notification(s)
100% passed</pre>
<p>w We confirmed that we can improve our program by eliminating duplications in our program without changing existing semantics.</p>
</div>
</div>
<div class="refsect1" title="Conclusion">
<a name="id428351"></a><h2>Conclusion</h2>
<p>This documentation shows how to setup a build environment system with GNU build system, write tests with Cutter and improve a program that has tests by using a small stack implementation.</p>
<div class="refsect2" title="Merit">
<a name="id428363"></a><h3>Merit</h3>
<p>GNU build system provides us portability.</p>
<p>Cutter provides us a method to write tests easily. Existing testing frameworks for C require to use macros to define a test or to register tests explicitly. We need to write many other things except writing tests. Cutter resolves this problem. Cutter doesn't require to use original macros to define a test. We can write a test as just a normal function. We can also write no test registration code.</p>
<p>We only used cut_assert() and cut_assert_equal_int() but Cutter provides many assertions to verify actual value is expected value like cut_assert_equal_string(). We will be able to write tests simply by them because we doesn't need to write our assertions for primitive types.</p>
<p>Cutter doesn't show needless information in test result message but show useful information as much as possible. It supports that we can find useful information easily and fix problems easily and rapidly. Cutter also tries to show backtraces on segmentation fault that is often caused for a program written by C for providing many information to fix problems.</p>
<p>It's very helpful for maintenance that improving internal structure of a program without changing existing semantics. We can easily confirm that existing semantics isn't changed with automated tests.</p>
<p>Automated tests also helps us when a new feature is developed. We can confirm that existing semantics isn't broken by codes for a new feature. Automated tests are useful for maintenance, developing new features and keeping high-quality.</p>
</div>
<hr>
<div class="refsect2" title="Stack test">
<a name="id428404"></a><h3>Stack test</h3>
<p>The following tests are the final version.</p>
<p>test/test-stack.c</p>
<pre class="programlisting">#include <cutter.h>
#include <stack.h>
void test_new_stack (void);
void test_push (void);
void test_pop (void);
static Stack *stack;
void
cut_setup (void)
{
stack = NULL;
}
void
cut_teardown (void)
{
if (stack)
stack_free(stack);
}
void
test_new_stack (void)
{
stack = stack_new();
cut_assert(stack_is_empty(stack));
}
void
test_push (void)
{
stack = stack_new();
cut_assert_equal_int(0, stack_get_size(stack));
stack_push(stack, 100);
cut_assert_equal_int(1, stack_get_size(stack));
cut_assert(!stack_is_empty(stack));
}
void
test_pop (void)
{
stack = stack_new();
stack_push(stack, 10);
stack_push(stack, 20);
stack_push(stack, 30);
cut_assert_equal_int(3, stack_get_size(stack));
cut_assert_equal_int(30, stack_pop(stack));
cut_assert_equal_int(2, stack_get_size(stack));
cut_assert_equal_int(20, stack_pop(stack));
cut_assert_equal_int(1, stack_get_size(stack));
stack_push(stack, 40);
cut_assert_equal_int(2, stack_get_size(stack));
cut_assert_equal_int(40, stack_pop(stack));
cut_assert_equal_int(1, stack_get_size(stack));
cut_assert_equal_int(10, stack_pop(stack));
cut_assert_equal_int(0, stack_get_size(stack));
cut_assert(stack_is_empty(stack));
}</pre>
</div>
<hr>
<div class="refsect2" title="Stack implementation">
<a name="id428435"></a><h3>Stack implementation</h3>
<p>The following codes are the final version. This stack implementation has some issues that error notification, performance tunings and so on because it's straightforward. But the implementation has basic features that is shown by test.</p>
<p>src/stack.c:</p>
<pre class="programlisting">#include <stdlib.h>
#include "stack.h"
#define TRUE 1
#define FALSE 0
struct _Stack {
int size;
int *data;
};
Stack *
stack_new (void)
{
Stack *stack;
stack = malloc(sizeof(Stack));
if (!stack)
return NULL;
stack->size = 0;
stack->data = NULL;
return stack;
}
void
stack_free (Stack *stack)
{
free(stack->data);
free(stack);
}
int
stack_is_empty (Stack *stack)
{
return stack->size == 0;
}
int
stack_get_size (Stack *stack)
{
return stack->size;
}
static int
stack_realloc (Stack *stack)
{
int *new_data;
new_data = realloc(stack->data, sizeof(*stack->data) * stack->size);
if (stack->size > 0 && !new_data) {
free(stack->data);
stack->data = NULL;
stack->size = 0;
return FALSE;
}
stack->data = new_data;
return TRUE;
}
void
stack_push (Stack *stack, int value)
{
stack->size++;
if (!stack_realloc(stack))
return;
stack->data[stack->size - 1] = value;
}
int
stack_pop (Stack *stack)
{
int value;
stack->size--;
value = stack->data[stack->size];
stack_realloc(stack);
return value;
}</pre>
</div>
<hr>
<div class="refsect2" title="Support no Cutter installed environment">
<a name="id428467"></a><h3>Support no Cutter installed environment</h3>
<p>In this tutorial, test/test-stack.c build is failed on no Cutter installed environment. That is make fails. If you are a developer, you must run test. So this behavior is reasonable.</p>
<p>But it's better that this stack library can be built without Cutter for users that just want to use this stack implementation as a library. They will use a released library that is tested by developers.</p>
<p>The following is a way to support no Cutter installed environment.</p>
<p>First, we change AC_CHECK_CUTTER call in configure.ac to work autogen.sh (to be exact, aclocal) without cutter.m4. (If autogen.sh is ran only by developers, this change isn't needed. In the case, aclocal fails because AC_CHECK_CUTTER isn't defined.)</p>
<p>configure.ac:</p>
<pre class="programlisting">...
m4_ifdef([AC_CHECK_CUTTER], [AC_CHECK_CUTTER], [ac_cv_use_cutter="no"])
...</pre>
<p>We use ac_cv_use_cutter as a variable name because AC_CHECK_CUTTER uses the same variable name. The variable becomes "no" if configure can't detect Cutter. On no cutter.m4 environment (no Cutter environment when autogen.sh is ran), we always can't detect Cutter.</p>
<p>Next, we define a condition that can be used in Makefile.am after AC_CHECK_CUTTER. The condition shows whether we detect Cutter or not.</p>
<p>configure.ac:</p>
<pre class="programlisting">...
m4_ifdef([AC_CHECK_CUTTER], [AC_CHECK_CUTTER], [ac_cv_use_cutter="no"])
AM_CONDITIONAL([WITH_CUTTER], [test "$ac_cv_use_cutter" != "no"])
...</pre>
<p>Last, we build test/test-stack.c and run test/run-test.sh only if WITH_CUTTER is true:</p>
<p>test/Makefile.am:</p>
<pre class="programlisting">if WITH_CUTTER
TESTS = run-test.sh
TESTS_ENVIRONMENT = NO_MAKE=yes CUTTER="$(CUTTER)"
noinst_LTLIBRARIES = test_stack.la
endif
...</pre>
<p>The followings are the whole of configure.ac and test/Makefile.am:</p>
<p>configure.ac:</p>
<pre class="programlisting">AC_PREREQ(2.59)
AC_INIT(stack, 0.0.1, you@example.com)
AC_CONFIG_AUX_DIR([config])
AC_CONFIG_HEADER([src/config.h])
AM_INIT_AUTOMAKE($PACKAGE_NAME, $PACKAGE_VERSION)
AC_PROG_LIBTOOL
m4_ifdef([AC_CHECK_CUTTER], [AC_CHECK_CUTTER], [ac_cv_use_cutter="no"])
AM_CONDITIONAL([WITH_CUTTER], [test "$ac_cv_use_cutter" != "no"])
m4_ifdef([AC_CHECK_COVERAGE], [AC_CHECK_COVERAGE])
AC_CONFIG_FILES([Makefile
src/Makefile
test/Makefile])
AC_OUTPUT</pre>
<p>test/Makefile.am:</p>
<pre class="programlisting">if WITH_CUTTER
TESTS = run-test.sh
TESTS_ENVIRONMENT = NO_MAKE=yes CUTTER="$(CUTTER)"
noinst_LTLIBRARIES = test_stack.la
endif
INCLUDES = -I$(top_srcdir)/src
LIBS = $(CUTTER_LIBS) $(top_builddir)/src/libstack.la
AM_CFLAGS = $(CUTTER_CFLAGS)
LDFLAGS = -module -rpath $(libdir) -avoid-version -no-undefined
test_stack_la_SOURCES = test-stack.c
echo-cutter:
@echo $(CUTTER)</pre>
</div>
<hr>
<div class="refsect2" title="See also">
<a name="id428572"></a><h3>See also</h3>
<div class="itemizedlist"><ul class="itemizedlist" type="disc">
<li class="listitem">
<p>xUnit: It's a library that supports a test style that uses assertXXX for verifying an actual value is an expected value. It also called testing framework. Cutter is one of xUnit testing framework. xUnit is implemented in many language:</p>
<div class="itemizedlist"><ul class="itemizedlist" type="circle">
<li class="listitem"><p>SUnit (Smalltalk)</p></li>
<li class="listitem"><p>JUnit (Java)</p></li>
<li class="listitem"><p>Test::Unit (Ruby)</p></li>
<li class="listitem"><p>PyUnit (Pytnon)</p></li>
<li class="listitem"><p>...</p></li>
</ul></div>
</li>
<li class="listitem"><p>Extreme Programming (XP): It's a programming methodology to develop high-quality software. It's heavy on testing.</p></li>
</ul></div>
</div>
</div>
</div>
<div class="footer">
<hr>
Generated by GTK-Doc V1.15</div>
</body>
</html>
|