1 /* Tests for persistence.
2  *
3  * FIXME - these need to be aggressive about finding failures, at the moment
4  * they are just confirming that good behaviour works. */
5 
6 #include <CUnit/CUnit.h>
7 #include <CUnit/Basic.h>
8 
9 #define WITH_BROKER
10 #define WITH_PERSISTENCE
11 
12 #include "mosquitto_broker_internal.h"
13 #include "persist.h"
14 
15 uint64_t last_retained;
16 char *last_sub = NULL;
17 int last_qos;
18 
19 
20 /* read entire file into memory */
file_read(const char * filename,uint8_t ** data,size_t * len)21 static int file_read(const char *filename, uint8_t **data, size_t *len)
22 {
23 	FILE *fptr;
24 	size_t rc;
25 
26 	fptr = fopen(filename, "rb");
27 	if(!fptr) return 1;
28 
29 	fseek(fptr, 0, SEEK_END);
30 	*len = ftell(fptr);
31 	*data = malloc(*len);
32 	if(!(*data)){
33 		fclose(fptr);
34 		return 1;
35 	}
36 	fseek(fptr, 0, SEEK_SET);
37 	rc = fread(*data, 1, *len, fptr);
38 	fclose(fptr);
39 
40 	if(rc == *len){
41 		return 0;
42 	}else{
43 		*len = 0;
44 		free(*data);
45 		return 1;
46 	}
47 }
48 
49 /* Crude file diff, only for small files */
file_diff(const char * one,const char * two)50 static int file_diff(const char *one, const char *two)
51 {
52 	size_t len1, len2;
53 	uint8_t *data1 = NULL, *data2 = NULL;
54 	int rc = 1;
55 
56 	if(file_read(one, &data1, &len1)){
57 		return 1;
58 	}
59 
60 	if(file_read(two, &data2, &len2)){
61 		free(data1);
62 		return 1;
63 	}
64 
65 	if(len1 == len2){
66 		rc = memcmp(data1, data2, len1);
67 	}
68 	free(data1);
69 	free(data2);
70 
71 	return rc;
72 }
73 
TEST_persistence_disabled(void)74 static void TEST_persistence_disabled(void)
75 {
76 	struct mosquitto_db db;
77 	struct mosquitto__config config;
78 	int rc;
79 
80 	memset(&db, 0, sizeof(struct mosquitto_db));
81 	memset(&config, 0, sizeof(struct mosquitto__config));
82 	db.config = &config;
83 
84 	rc = persist__backup(&db, false);
85 	CU_ASSERT_EQUAL(rc, MOSQ_ERR_INVAL);
86 
87 	config.persistence_filepath = "disabled.db";
88 	rc = persist__backup(&db, false);
89 	CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);
90 }
91 
92 
TEST_empty_file(void)93 static void TEST_empty_file(void)
94 {
95 	struct mosquitto_db db;
96 	struct mosquitto__config config;
97 	int rc;
98 
99 	memset(&db, 0, sizeof(struct mosquitto_db));
100 	memset(&config, 0, sizeof(struct mosquitto__config));
101 	db.config = &config;
102 
103 	config.persistence = true;
104 
105 	config.persistence_filepath = "empty.db";
106 	rc = persist__backup(&db, false);
107 	CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);
108 	CU_ASSERT_EQUAL(0, file_diff("files/persist_write/empty.test-db", "empty.db"));
109 	unlink("empty.db");
110 }
111 
112 
TEST_v5_config_ok(void)113 static void TEST_v5_config_ok(void)
114 {
115 	struct mosquitto_db db;
116 	struct mosquitto__config config;
117 	int rc;
118 
119 	memset(&db, 0, sizeof(struct mosquitto_db));
120 	memset(&config, 0, sizeof(struct mosquitto__config));
121 	db.config = &config;
122 
123 	config.persistence = true;
124 	config.persistence_filepath = "files/persist_read/v5-cfg.test-db";
125 	rc = persist__restore(&db);
126 	CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);
127 
128 	config.persistence_filepath = "v5-cfg.db";
129 	rc = persist__backup(&db, true);
130 	CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);
131 
132 	CU_ASSERT_EQUAL(0, file_diff("files/persist_read/v5-cfg.test-db", "v5-cfg.db"));
133 	unlink("v5-cfg.db");
134 }
135 
136 
TEST_v5_message_store_no_ref(void)137 static void TEST_v5_message_store_no_ref(void)
138 {
139 	struct mosquitto_db db;
140 	struct mosquitto__config config;
141 	int rc;
142 
143 	memset(&db, 0, sizeof(struct mosquitto_db));
144 	memset(&config, 0, sizeof(struct mosquitto__config));
145 	db.config = &config;
146 
147 	config.persistence = true;
148 	config.persistence_filepath = "files/persist_read/v5-message-store.test-db";
149 	rc = persist__restore(&db);
150 	CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);
151 
152 	config.persistence_filepath = "v5-message-store-no-ref.db";
153 	rc = persist__backup(&db, true);
154 	CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);
155 
156 	CU_ASSERT_EQUAL(0, file_diff("files/persist_write/v5-message-store-no-ref.test-db", "v5-message-store-no-ref.db"));
157 	unlink("v5-message-store-no-ref.db");
158 }
159 
160 
TEST_v5_message_store_props(void)161 static void TEST_v5_message_store_props(void)
162 {
163 	struct mosquitto_db db;
164 	struct mosquitto__config config;
165 	struct mosquitto__listener listener;
166 	int rc;
167 
168 	memset(&db, 0, sizeof(struct mosquitto_db));
169 	memset(&config, 0, sizeof(struct mosquitto__config));
170 	memset(&listener, 0, sizeof(struct mosquitto__listener));
171 	db.config = &config;
172 	listener.port = 1883;
173 	config.listeners = &listener;
174 	config.listener_count = 1;
175 
176 	config.persistence = true;
177 	config.persistence_filepath = "files/persist_read/v5-message-store-props.test-db";
178 	rc = persist__restore(&db);
179 	CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);
180 
181 	config.persistence_filepath = "v5-message-store-props.db";
182 	rc = persist__backup(&db, true);
183 	CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);
184 
185 	CU_ASSERT_EQUAL(0, file_diff("files/persist_read/v5-message-store-props.test-db", "v5-message-store-props.db"));
186 	unlink("v5-message-store-props.db");
187 }
188 
189 
TEST_v5_client(void)190 static void TEST_v5_client(void)
191 {
192 	struct mosquitto_db db;
193 	struct mosquitto__config config;
194 	int rc;
195 
196 	memset(&db, 0, sizeof(struct mosquitto_db));
197 	memset(&config, 0, sizeof(struct mosquitto__config));
198 	db.config = &config;
199 
200 	config.persistence = true;
201 	config.persistence_filepath = "files/persist_read/v5-client.test-db";
202 	rc = persist__restore(&db);
203 	CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);
204 
205 	config.persistence_filepath = "v5-client.db";
206 	rc = persist__backup(&db, true);
207 	CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);
208 
209 	CU_ASSERT_EQUAL(0, file_diff("files/persist_read/v5-client.test-db", "v5-client.db"));
210 	unlink("v5-client.db");
211 }
212 
213 
TEST_v5_client_message(void)214 static void TEST_v5_client_message(void)
215 {
216 	struct mosquitto_db db;
217 	struct mosquitto__config config;
218 	struct mosquitto__listener listener;
219 	int rc;
220 
221 	memset(&db, 0, sizeof(struct mosquitto_db));
222 	memset(&config, 0, sizeof(struct mosquitto__config));
223 	memset(&listener, 0, sizeof(struct mosquitto__listener));
224 	db.config = &config;
225 	listener.port = 1883;
226 	config.listeners = &listener;
227 	config.listener_count = 1;
228 
229 	config.persistence = true;
230 	config.persistence_filepath = "files/persist_read/v5-client-message.test-db";
231 	rc = persist__restore(&db);
232 	CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);
233 
234 	config.persistence_filepath = "v5-client-message.db";
235 	rc = persist__backup(&db, true);
236 	CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);
237 
238 	CU_ASSERT_EQUAL(0, file_diff("files/persist_read/v5-client-message.test-db", "v5-client-message.db"));
239 	unlink("v5-client-message.db");
240 }
241 
242 
TEST_v5_client_message_props(void)243 static void TEST_v5_client_message_props(void)
244 {
245 	struct mosquitto_db db;
246 	struct mosquitto__config config;
247 	struct mosquitto__listener listener;
248 	int rc;
249 
250 	memset(&db, 0, sizeof(struct mosquitto_db));
251 	memset(&config, 0, sizeof(struct mosquitto__config));
252 	memset(&listener, 0, sizeof(struct mosquitto__listener));
253 	db.config = &config;
254 	listener.port = 1883;
255 	config.listeners = &listener;
256 	config.listener_count = 1;
257 
258 	config.persistence = true;
259 	config.persistence_filepath = "files/persist_read/v5-client-message-props.test-db";
260 	rc = persist__restore(&db);
261 	CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);
262 
263 	CU_ASSERT_PTR_NOT_NULL(db.msg_store);
264 	if(db.msg_store){
265 		CU_ASSERT_PTR_NOT_NULL(db.msg_store->source_listener);
266 		if(db.msg_store->source_listener){
267 			CU_ASSERT_EQUAL(db.msg_store->source_listener->port, 1883);
268 		}
269 	}
270 
271 	config.persistence_filepath = "v5-client-message-props.db";
272 	rc = persist__backup(&db, true);
273 	CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);
274 
275 	CU_ASSERT_EQUAL(0, file_diff("files/persist_read/v5-client-message-props.test-db", "v5-client-message-props.db"));
276 	unlink("v5-client-message-props.db");
277 }
278 
279 
TEST_v5_sub(void)280 static void TEST_v5_sub(void)
281 {
282 	struct mosquitto_db db;
283 	struct mosquitto__config config;
284 	struct mosquitto__listener listener;
285 	int rc;
286 
287 	memset(&db, 0, sizeof(struct mosquitto_db));
288 	memset(&config, 0, sizeof(struct mosquitto__config));
289 	memset(&listener, 0, sizeof(struct mosquitto__listener));
290 	db.config = &config;
291 	listener.port = 1883;
292 	config.listeners = &listener;
293 	config.listener_count = 1;
294 
295 	db__open(&config, &db);
296 
297 	config.persistence = true;
298 	config.persistence_filepath = "files/persist_read/v5-sub.test-db";
299 	rc = persist__restore(&db);
300 	CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);
301 
302 	config.persistence_filepath = "v5-sub.db";
303 	rc = persist__backup(&db, true);
304 	CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);
305 
306 	CU_ASSERT_EQUAL(0, file_diff("files/persist_read/v5-sub.test-db", "v5-sub.db"));
307 	unlink("v5-sub.db");
308 }
309 
310 
311 #if 0
312 NOT WORKING
313 static void TEST_v5_full(void)
314 {
315 	struct mosquitto_db db;
316 	struct mosquitto__config config;
317 	int rc;
318 
319 	memset(&db, 0, sizeof(struct mosquitto_db));
320 	memset(&config, 0, sizeof(struct mosquitto__config));
321 	db.config = &config;
322 
323 	db__open(&config, &db);
324 
325 	config.persistence = true;
326 	config.persistence_filepath = "files/persist_write/v5-full.test-db";
327 	rc = persist__restore(&db);
328 	CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);
329 
330 	config.persistence_filepath = "v5-full.db";
331 	rc = persist__backup(&db, true);
332 	CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS);
333 
334 	CU_ASSERT_EQUAL(0, file_diff("files/persist_write/v5-full.test-db", "v5-full.db"));
335 	unlink("v5-full.db");
336 }
337 #endif
338 
339 
340 /* ========================================================================
341  * TEST SUITE SETUP
342  * ======================================================================== */
343 
344 
main(int argc,char * argv[])345 int main(int argc, char *argv[])
346 {
347 	CU_pSuite test_suite = NULL;
348 	unsigned int fails;
349 
350     if(CU_initialize_registry() != CUE_SUCCESS){
351         printf("Error initializing CUnit registry.\n");
352         return 1;
353     }
354 
355 	test_suite = CU_add_suite("Persist write", NULL, NULL);
356 	if(!test_suite){
357 		printf("Error adding CUnit persist write test suite.\n");
358         CU_cleanup_registry();
359 		return 1;
360 	}
361 
362 	if(0
363 			|| !CU_add_test(test_suite, "Persistence disabled", TEST_persistence_disabled)
364 			|| !CU_add_test(test_suite, "Empty file", TEST_empty_file)
365 			|| !CU_add_test(test_suite, "v5 config ok", TEST_v5_config_ok)
366 			|| !CU_add_test(test_suite, "v5 message store (message has no refs)", TEST_v5_message_store_no_ref)
367 			|| !CU_add_test(test_suite, "v5 message store + props", TEST_v5_message_store_props)
368 			|| !CU_add_test(test_suite, "v5 client", TEST_v5_client)
369 			|| !CU_add_test(test_suite, "v5 client message", TEST_v5_client_message)
370 			|| !CU_add_test(test_suite, "v5 client message+props", TEST_v5_client_message_props)
371 			|| !CU_add_test(test_suite, "v5 sub", TEST_v5_sub)
372 			//|| !CU_add_test(test_suite, "v5 full", TEST_v5_full)
373 			){
374 
375 		printf("Error adding persist CUnit tests.\n");
376 		CU_cleanup_registry();
377         return 1;
378     }
379 
380     CU_basic_set_mode(CU_BRM_VERBOSE);
381     CU_basic_run_tests();
382 	fails = CU_get_number_of_failures();
383     CU_cleanup_registry();
384 
385     return (int)fails;
386 }
387