Skip to content

Commit 08b831d

Browse files
committed
Add cache tests
1 parent fa9fc69 commit 08b831d

File tree

8 files changed

+315
-5
lines changed

8 files changed

+315
-5
lines changed

.gitignore

+4-1
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,7 @@
33
*.dSYM
44
src/server
55
src/data.txt
6-
*.o
6+
*.o
7+
cache_tests/cache_tests
8+
cache_tests/cache_tests.exe
9+
cache_tests/cache_tests.log

src/Makefile

+21-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1+
CFLAGS=-std=c99 -Wall -Wextra
2+
13
OBJS=server.o net.o file.o mime.o cache.o hashtable.o llist.o
24

35
server: $(OBJS)
4-
gcc -Wall -Wextra -o $@ $^
6+
gcc -Wall -Wextra -g -o $@ $^
57

68
net.o: net.c net.h
79
gcc -Wall -Wextra -c $<
@@ -28,3 +30,21 @@ llist.o: llist.c llist.h
2830

2931
clean:
3032
rm -f $(OBJS)
33+
rm -f server
34+
rm -f cache_tests/cache_tests
35+
rm -f cache_tests/cache_tests.exe
36+
rm -f cache_tests/cache_tests.log
37+
38+
TEST_SRC=$(wildcard cache_tests/*_tests.c)
39+
TESTS=$(patsubst %.c,%,$(TEST_SRC))
40+
41+
cache_tests/cache_tests:
42+
cc cache_tests/cache_tests.c cache.c hashtable.c llist.c -o cache_tests/cache_tests
43+
44+
test:
45+
tests
46+
47+
tests: clean $(TESTS)
48+
sh ./cache_tests/runtests.sh
49+
50+
.PHONY: tests

src/cache.h

+6-3
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@
33

44
// Individual hash table entry
55
struct cache_entry {
6-
///////////////////
7-
// IMPLEMENT ME! //
8-
///////////////////
6+
char *path; // Endpoint path--key to the cache
7+
char *content_type;
8+
int content_length;
9+
void *content;
910

1011
struct cache_entry *prev, *next; // Doubly-linked list
1112
};
@@ -18,6 +19,8 @@ struct cache {
1819
int cur_size; // Current number of entries
1920
};
2021

22+
extern struct cache_entry *alloc_entry(char *path, char *content_type, void *content, int content_length);
23+
extern void free_entry(struct cache_entry *entry);
2124
extern struct cache *cache_create(int max_size, int hashsize);
2225
extern void cache_put(struct cache *cache, char *path, char *content_type, void *content, int content_length);
2326
extern struct cache_entry *cache_get(struct cache *cache, char *path);

src/cache_tests/cache_tests.c

+160
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
#include <stdlib.h>
2+
#include <string.h>
3+
#include "utils.h"
4+
#include "minunit.h"
5+
#include "../cache.h"
6+
#include "../hashtable.h"
7+
8+
char *test_cache_create()
9+
{
10+
int max_size = 10;
11+
int hash_size = 0;
12+
13+
struct cache *cache = cache_create(max_size, hash_size);
14+
15+
// Check that each field of the cache struct was initialized to the proper value
16+
mu_assert(cache, "Your cache_create function did not return a valid pointer to the created cache");
17+
mu_assert(cache->head == NULL, "The head pointer of the cache should be initialized to NULL");
18+
mu_assert(cache->tail == NULL, "The tail pointer of the cache should be initialized to NULL");
19+
mu_assert(cache->cur_size == 0, "The cur_size field of the cache should be initialized to 0");
20+
mu_assert(cache->max_size == max_size, "The max_size field of the cache was not initialized to the expected value");
21+
mu_assert(cache->index != NULL, "The index field of the cache was not initialized");
22+
23+
cache_free(cache);
24+
25+
return NULL;
26+
}
27+
28+
char *test_cache_alloc_entry()
29+
{
30+
char *path = "/bazz/lurman.html";
31+
char *content_type = "text/html";
32+
char *content = "<head>Bazz Lurman</head>";
33+
34+
struct cache_entry *ce = alloc_entry(path, content_type, content, strlen(content));
35+
36+
// Check that the allocated entry was initialized with expected values
37+
mu_assert(check_strings(ce->path, path) == 0, "Your alloc_entry function did not allocate the path field to the expected string");
38+
mu_assert(check_strings(ce->content_type, content_type) == 0, "Your alloc_entry function did not allocate the content_type field to the expected string");
39+
mu_assert(check_strings(ce->content, content) == 0, "Your alloc_entry function did not allocate the content field to the expected string");
40+
mu_assert(ce->content_length == strlen(content), "Your alloc_entry function did not allocate the content_length field to the expected length")
41+
42+
free_entry(ce);
43+
44+
return NULL;
45+
}
46+
47+
char *test_cache_put()
48+
{
49+
// Create a cache with 3 slots
50+
struct cache *cache = cache_create(3, 0);
51+
// Create 4 test entries
52+
struct cache_entry *test_entry_1 = alloc_entry("/1", "text/plain", "1", 2);
53+
struct cache_entry *test_entry_2 = alloc_entry("/2", "text/html", "2", 2);
54+
struct cache_entry *test_entry_3 = alloc_entry("/3", "application/json", "3", 2);
55+
struct cache_entry *test_entry_4 = alloc_entry("/4", "image/png", "4", 2);
56+
57+
// Add in a single entry to the cache
58+
cache_put(cache, test_entry_1->path, test_entry_1->content_type, test_entry_1->content, test_entry_1->content_length);
59+
// Check that the cache is handling a single entry as expected
60+
mu_assert(cache->cur_size == 1, "Your cache_put function did not correctly increment the cur_size field when adding a new cache entry");
61+
mu_assert(cache->head->prev == NULL && cache->tail->next == NULL, "The head and tail of your cache should have NULL prev and next pointers when a new entry is put in an empty cache");
62+
mu_assert(check_cache_entries(cache->head, test_entry_1) == 0, "Your cache_put function did not put an entry into the head of the empty cache with the expected form");
63+
mu_assert(check_cache_entries(cache->tail, test_entry_1) == 0, "Your cache_put function did not put an entry into the tail of the empty cache with the expected form")
64+
mu_assert(check_cache_entries(hashtable_get(cache->index, "/1"), test_entry_1) == 0, "Your cache_put function did not put the expected entry into the hashtable");
65+
66+
// Add in a second entry to the cache
67+
cache_put(cache, test_entry_2->path, test_entry_2->content_type, test_entry_2->content, test_entry_2->content_length);
68+
// Check that the cache is handling both entries as expected
69+
mu_assert(cache->cur_size == 2, "Your cache_put function did not correctly increment the cur_size field when adding a new cache entry");
70+
mu_assert(check_cache_entries(cache->head, test_entry_2) == 0, "Your cache_put function did not put an entry into the head of the cache with the expected form");
71+
mu_assert(check_cache_entries(cache->tail, test_entry_1) == 0, "Your cache_put function did not move the oldest entry in the cache to the tail of the cache");
72+
mu_assert(check_cache_entries(cache->head->next, test_entry_1) == 0, "Your cache_put function did not correctly set the head->next pointer of the cache");
73+
mu_assert(check_cache_entries(hashtable_get(cache->index, "/2"), test_entry_2) == 0, "Your cache_put function did not put the expected entry into the hashtable");
74+
75+
// Add in a third entry to the cache
76+
cache_put(cache, test_entry_3->path, test_entry_3->content_type, test_entry_3->content, test_entry_3->content_length);
77+
// Check that the cache is handling all three entries as expected
78+
mu_assert(cache->cur_size == 3, "Your cache_put function did not correctly increment the cur_size field when adding a new cache entry");
79+
mu_assert(check_cache_entries(cache->head, test_entry_3) == 0, "Your cache_put function did not correctly update the head pointer of the cache");
80+
mu_assert(check_cache_entries(cache->head->next, test_entry_2) == 0, "Your cache_put function did not update the head->next pointer to point to the old head");
81+
mu_assert(check_cache_entries(cache->head->next->prev, test_entry_3) == 0, "Your cache_put function did not update the head->next->prev pointer to point to the new head entry");
82+
mu_assert(check_cache_entries(cache->head->next->next, test_entry_1) == 0, "Your cache_put function did not update the head->next->next pointer to point to the tail entry");
83+
mu_assert(check_cache_entries(cache->tail->prev, test_entry_2) == 0, "Your cache_put function did not update the tail->prev pointer to poin to the second-to-last entry");
84+
mu_assert(check_cache_entries(cache->tail, test_entry_1) == 0, "Your cache_put function did not correctly update the tail pointer of the cache");
85+
86+
// Add in a fourth entry to the cache
87+
cache_put(cache, test_entry_4->path, test_entry_4->content_type, test_entry_4->content, test_entry_4->content_length);
88+
// Check that the cache removed the oldest entry and is handling the three most-recent entries correctly
89+
mu_assert(cache->cur_size == 3, "Your cache_put function did not correctly handle the cur_size field when adding a new cache entry to a full cache");
90+
mu_assert(check_cache_entries(cache->head, test_entry_4) == 0, "Your cache_put function did not correctly handle adding a new entry to an already-full cache");
91+
mu_assert(check_cache_entries(cache->head->next, test_entry_3) == 0, "Your cache_put function did not update the head->next pointer to point to the old head");
92+
mu_assert(check_cache_entries(cache->head->next->prev, test_entry_4) == 0, "Your cache_put function did not update the head->next->prev pointer to point to the new head entry");
93+
mu_assert(check_cache_entries(cache->head->next->next, test_entry_2) == 0, "Your cache_put function did not update the head->next->next pointer to point to the tail entry");
94+
mu_assert(check_cache_entries(cache->tail->prev, test_entry_3) == 0, "Your cache_put function did not update the tail->prev pointer to poin to the second-to-last entry");
95+
mu_assert(check_cache_entries(cache->tail, test_entry_2) == 0, "Your cache_put function did not correctly handle the tail of an already-full cache");
96+
97+
cache_free(cache);
98+
99+
return NULL;
100+
}
101+
102+
char *test_cache_get()
103+
{
104+
// Create a cache with 2 slots
105+
struct cache *cache = cache_create(2, 0);
106+
// Create 3 test entries
107+
struct cache_entry *test_entry_1 = alloc_entry("/1", "text/plain", "1", 2);
108+
struct cache_entry *test_entry_2 = alloc_entry("/2", "text/html", "2", 2);
109+
struct cache_entry *test_entry_3 = alloc_entry("/3", "application/json", "3", 2);
110+
111+
struct cache_entry *entry;
112+
113+
// Insert an entry into the cache, then retrieve it
114+
cache_put(cache, test_entry_1->path, test_entry_1->content_type, test_entry_1->content, test_entry_1->content_length);
115+
entry = cache_get(cache, test_entry_1->path);
116+
// Check that the retrieved entry's values match the values of the inserted entry
117+
mu_assert(check_cache_entries(entry, test_entry_1) == 0, "Your cache_get function did not retrieve the newly-added cache entry when there was 1 entry in the cache");
118+
119+
// Insert another entry into the cache, then retrieve it
120+
cache_put(cache, test_entry_2->path, test_entry_2->content_type, test_entry_2->content, test_entry_2->content_length);
121+
entry = cache_get(cache, test_entry_2->path);
122+
// Check the retrieved entry's values and also check that the entries are ordered correctly in the cache
123+
mu_assert(check_cache_entries(entry, test_entry_2) == 0, "Your cache_get function did not retrieve the newly-added cache entry when there were 2 entries in the cache");
124+
mu_assert(check_cache_entries(cache->head, test_entry_2) == 0, "Your cache_get function did not update the head pointer to point to the newly-added entry when there are 2 entries");
125+
mu_assert(check_cache_entries(cache->tail, test_entry_1) == 0, "Your cache_get function did not move the oldest entry to the tail of the cache");
126+
127+
// Insert a third entry into the cache, then retrieve it
128+
cache_put(cache, test_entry_3->path, test_entry_3->content_type, test_entry_3->content, test_entry_3->content_length);
129+
entry = cache_get(cache, test_entry_3->path);
130+
// Check the retrieved entry's values and also check that the entries are ordered correctly in the cache
131+
mu_assert(check_cache_entries(entry, test_entry_3) == 0, "Your cache_get function did not retrieve the newly-added cache entry when there were 3 entries in the cache");
132+
mu_assert(check_cache_entries(cache->head, test_entry_3) == 0, "Your cache_get function did not update the head pointer to point to the newly-added entry when there are 3 entries");
133+
mu_assert(check_cache_entries(cache->tail, test_entry_2) == 0, "Your cache_get function did not move the oldest entry to the tail of the cache when there are 3 entries");
134+
// Check that the oldest cache entry cannot be retrieved
135+
mu_assert(cache_get(cache, test_entry_1->path) == NULL, "Your cache_get function did not remove the oldest entry from the cache");
136+
137+
// Retrieve the oldest entry in the cache
138+
entry = cache_get(cache, test_entry_2->path);
139+
// Check that the most-recently accessed entry has been moved to the head of the cache
140+
mu_assert(check_cache_entries(cache->head, test_entry_2) == 0, "Your cache_get function did not move the most-recently retrieved entry to the head of the cache");
141+
mu_assert(check_cache_entries(cache->tail, test_entry_3) == 0, "Your cache_get function did not move the oldest entry to the tail of the cache");
142+
143+
cache_free(cache);
144+
145+
return NULL;
146+
}
147+
148+
char *all_tests()
149+
{
150+
mu_suite_start();
151+
152+
mu_run_test(test_cache_create);
153+
mu_run_test(test_cache_alloc_entry);
154+
mu_run_test(test_cache_put);
155+
mu_run_test(test_cache_get);
156+
157+
return NULL;
158+
}
159+
160+
RUN_TESTS(all_tests)

src/cache_tests/dbg.h

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#ifndef __dbg_h__
2+
#define __dbg_h__
3+
4+
#include <stdio.h>
5+
#include <errno.h>
6+
#include <string.h>
7+
8+
#ifdef NDEBUG
9+
#define debug(M, ...)
10+
#else
11+
#define debug(M, ...) fprintf(stderr, "DEBUG %s:%d: " M "\n", __FILE__, __LINE__, ##__VA_ARGS__)
12+
#endif
13+
14+
#define clean_errno() (errno == 0 ? "None" : strerror(errno))
15+
16+
#define log_err(M, ...) fprintf(stderr, "[ERROR] (%s:%d: errno: %s) " M "\n", __FILE__, __LINE__, clean_errno(), ##__VA_ARGS__)
17+
18+
#define log_warn(M, ...) fprintf(stderr, "[WARN] (%s:%d: errno: %s) " M "\n", __FILE__, __LINE__, clean_errno(), ##__VA_ARGS__)
19+
20+
#define log_info(M, ...) fprintf(stderr, "[INFO] (%s:%d) " M "\n", __FILE__, __LINE__, ##__VA_ARGS__)
21+
22+
#define check(A, M, ...) if(!(A)) { log_err(M, ##__VA_ARGS__); errno=0; goto error; }
23+
24+
#define sentinel(M, ...) { log_err(M, ##__VA_ARGS__); errno=0; goto error; }
25+
26+
#define check_mem(A) check((A), "Out of memory.")
27+
28+
#define check_debug(A, M, ...) if(!(A)) { debug(M, ##__VA_ARGS__); errno=0; goto error; }
29+
30+
#endif

src/cache_tests/minunit.h

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
#undef NDEBUG
2+
#ifndef _minuit_h_
3+
#define _minuit_h_
4+
5+
#include <stdio.h>
6+
#include <stdlib.h>
7+
#include "dbg.h"
8+
9+
#define mu_suite_start() char *message = NULL
10+
11+
#define mu_assert(test, message) if(!(test)) {\
12+
log_err(message); return message; }
13+
14+
#define mu_run_test(test) debug("\n-----%s", " " #test); \
15+
message = test(); tests_run++; if (message) return message;
16+
17+
#define RUN_TESTS(name) int main(int argc, char *argv[]) {\
18+
argc = 1;\
19+
debug("----- RUNNING: %s", argv[0]);\
20+
printf("-----\nRUNNING: %s\n", argv[0]);\
21+
char *result = name();\
22+
if (result != 0) {\
23+
printf("FAILED: %s\n", result);\
24+
} else {\
25+
printf("ALL TESTS PASSED\n");\
26+
}\
27+
printf("Tests run: %d\n", tests_run);\
28+
exit(result != 0);\
29+
}
30+
31+
int tests_run;
32+
33+
#endif

src/cache_tests/runtests.sh

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
echo "Running unit tests:"
2+
3+
for i in cache_tests/*_tests cache_tests/*_tests.exe
4+
do
5+
if test -f $i
6+
then
7+
if $VALGRIND ./$i 2>> cache_tests/cache_tests.log
8+
then
9+
echo $i PASS
10+
else
11+
echo "ERROR in test $i: here's cache_tests/cache_tests.log"
12+
echo "-----"
13+
tail cache_tests/cache_tests.log
14+
exit 1
15+
fi
16+
fi
17+
done
18+
19+
echo ""

src/cache_tests/utils.h

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
#ifndef _UTILS_H_
2+
#define _UTILS_H_
3+
4+
#include "../cache.h"
5+
6+
int check_strings(char *input, char *expected)
7+
{
8+
for (; *input == *expected; input++, expected++) {
9+
if (*input == '\0') {
10+
return 0;
11+
}
12+
}
13+
14+
return *input - *expected;
15+
}
16+
17+
int check_cache_entries(struct cache_entry *input, struct cache_entry *expected)
18+
{
19+
if (input == NULL) {
20+
return 1;
21+
}
22+
23+
if (check_strings(input->path, expected->path) != 0) {
24+
return 1;
25+
}
26+
27+
if (check_strings(input->content_type, expected->content_type) != 0) {
28+
return 1;
29+
}
30+
31+
if (check_strings(input->content, expected->content) != 0) {
32+
return 1;
33+
}
34+
35+
if (input->content_length != expected->content_length) {
36+
return 1;
37+
}
38+
39+
return 0;
40+
}
41+
42+
#endif

0 commit comments

Comments
 (0)