1 /*
2  * Copyright 2016 Vincent Sanders <vince@netsurf-browser.org>
3  *
4  * This file is part of NetSurf, http://www.netsurf-browser.org/
5  *
6  * NetSurf is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; version 2 of the License.
9  *
10  * NetSurf is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 /**
20  * \file
21  * Tests for user option processing
22  */
23 
24 #include <assert.h>
25 #include <stdbool.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <unistd.h>
30 #include <check.h>
31 
32 #include "utils/errors.h"
33 #include "utils/log.h"
34 #include "utils/nsoption.h"
35 
36 #ifndef TESTROOT
37 #define TESTROOT "/tmp"
38 #endif
39 
40 const char *test_choices_path = "test/data/Choices";
41 const char *test_choices_short_path = "test/data/Choices-short";
42 const char *test_choices_all_path = "test/data/Choices-all";
43 const char *test_choices_full_path = "test/data/Choices-full";
44 const char *test_choices_missing_path = "test/data/Choices-missing";
45 
46 /* Stubs */
nslog_set_filter_by_options()47 nserror nslog_set_filter_by_options() { return NSERROR_OK; }
48 
49 /**
50  * generate test output filenames
51  */
testnam(char * out)52 static char *testnam(char *out)
53 {
54 	static int count = 0;
55 	static char name[64];
56 	int pid;
57 	pid=getpid();
58 	snprintf(name, 64, TESTROOT"/nsoptiontest%d%d", pid, count);
59 	count++;
60 	return name;
61 }
62 
gui_options_init_defaults(struct nsoption_s * defaults)63 static nserror gui_options_init_defaults(struct nsoption_s *defaults)
64 {
65 	/* Set defaults for absent option strings */
66 	nsoption_setnull_charp(ca_bundle, strdup("NetSurf:Resources.ca-bundle"));
67 	nsoption_setnull_charp(cookie_file, strdup("NetSurf:Cookies"));
68 	nsoption_setnull_charp(cookie_jar, strdup("Cookies"));
69 
70 	if (nsoption_charp(ca_bundle) == NULL ||
71 	    nsoption_charp(cookie_file) == NULL ||
72 	    nsoption_charp(cookie_jar) == NULL) {
73 		return NSERROR_BAD_PARAMETER;
74 	}
75 	return NSERROR_OK;
76 }
77 
78 
79 /**
80  * compare two files contents
81  */
cmp(const char * f1,const char * f2)82 static int cmp(const char *f1, const char *f2)
83 {
84 	int res = 0;
85 	FILE *fp1;
86 	FILE *fp2;
87 	int ch1;
88 	int ch2;
89 
90 	fp1 = fopen(f1, "r");
91 	if (fp1 == NULL) {
92 		return -1;
93 	}
94 	fp2 = fopen(f2, "r");
95 	if (fp2 == NULL) {
96 		fclose(fp1);
97 		return -1;
98 	}
99 
100 	while (res == 0) {
101 		ch1 = fgetc(fp1);
102 		ch2 = fgetc(fp2);
103 
104 		if (ch1 != ch2) {
105 			res = 1;
106 		}
107 
108 		if (ch1 == EOF) {
109 			break;
110 		}
111 	}
112 
113 	fclose(fp1);
114 	fclose(fp2);
115 	return res;
116 }
117 
118 /** option create fixture */
nsoption_create(void)119 static void nsoption_create(void)
120 {
121 	nserror res;
122 
123 	res = nsoption_init(NULL, NULL, NULL);
124 	ck_assert_int_eq(res, NSERROR_OK);
125 }
126 
127 /** option create fixture for format case */
nsoption_format_create(void)128 static void nsoption_format_create(void)
129 {
130 	nserror res;
131 
132 	res = nsoption_init(NULL, NULL, NULL);
133 	ck_assert_int_eq(res, NSERROR_OK);
134 
135 	/* read from file */
136 	res = nsoption_read(test_choices_path, NULL);
137 	ck_assert_int_eq(res, NSERROR_OK);
138 }
139 
140 /** option teardown fixture */
nsoption_teardown(void)141 static void nsoption_teardown(void)
142 {
143 	nserror res;
144 
145 	res = nsoption_finalise(NULL, NULL);
146 	ck_assert_int_eq(res, NSERROR_OK);
147 }
148 
149 
150 /**
151  * Test full options session from start to finish
152  */
START_TEST(nsoption_session_test)153 START_TEST(nsoption_session_test)
154 {
155 	nserror res;
156 	int argc = 2;
157 	char arg1[] = "nsoption";
158 	char arg2[] = "--http_proxy_host=fooo";
159 	char *argv[] = { arg1, arg2, NULL};
160 	char *outnam;
161 
162 	res = nsoption_init(gui_options_init_defaults, NULL, NULL);
163 	ck_assert_int_eq(res, NSERROR_OK);
164 
165 	/* read from file */
166 	res = nsoption_read(test_choices_path, NULL);
167 	ck_assert_int_eq(res, NSERROR_OK);
168 
169 	/* overlay commandline */
170 	res = nsoption_commandline(&argc, &argv[0], NULL);
171 	ck_assert_int_eq(res, NSERROR_OK);
172 
173 	/* change a string option */
174 	nsoption_set_charp(http_proxy_host, strdup("bar"));
175 
176 	/* change an uint option */
177 	nsoption_set_uint(disc_cache_size, 42);
178 
179 	/* change a colour */
180 	nsoption_set_colour(sys_colour_ActiveBorder, 0x00d0000d);
181 
182 	/* write options out */
183 	outnam = testnam(NULL);
184 	res = nsoption_write(outnam, NULL, NULL);
185 	ck_assert_int_eq(res, NSERROR_OK);
186 
187 	/* check for the correct answer */
188 	ck_assert_int_eq(cmp(outnam, test_choices_full_path), 0);
189 
190 	/* remove test output */
191 	unlink(outnam);
192 
193 	res = nsoption_finalise(NULL, NULL);
194 	ck_assert_int_eq(res, NSERROR_OK);
195 
196 }
197 END_TEST
198 
nsoption_session_case_create(void)199 static TCase *nsoption_session_case_create(void)
200 {
201 	TCase *tc;
202 	tc = tcase_create("Full session");
203 
204 	tcase_add_test(tc, nsoption_session_test);
205 
206 	return tc;
207 }
208 
209 
210 
211 struct format_test_vec_s {
212 	int opt_idx;
213 	const char *res_html;
214 	const char *res_text;
215 };
216 
217 struct format_test_vec_s format_test_vec[] = {
218 	{
219 		NSOPTION_http_proxy,
220 		"<tr><th>http_proxy</th><td>boolean</td><td>default</td><td>false</td></tr>",
221 		"http_proxy:0"
222 	},
223 	{
224 		NSOPTION_enable_javascript,
225 		"<tr><th>enable_javascript</th><td>boolean</td><td>user</td><td>true</td></tr>",
226 		"enable_javascript:1"
227 	},
228 	{
229 		NSOPTION_http_proxy_port,
230 		"<tr><th>http_proxy_port</th><td>integer</td><td>default</td><td>8080</td></tr>",
231 		"http_proxy_port:8080"
232 	},
233 	{
234 		NSOPTION_http_proxy_host,
235 		"<tr><th>http_proxy_host</th><td>string</td><td>default</td><td><span class=\"null-content\">NULL</span></td></tr>",
236 		"http_proxy_host:"
237 	},
238 	{
239 		NSOPTION_cookie_file,
240 		"<tr><th>cookie_file</th><td>string</td><td>user</td><td>/home/vince/.netsurf/Cookies</td></tr>",
241 		"cookie_file:/home/vince/.netsurf/Cookies"
242 	},
243 	{
244 		NSOPTION_disc_cache_size,
245 		"<tr><th>disc_cache_size</th><td>unsigned integer</td><td>default</td><td>1073741824</td></tr>",
246 		"disc_cache_size:1073741824"
247 	},
248 	{
249 		NSOPTION_sys_colour_ActiveBorder,
250 		"<tr><th>sys_colour_ActiveBorder</th><td>colour</td><td>default</td><td><span style=\"font-family:Monospace;\">#D3D3D3</span> <span style=\"background-color: #d3d3d3; border: 1px solid #000000; display: inline-block; width: 1em; height: 1em;\"></span></td></tr>",
251 		"sys_colour_ActiveBorder:d3d3d3"
252 	},
253 };
254 
255 /**
256  * Test formatting of html output
257  */
START_TEST(nsoption_format_html_test)258 START_TEST(nsoption_format_html_test)
259 {
260 	int ret;
261 	char buffer[1024];
262 	struct format_test_vec_s *tst = &format_test_vec[_i];
263 
264 	ret = nsoption_snoptionf(buffer, sizeof buffer, tst->opt_idx,
265 		"<tr><th>%k</th><td>%t</td><td>%p</td><td>%V</td></tr>");
266 	ck_assert_int_gt(ret, 0);
267 	ck_assert_str_eq(buffer, tst->res_html);
268 }
269 END_TEST
270 
271 /**
272  * Test formatting of text output
273  */
START_TEST(nsoption_format_text_test)274 START_TEST(nsoption_format_text_test)
275 {
276 	int ret;
277 	char buffer[1024];
278 	struct format_test_vec_s *tst = &format_test_vec[_i];
279 
280 	ret = nsoption_snoptionf(buffer, sizeof buffer, tst->opt_idx,
281 				 "%k:%v");
282 	ck_assert_int_gt(ret, 0);
283 	ck_assert_str_eq(buffer, tst->res_text);
284 }
285 END_TEST
286 
287 #define NELEMS(x)  (sizeof(x) / sizeof((x)[0]))
288 
nsoption_format_case_create(void)289 static TCase *nsoption_format_case_create(void)
290 {
291 	TCase *tc;
292 	tc = tcase_create("Formatted output");
293 
294 	/* ensure options are initialised and finalised for every test */
295 	tcase_add_unchecked_fixture(tc,
296 				    nsoption_format_create,
297 				    nsoption_teardown);
298 
299 	tcase_add_loop_test(tc,
300 			    nsoption_format_html_test,
301 			    0, NELEMS(format_test_vec));
302 
303 	tcase_add_loop_test(tc,
304 			    nsoption_format_text_test,
305 			    0, NELEMS(format_test_vec));
306 
307 	return tc;
308 }
309 
310 
311 /**
312  * Test dumping option file
313  */
START_TEST(nsoption_dump_test)314 START_TEST(nsoption_dump_test)
315 {
316 	nserror res;
317 	char *outnam;
318 	FILE *fp;
319 
320 	res = nsoption_read(test_choices_path, NULL);
321 	ck_assert_int_eq(res, NSERROR_OK);
322 
323 	outnam = testnam(NULL);
324 
325 	fp = fopen(outnam, "w");
326 	res = nsoption_dump(fp, NULL);
327 	fclose(fp);
328 
329 	ck_assert_int_eq(res, NSERROR_OK);
330 
331 	ck_assert_int_eq(cmp(outnam, test_choices_all_path), 0);
332 
333 	unlink(outnam);
334 }
335 END_TEST
336 
337 /**
338  * Test writing option file
339  */
START_TEST(nsoption_write_test)340 START_TEST(nsoption_write_test)
341 {
342 	nserror res;
343 	char *outnam;
344 
345 	res = nsoption_read(test_choices_path, NULL);
346 	ck_assert_int_eq(res, NSERROR_OK);
347 
348 	outnam = testnam(NULL);
349 
350 	res = nsoption_write(outnam, NULL, NULL);
351 	ck_assert_int_eq(res, NSERROR_OK);
352 
353 	ck_assert_int_eq(cmp(outnam, test_choices_short_path), 0);
354 
355 	unlink(outnam);
356 }
357 END_TEST
358 
359 /**
360  * Test reading option file
361  */
START_TEST(nsoption_read_test)362 START_TEST(nsoption_read_test)
363 {
364 	nserror res;
365 	res = nsoption_read(test_choices_path, NULL);
366 	ck_assert_int_eq(res, NSERROR_OK);
367 
368 	ck_assert(nsoption_charp(homepage_url) != NULL);
369 	ck_assert_str_eq(nsoption_charp(homepage_url), "about:welcome");
370 }
371 END_TEST
372 
373 
374 /**
375  * Test reading missing option file
376  */
START_TEST(nsoption_read_missing_test)377 START_TEST(nsoption_read_missing_test)
378 {
379 	nserror res;
380 	res = nsoption_read(test_choices_missing_path, NULL);
381 	ck_assert_int_eq(res, NSERROR_NOT_FOUND);
382 }
383 END_TEST
384 
385 /**
386  * Test commandline string value setting
387  */
START_TEST(nsoption_commandline_test)388 START_TEST(nsoption_commandline_test)
389 {
390 	nserror res;
391 	int argc = 4;
392 	char arg1[] = "nsoption";
393 	char arg2[] = "--http_proxy_host=fooo";
394 	char arg3[] = "--http_proxy_port";
395 	char arg4[] = "not-option";
396 	char *argv[] = { arg1, arg2, arg3, arg4, NULL};
397 
398 	/* commandline */
399 	res = nsoption_commandline(&argc, &argv[0], NULL);
400 	ck_assert_int_eq(res, NSERROR_OK);
401 
402 	ck_assert(nsoption_charp(http_proxy_host) != NULL);
403 	ck_assert_str_eq(nsoption_charp(http_proxy_host), "fooo");
404 }
405 END_TEST
406 
nsoption_case_create(void)407 static TCase *nsoption_case_create(void)
408 {
409 	TCase *tc;
410 	tc = tcase_create("File operations");
411 
412 	/* ensure options are initialised and finalised for every test */
413 	tcase_add_unchecked_fixture(tc,
414 				    nsoption_create,
415 				    nsoption_teardown);
416 
417 	tcase_add_test(tc, nsoption_commandline_test);
418 	tcase_add_test(tc, nsoption_read_test);
419 	tcase_add_test(tc, nsoption_read_missing_test);
420 	tcase_add_test(tc, nsoption_write_test);
421 	tcase_add_test(tc, nsoption_dump_test);
422 
423 	return tc;
424 }
425 
426 
427 /**
428  * Test finalisation without init
429  */
START_TEST(nsoption_api_fini_no_init_test)430 START_TEST(nsoption_api_fini_no_init_test)
431 {
432 	nserror res;
433 
434 	/* attempt to finalise without init */
435 	res = nsoption_finalise(NULL, NULL);
436 	ck_assert_int_eq(res, NSERROR_BAD_PARAMETER);
437 }
438 END_TEST
439 
440 /**
441  * Test read without path
442  */
START_TEST(nsoption_api_read_no_path_test)443 START_TEST(nsoption_api_read_no_path_test)
444 {
445 	nserror res;
446 
447 	/* read with no path or init */
448 	res = nsoption_read(NULL, NULL);
449 	ck_assert_int_eq(res, NSERROR_BAD_PARAMETER);
450 }
451 END_TEST
452 
453 /**
454  * Test read without init
455  */
START_TEST(nsoption_api_read_no_init_test)456 START_TEST(nsoption_api_read_no_init_test)
457 {
458 	nserror res;
459 
460 	/* read with path but no init */
461 	res = nsoption_read(test_choices_path, NULL);
462 	ck_assert_int_eq(res, NSERROR_BAD_PARAMETER);
463 }
464 END_TEST
465 
466 /**
467  * Test write without path
468  */
START_TEST(nsoption_api_write_no_path_test)469 START_TEST(nsoption_api_write_no_path_test)
470 {
471 	nserror res;
472 
473 	/* write with no path or init */
474 	res = nsoption_write(NULL, NULL, NULL);
475 	ck_assert_int_eq(res, NSERROR_BAD_PARAMETER);
476 }
477 END_TEST
478 
479 /**
480  * Test write without init
481  */
START_TEST(nsoption_api_write_no_init_test)482 START_TEST(nsoption_api_write_no_init_test)
483 {
484 	nserror res;
485 
486 	/* write with path but no init */
487 	res = nsoption_write(test_choices_path, NULL, NULL);
488 	ck_assert_int_eq(res, NSERROR_BAD_PARAMETER);
489 
490 }
491 END_TEST
492 
493 /**
494  * Test dump without path
495  */
START_TEST(nsoption_api_dump_no_path_test)496 START_TEST(nsoption_api_dump_no_path_test)
497 {
498 	nserror res;
499 
500 	/* write with no path or init */
501 	res = nsoption_dump(NULL, NULL);
502 	ck_assert_int_eq(res, NSERROR_BAD_PARAMETER);
503 }
504 END_TEST
505 
506 /**
507  * Test dump without init
508  */
START_TEST(nsoption_api_dump_no_init_test)509 START_TEST(nsoption_api_dump_no_init_test)
510 {
511 	nserror res;
512 	FILE *outf;
513 
514 	outf = tmpfile();
515 	ck_assert(outf != NULL);
516 
517 	/* write with path but no init */
518 	res = nsoption_dump(outf, NULL);
519 	ck_assert_int_eq(res, NSERROR_BAD_PARAMETER);
520 
521 	fclose(outf);
522 }
523 END_TEST
524 
525 /**
526  * Test commandline without args
527  */
START_TEST(nsoption_api_commandline_no_args_test)528 START_TEST(nsoption_api_commandline_no_args_test)
529 {
530 	nserror res;
531 	int argc = 2;
532 	char arg1[] = "nsoption";
533 	char arg2[] = "--http_proxy_host=fooo";
534 	char *argv[] = { arg1, arg2, NULL};
535 
536 	/* commandline with no argument count or init */
537 	res = nsoption_commandline(NULL, &argv[0], NULL);
538 	ck_assert_int_eq(res, NSERROR_BAD_PARAMETER);
539 
540 	/* commandline with no argument vector or init */
541 	res = nsoption_commandline(&argc, NULL, NULL);
542 	ck_assert_int_eq(res, NSERROR_BAD_PARAMETER);
543 }
544 END_TEST
545 
546 /**
547  * Test commandline without init
548  */
START_TEST(nsoption_api_commandline_no_init_test)549 START_TEST(nsoption_api_commandline_no_init_test)
550 {
551 	nserror res;
552 	int argc = 2;
553 	char arg1[] = "nsoption";
554 	char arg2[] = "--http_proxy_host=fooo";
555 	char *argv[] = { arg1, arg2, NULL};
556 
557 	/* write with path but no init */
558 	res = nsoption_commandline(&argc, &argv[0], NULL);
559 	ck_assert_int_eq(res, NSERROR_BAD_PARAMETER);
560 }
561 END_TEST
562 
563 
564 /**
565  * Test default initialisation and repeated finalisation
566  */
START_TEST(nsoption_api_fini_twice_test)567 START_TEST(nsoption_api_fini_twice_test)
568 {
569 	nserror res;
570 	res = nsoption_init(NULL, NULL, NULL);
571 	ck_assert_int_eq(res, NSERROR_OK);
572 
573 	res = nsoption_finalise(NULL, NULL);
574 	ck_assert_int_eq(res, NSERROR_OK);
575 
576 	res = nsoption_finalise(NULL, NULL);
577 	ck_assert_int_eq(res, NSERROR_BAD_PARAMETER);
578 }
579 END_TEST
580 
581 
582 /**
583  * Test default initialisation and finalisation
584  */
START_TEST(nsoption_api_init_def_test)585 START_TEST(nsoption_api_init_def_test)
586 {
587 	nserror res;
588 	res = nsoption_init(NULL, NULL, NULL);
589 	ck_assert_int_eq(res, NSERROR_OK);
590 
591 	res = nsoption_finalise(NULL, NULL);
592 	ck_assert_int_eq(res, NSERROR_OK);
593 }
594 END_TEST
595 
596 /**
597  * Test default initialisation and finalisation with parameters
598  */
START_TEST(nsoption_api_init_param_test)599 START_TEST(nsoption_api_init_param_test)
600 {
601 	nserror res;
602 	res = nsoption_init(NULL, &nsoptions, &nsoptions_default);
603 	ck_assert_int_eq(res, NSERROR_OK);
604 
605 	res = nsoption_finalise(nsoptions, nsoptions_default);
606 	ck_assert_int_eq(res, NSERROR_OK);
607 }
608 END_TEST
609 
failing_init_cb(struct nsoption_s * defaults)610 static nserror failing_init_cb(struct nsoption_s *defaults)
611 {
612 	return NSERROR_INIT_FAILED;
613 }
614 
615 /**
616  * Test default initialisation with failing callback
617  */
START_TEST(nsoption_api_init_failcb_test)618 START_TEST(nsoption_api_init_failcb_test)
619 {
620 	nserror res;
621 	res = nsoption_init(failing_init_cb, NULL, NULL);
622 	ck_assert_int_eq(res, NSERROR_INIT_FAILED);
623 }
624 END_TEST
625 
626 /**
627  * Test snoptionf format
628  */
START_TEST(nsoption_api_snoptionf_badfmt_test)629 START_TEST(nsoption_api_snoptionf_badfmt_test)
630 {
631 	int ret;
632 	ret = nsoption_snoptionf(NULL, 0, -1, NULL);
633 	ck_assert_int_eq(ret, -1);
634 }
635 END_TEST
636 
637 /**
638  * Test snoptionf range
639  */
START_TEST(nsoption_api_snoptionf_param_test)640 START_TEST(nsoption_api_snoptionf_param_test)
641 {
642 	int ret;
643 
644 	ret = nsoption_snoptionf(NULL, 0, NSOPTION_LISTEND, "");
645 	ck_assert_int_eq(ret, -1);
646 }
647 END_TEST
648 
649 /**
650  * Test snoptionf with no initialisation
651  */
START_TEST(nsoption_api_snoptionf_no_init_test)652 START_TEST(nsoption_api_snoptionf_no_init_test)
653 {
654 	int ret;
655 	ret = nsoption_snoptionf(NULL, 0, 0, "");
656 	ck_assert_int_eq(ret, -1);
657 }
658 END_TEST
659 
660 
nsoption_api_case_create(void)661 static TCase *nsoption_api_case_create(void)
662 {
663 	TCase *tc;
664 	tc = tcase_create("API checks");
665 
666 	tcase_add_test(tc, nsoption_api_fini_no_init_test);
667 	tcase_add_test(tc, nsoption_api_read_no_path_test);
668 	tcase_add_test(tc, nsoption_api_read_no_init_test);
669 	tcase_add_test(tc, nsoption_api_write_no_path_test);
670 	tcase_add_test(tc, nsoption_api_write_no_init_test);
671 	tcase_add_test(tc, nsoption_api_dump_no_path_test);
672 	tcase_add_test(tc, nsoption_api_dump_no_init_test);
673 	tcase_add_test(tc, nsoption_api_commandline_no_args_test);
674 	tcase_add_test(tc, nsoption_api_commandline_no_init_test);
675 	tcase_add_test(tc, nsoption_api_init_def_test);
676 	tcase_add_test(tc, nsoption_api_fini_twice_test);
677 	tcase_add_test(tc, nsoption_api_init_param_test);
678 	tcase_add_test(tc, nsoption_api_init_failcb_test);
679 	tcase_add_test(tc, nsoption_api_snoptionf_no_init_test);
680 	tcase_add_test(tc, nsoption_api_snoptionf_badfmt_test);
681 	tcase_add_test(tc, nsoption_api_snoptionf_param_test);
682 
683 	return tc;
684 }
685 
686 
nsoption_suite_create(void)687 static Suite *nsoption_suite_create(void)
688 {
689 	Suite *s;
690 	s = suite_create("User options");
691 
692 	suite_add_tcase(s, nsoption_api_case_create());
693 	suite_add_tcase(s, nsoption_case_create());
694 	suite_add_tcase(s, nsoption_format_case_create());
695 	suite_add_tcase(s, nsoption_session_case_create());
696 
697 	return s;
698 }
699 
main(int argc,char ** argv)700 int main(int argc, char **argv)
701 {
702 	int number_failed;
703 	SRunner *sr;
704 
705 	sr = srunner_create(nsoption_suite_create());
706 
707 	srunner_run_all(sr, CK_ENV);
708 
709 	number_failed = srunner_ntests_failed(sr);
710 	srunner_free(sr);
711 
712 	return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
713 }
714