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