Skip to content

Commit 8d8be87

Browse files
authored
Merge pull request #12 from UMM-CSci-Systems/add-gtest-tests
Add tests
2 parents 937f926 + 49876f4 commit 8d8be87

File tree

7 files changed

+216
-35
lines changed

7 files changed

+216
-35
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# Don't commit the executable
22
check_whitespace
3+
check_whitespace_test
34

45
# Created by https://www.toptal.com/developers/gitignore/api/c,code,emacs,vim
56
# Edit at https://www.toptal.com/developers/gitignore?templates=c,code,emacs,vim

.vscode/settings.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
"calloc",
55
"cmockery",
66
"malloc",
7+
"STREQ",
78
"structs",
89
"valgrind"
910
]

README.md

Lines changed: 128 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
# C-programming-pre-lab <!-- omit in toc -->
22

3-
Pre-lab to get started on compiling and running C programs and using `valgrind` to identify
4-
memory leaks.
3+
This is a pre-lab to get you started started on compiling and running C programs
4+
and using `valgrind` to identify memory leaks.
55

66
- [Background](#background)
77
- [Compiling and running a C program](#compiling-and-running-a-c-program)
8+
- [Compiling and running the tests](#compiling-and-running-the-tests)
89
- [Using valgrind to find memory leaks](#using-valgrind-to-find-memory-leaks)
910
- [What to do](#what-to-do)
1011

@@ -50,11 +51,11 @@ Assuming you're in the project directory, you can compile
5051
this using the command
5152

5253
```bash
53-
gcc -g -Wall -o check_whitespace check_whitespace.c
54+
gcc -g -Wall -o check_whitespace main.c check_whitespace.c
5455
```
5556

56-
`gcc` is the GNU C Compiler, which is pretty much the only C compiler
57-
people use on Linux boxes these days. The meaning of the flags:
57+
`gcc` is the GNU C Compiler, which, along with Clang/LLVM, dominates the
58+
C/C++ compiler space on Linux boxes these days. The meaning of the flags:
5859

5960
- `-g` tells `gcc` to include debugging information in the generated
6061
executable. This is allows, for example, programs like `valgrind`
@@ -74,13 +75,110 @@ people use on Linux boxes these days. The meaning of the flags:
7475
will write the executable to a file called `a.out` for strange
7576
historical reasons.
7677

78+
After the flags, we provide a list of all the `.c` files that need to be
79+
compiled and [linked together](https://en.wikipedia.org/wiki/Linker_(computing))
80+
to form a working executable.
81+
7782
Assuming your program compiled correctly (**check the output!**) then you
7883
should be able to run the program like any other executable:
7984

8085
```{bash}
8186
./check_whitespace
8287
```
8388

89+
### Compiling and running the tests
90+
91+
This includes an example of using
92+
[the GoogleTest library](https://google.github.io/googletest/)
93+
for writing unit tests for C and C++ programs. The tests are in
94+
`check_whitespace_test.cpp`, which has a `.cpp` extension because
95+
tests in GoogleTest are actually C++ (`.cpp`) instead of just C.
96+
97+
Below are examples of a few tests in GoogleTests:
98+
99+
```c++
100+
TEST(Strip, WhitespaceOnBothEnds) {
101+
ASSERT_STREQ("frog", strip(" frog "));
102+
}
103+
104+
TEST(IsClean, NoWhitespace) {
105+
ASSERT_TRUE(is_clean("University of Minnesota Morris"));
106+
}
107+
```
108+
109+
The two arguments to `TEST` are arbitrary names. The first is the name of
110+
the _suite_ this test is part of; we just used the name of the function
111+
being tested by this test (`Strip` or `IsClean`). The second is the name
112+
of this particular test, and should hopefully provide some useful information
113+
on what's being tested here.
114+
115+
GoogleTest has quite a few assertions. Here we're using `ASSERT_STREQ`, which
116+
asserts that two strings (`STR`) are equal (`EQ`), and `ASSERT_TRUE`.
117+
118+
To compile this is considerably more complicated because we have to use C++
119+
and we need to include the GoogleTest library:
120+
121+
```text
122+
g++ -Wall -g -o check_whitespace_test check_whitespace.c check_whitespace_test.cpp -lgtest
123+
```
124+
125+
Here we're using `g++` instead of `gcc` to indicate that we want the C++ compiler.
126+
We've also added the `-lgtest` at the end, telling the compiler to include the
127+
`gtest` _library_ (hence the `-l`) when constructing the final executable. Also
128+
note that we're _not_ including `main.c` when we compile the test code;
129+
`check_whitespace_test.cpp` contains a `main()` function so we _can't_ include
130+
`main.c` (which provides a `main()` function), or the system won't know which
131+
one to call.
132+
133+
Assuming your program compiled correctly (again, **check the output!**) then you
134+
should be able to run the test code:
135+
136+
```{bash}
137+
./check_whitespace_test
138+
```
139+
140+
If all the tests pass (and they should initially) you should get something like:
141+
142+
```text
143+
$ ./check_whitespace_test
144+
[==========] Running 10 tests from 2 test cases.
145+
[----------] Global test environment set-up.
146+
[----------] 5 tests from strip
147+
[ RUN ] strip.EmptyString
148+
[ OK ] strip.EmptyString (0 ms)
149+
[ RUN ] strip.NoWhitespace
150+
[ OK ] strip.NoWhitespace (0 ms)
151+
[ RUN ] strip.WhitespaceOnFront
152+
[ OK ] strip.WhitespaceOnFront (0 ms)
153+
[ RUN ] strip.WhitespaceOnBack
154+
[ OK ] strip.WhitespaceOnBack (0 ms)
155+
[ RUN ] strip.WhitespaceOnBothEnds
156+
[ OK ] strip.WhitespaceOnBothEnds (0 ms)
157+
[----------] 5 tests from strip (0 ms total)
158+
159+
[----------] 5 tests from is_clean
160+
[ RUN ] is_clean.EmptyString
161+
[ OK ] is_clean.EmptyString (0 ms)
162+
[ RUN ] is_clean.NoWhitespace
163+
[ OK ] is_clean.NoWhitespace (0 ms)
164+
[ RUN ] is_clean.WhitespaceOnFront
165+
[ OK ] is_clean.WhitespaceOnFront (0 ms)
166+
[ RUN ] is_clean.WhitespaceOnBack
167+
[ OK ] is_clean.WhitespaceOnBack (0 ms)
168+
[ RUN ] is_clean.WhitespaceOnBothEnds
169+
[ OK ] is_clean.WhitespaceOnBothEnds (0 ms)
170+
[----------] 5 tests from is_clean (0 ms total)
171+
172+
[----------] Global test environment tear-down
173+
[==========] 10 tests from 2 test cases ran. (0 ms total)
174+
[ PASSED ] 10 tests.
175+
```
176+
177+
You should make sure you recompile and rerun the tests after you
178+
make changes while fixing the memory leak problems down below. It's
179+
not enough to just fix the memory leaks if you also break the code in
180+
the process, and the tests should help ensure that you're good there.
181+
84182
### Using valgrind to find memory leaks
85183

86184
One of the more subtle problems with explicit memory management is that
@@ -163,7 +261,10 @@ Note that this tells you where the lost bytes were
163261
be *freed*, as that's going to depend on how they're used after they're
164262
allocated.
165263

166-
There are two types of memory leaks, one of which is frankly easier to
264+
:warning: not all of these output blocks will be useful. Look for ones that
265+
refer to some of _your_ code somewhere, like `strip` or `is_clean`.
266+
267+
There are two common types of memory leaks, one of which is frankly easier to
167268
sort out than the other.
168269

169270
The easy ones are where function `f()` allocates _local_ memory (memory
@@ -194,11 +295,31 @@ test code (you could always just change the test code to say everything
194295
passes!), but if the memory leaks to the test code, then that's where the
195296
fix has to be made.
196297

298+
:raising_hand: **Tip:** If you need to free a value and you don't have a
299+
name for it, _give it one_. E.g., add an assignment statement like
300+
`s = value_to_free()` that gives that value (`value_to_free()`) a name
301+
(`s`) so you can free it with something like `free(s)`. Also, don't forget
302+
how to write clean code just because you're using C. If you find yourself
303+
with multiple functions with the same structure, is there a way you can
304+
write a helper function that captures that structure so you don't have to
305+
repeat it over and over?
306+
307+
Once you have everything happy, you will hopefully get a line like:
308+
309+
```text
310+
==357046== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
311+
312+
```
313+
314+
at the end indicating that you now have 0 errors and all is well.
315+
197316
## What to do
198317

199-
- [ ] Compile the program `check_whitespace.c`
318+
- [ ] Compile the program `check_whitespace`
200319
and run `valgrind` on it to find any leaks it may have (hint: it has at
201320
least one).
321+
- [ ] Also compile `check_whitespace_test` and run `valgrind` on the test code, to
322+
find any leaks there (there are several).
202323
- [ ] In `leak_report.md` describe why the memory errors happen, and how to fix them.
203324
- [ ] Actually fix the code.
204325
- [ ] Commit, push, etc.

check_whitespace.c

Lines changed: 4 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
* Strips spaces from both the front and back of a string,
77
* leaving any internal spaces alone.
88
*/
9-
char* strip(char* str) {
9+
char const *strip(char const *str) {
1010
int size = strlen(str);
1111

1212
// This counts the number of leading and trailing spaces
@@ -33,7 +33,7 @@ char* strip(char* str) {
3333

3434
// Allocate a slot for all the "saved" characters
3535
// plus one extra for the null terminator.
36-
char* result = calloc(size-num_spaces+1, sizeof(char));
36+
char* result = (char*) calloc(size-num_spaces+1, sizeof(char));
3737

3838
// Copy in the "saved" characters.
3939
int i;
@@ -50,10 +50,10 @@ char* strip(char* str) {
5050
* Return true (1) if the given string is "clean", i.e., has
5151
* no spaces at the front or the back of the string.
5252
*/
53-
int is_clean(char* str) {
53+
int is_clean(char const *str) {
5454
// We check if it's clean by calling strip and seeing if the
5555
// result is the same as the original string.
56-
char* cleaned = strip(str);
56+
char const *cleaned = strip(str);
5757

5858
// strcmp compares two strings, returning a negative value if
5959
// the first is less than the second (in alphabetical order),
@@ -63,27 +63,3 @@ int is_clean(char* str) {
6363

6464
return result == 0;
6565
}
66-
67-
int main() {
68-
int NUM_STRINGS = 7;
69-
// Makes an array of 7 string constants for testing.
70-
char* strings[] = {
71-
"Morris",
72-
" stuff",
73-
"Minnesota",
74-
"nonsense ",
75-
"USA",
76-
" ",
77-
" silliness "
78-
};
79-
80-
for (int i = 0; i < NUM_STRINGS; ++i) {
81-
if (is_clean(strings[i])) {
82-
printf("The string '%s' is clean.\n", strings[i]);
83-
} else {
84-
printf("The string '%s' is NOT clean.\n", strings[i]);
85-
}
86-
}
87-
88-
return 0;
89-
}

check_whitespace.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#ifndef CHECK_WHITESPACE_H_GUARD
2+
#define CHECK_WHITESPACE_H_GUARD
3+
4+
char* strip(char const *str);
5+
int is_clean(char const *str);
6+
7+
#endif

check_whitespace_test.cpp

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
#include <gtest/gtest.h>
2+
3+
#include "check_whitespace.h"
4+
5+
TEST(strip, EmptyString) {
6+
ASSERT_STREQ("", strip(""));
7+
}
8+
9+
TEST(strip, NoWhitespace) {
10+
ASSERT_STREQ("frog", strip("frog"));
11+
}
12+
13+
TEST(strip, WhitespaceOnFront) {
14+
ASSERT_STREQ("frog", strip(" frog"));
15+
}
16+
17+
TEST(strip, WhitespaceOnBack) {
18+
ASSERT_STREQ("frog", strip("frog "));
19+
}
20+
21+
TEST(strip, WhitespaceOnBothEnds) {
22+
ASSERT_STREQ("frog", strip(" frog "));
23+
}
24+
25+
TEST(is_clean, EmptyString) {
26+
ASSERT_TRUE(is_clean(""));
27+
}
28+
29+
TEST(is_clean, NoWhitespace) {
30+
ASSERT_TRUE(is_clean("University of Minnesota Morris"));
31+
}
32+
33+
TEST(is_clean, WhitespaceOnFront) {
34+
ASSERT_FALSE(is_clean(" University of Minnesota Morris"));
35+
}
36+
37+
TEST(is_clean, WhitespaceOnBack) {
38+
ASSERT_FALSE(is_clean("University of Minnesota Morris "));
39+
}
40+
41+
TEST(is_clean, WhitespaceOnBothEnds) {
42+
ASSERT_FALSE(is_clean(" University of Minnesota Morris" ));
43+
}
44+
45+
int main(int argc, char *argv[]) {
46+
::testing::InitGoogleTest(&argc, argv);
47+
return RUN_ALL_TESTS();
48+
}

main.c

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#include <stdio.h>
2+
3+
#include "check_whitespace.h"
4+
5+
int main() {
6+
int NUM_STRINGS = 7;
7+
// Makes an array of 7 string constants for testing.
8+
char const *strings[] = {
9+
"Morris",
10+
" stuff",
11+
"Minnesota",
12+
"nonsense ",
13+
"USA",
14+
" ",
15+
" silliness "
16+
};
17+
18+
for (int i = 0; i < NUM_STRINGS; ++i) {
19+
if (is_clean(strings[i])) {
20+
printf("The string '%s' is clean.\n", strings[i]);
21+
} else {
22+
printf("The string '%s' is NOT clean.\n", strings[i]);
23+
}
24+
}
25+
26+
return 0;
27+
}

0 commit comments

Comments
 (0)