diff options
Diffstat (limited to 'test')
-rw-r--r-- | test/Makefile | 9 | ||||
-rw-r--r-- | test/assert.c | 19 | ||||
-rw-r--r-- | test/corestrings.c | 12 | ||||
-rw-r--r-- | test/data/Choices | 5 | ||||
-rw-r--r-- | test/data/Choices-all | 11 | ||||
-rw-r--r-- | test/hashmap.c | 571 | ||||
-rw-r--r-- | test/js/class-list.html | 29 | ||||
-rw-r--r-- | test/js/index.html | 3 | ||||
-rw-r--r-- | test/js/life.html | 175 | ||||
-rw-r--r-- | test/js/mandelbrot.html | 31 | ||||
-rw-r--r-- | test/messages.c | 3 | ||||
-rwxr-xr-x | test/monkey-see-monkey-do | 139 | ||||
-rw-r--r-- | test/monkey-tests/start-stop-no-js.yaml | 5 | ||||
-rw-r--r-- | test/monkey-tests/state-test.yaml | 69 | ||||
-rwxr-xr-x | test/monkey_driver.py | 171 | ||||
-rw-r--r-- | test/monkeyfarmer.py | 120 | ||||
-rw-r--r-- | test/nsoption.c | 10 | ||||
-rw-r--r-- | test/utils.c | 33 |
18 files changed, 1194 insertions, 221 deletions
diff --git a/test/Makefile b/test/Makefile index e92ece75d..82ffee6fa 100644 --- a/test/Makefile +++ b/test/Makefile @@ -7,6 +7,7 @@ TESTS := \ nsoption \ bloom \ hashtable \ + hashmap \ urlescape \ utils \ messages \ @@ -52,6 +53,10 @@ bloom_SRCS := utils/bloom.c test/bloom.c # hash table test sources hashtable_SRCS := utils/hashtable.c test/log.c test/hashtable.c +# hashmap test sources +hashmap_SRCS := $(NSURL_SOURCES) utils/hashmap.c utils/corestrings.c test/log.c test/hashmap.c +hashmap_LD := -lmalloc_fig + # url escape test sources urlescape_SRCS := utils/url.c test/log.c test/urlescape.c @@ -143,6 +148,8 @@ BASE_TESTCFLAGS := -std=c99 -g \ -Dnsgtk \ -DNETSURF_BUILTIN_LOG_FILTER=\"level:WARNING\" \ -DNETSURF_BUILTIN_VERBOSE_FILTER=\"level:DEBUG\" \ + -DTESTROOT=\"$(TESTROOT)\" \ + -DWITH_UTF8PROC \ $(SAN_FLAGS) \ $(shell pkg-config --cflags libcurl libparserutils libwapcaplet libdom libnsutils libutf8proc) \ $(LIB_CFLAGS) @@ -158,7 +165,7 @@ TESTLDFLAGS := -L$(TESTROOT) \ # malloc faliure injection generator $(TESTROOT)/libmalloc_fig.so:test/malloc_fig.c - $(CC) -shared -fPIC -I. -std=c99 $(TEST_WARNFLAGS) $^ -o $@ + $(CC) -shared -fPIC -I. -std=c99 $(TEST_WARNFLAGS) $^ -ldl -o $@ # Source files for all tests being compiled TESTSOURCES := diff --git a/test/assert.c b/test/assert.c index d21926e5e..fb4db8cc9 100644 --- a/test/assert.c +++ b/test/assert.c @@ -30,7 +30,23 @@ __ns_assert_fail(const char *__assertion, const char *__file, unsigned int __line, const char *__function) __THROW __attribute__ ((__noreturn__)); -/* We use this to flush coverage data */ +#if __GNUC__ > 10 + +/* We use this to dump coverage data in gcc 11 and later */ +extern void __gcov_dump(void); + +/* And here's our entry point */ +void +__ns_assert_fail(const char *__assertion, const char *__file, + unsigned int __line, const char *__function) +{ + __gcov_dump(); + __assert_fail(__assertion, __file, __line, __function); +} + +#else + +/* We use this to flush coverage data before gcc 11 */ extern void __gcov_flush(void); /* And here's our entry point */ @@ -41,3 +57,4 @@ __ns_assert_fail(const char *__assertion, const char *__file, __gcov_flush(); __assert_fail(__assertion, __file, __line, __function); } +#endif diff --git a/test/corestrings.c b/test/corestrings.c index 02640c953..c3c4e93eb 100644 --- a/test/corestrings.c +++ b/test/corestrings.c @@ -40,7 +40,7 @@ * * This is used to test all the out of memory paths in initialisation. */ -#define CORESTRING_TEST_COUNT 435 +#define CORESTRING_TEST_COUNT 488 START_TEST(corestrings_test) { @@ -53,8 +53,12 @@ START_TEST(corestrings_test) res = corestrings_fini(); malloc_limit(UINT_MAX); - - ck_assert_int_eq(ires, NSERROR_NOMEM); + + if (_i < CORESTRING_TEST_COUNT) { + ck_assert_int_eq(ires, NSERROR_NOMEM); + } else { + ck_assert_int_eq(ires, NSERROR_OK); + } ck_assert_int_eq(res, NSERROR_OK); } END_TEST @@ -65,7 +69,7 @@ static TCase *corestrings_case_create(void) TCase *tc; tc = tcase_create("corestrings"); - tcase_add_loop_test(tc, corestrings_test, 0, CORESTRING_TEST_COUNT); + tcase_add_loop_test(tc, corestrings_test, 0, CORESTRING_TEST_COUNT + 1); return tc; } diff --git a/test/data/Choices b/test/data/Choices index bd946f77b..511ecf87e 100644 --- a/test/data/Choices +++ b/test/data/Choices @@ -30,7 +30,6 @@ disc_cache_size:1073741824 disc_cache_age:28 block_advertisements:0 do_not_track:0 -minimum_gif_delay:10 send_referer:1 foreground_images:1 background_images:1 @@ -50,8 +49,6 @@ window_x:0 window_y:0 window_width:0 window_height:0 -window_screen_width:0 -window_screen_height:0 toolbar_status_size:6667 scale:100 incremental_reflow:1 @@ -101,7 +98,6 @@ sys_colour_ThreeDShadow:000000 sys_colour_Window:000000 sys_colour_WindowFrame:000000 sys_colour_WindowText:000000 -render_resample:1 downloads_clear:0 request_overwrite:1 downloads_directory:/home/vince @@ -109,7 +105,6 @@ url_file:/home/vince/.netsurf/URLs show_single_tab:1 button_type:1 disable_popups:0 -disable_plugins:0 history_age:0 hover_urls:0 focus_new:0 diff --git a/test/data/Choices-all b/test/data/Choices-all index d16c67568..5c26f2887 100644 --- a/test/data/Choices-all +++ b/test/data/Choices-all @@ -16,16 +16,17 @@ font_fantasy:Serif accept_language:en accept_charset: memory_cache_size:12582912 +disc_cache_path: disc_cache_size:1073741824 disc_cache_age:28 block_advertisements:0 do_not_track:0 -minimum_gif_delay:10 send_referer:1 foreground_images:1 background_images:1 animate_images:1 enable_javascript:1 +author_level_css:1 script_timeout:10 expire_url:28 font_default:0 @@ -41,8 +42,6 @@ window_x:0 window_y:0 window_width:0 window_height:0 -window_screen_width:0 -window_screen_height:0 toolbar_status_size:6667 scale:100 incremental_reflow:1 @@ -67,6 +66,7 @@ remove_backgrounds:0 enable_loosening:1 enable_PDF_compression:1 enable_PDF_password:0 +prefer_dark_mode:0 sys_colour_ActiveBorder:d3d3d3 sys_colour_ActiveCaption:f1f1f1 sys_colour_AppWorkspace:f1f1f1 @@ -97,7 +97,6 @@ sys_colour_WindowFrame:4e4e4e sys_colour_WindowText:000000 log_filter:level:WARNING verbose_filter:level:DEBUG -render_resample:1 downloads_clear:0 request_overwrite:1 downloads_directory:/home/vince @@ -105,7 +104,6 @@ url_file:/home/vince/.netsurf/URLs show_single_tab:1 button_type:1 disable_popups:0 -disable_plugins:0 history_age:0 hover_urls:0 focus_new:0 @@ -113,4 +111,5 @@ new_blank:0 hotlist_path:/home/vince/.netsurf/Hotlist developer_view:0 position_tab:0 -toolbar_order: +toolbar_items: +bar_show: diff --git a/test/hashmap.c b/test/hashmap.c new file mode 100644 index 000000000..ed951e9a7 --- /dev/null +++ b/test/hashmap.c @@ -0,0 +1,571 @@ +/* + * Copyright 2020 Daniel Silverstone <dsilvers@netsurf-browser.org> + * Copyright 2016 Vincent Sanders <vince@netsurf-browser.org> + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * NetSurf is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/** + * \file + * Tests for hashmap. + * + * In part, borrows from the corestrings tests + */ + +#include "utils/config.h" + +#include <assert.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <check.h> +#include <limits.h> + +#include <libwapcaplet/libwapcaplet.h> + +#include "utils/nsurl.h" +#include "utils/corestrings.h" +#include "utils/hashmap.h" + +#include "test/malloc_fig.h" + +/* Low level fixtures */ + +static void +corestring_create(void) +{ + ck_assert(corestrings_init() == NSERROR_OK); +} + +/** + * iterator for any remaining strings in teardown fixture + */ +static void +netsurf_lwc_iterator(lwc_string *str, void *pw) +{ + fprintf(stderr, + "[%3u] %.*s", + str->refcnt, + (int)lwc_string_length(str), + lwc_string_data(str)); +} + +static void +corestring_teardown(void) +{ + corestrings_fini(); + + lwc_iterate_strings(netsurf_lwc_iterator, NULL); +} + +/* Infra */ + +static ssize_t keys; +static ssize_t values; + +typedef struct { + nsurl *key; +} hashmap_test_value_t; + +static void * +key_clone(void *key) +{ + /* Pretend cloning costs memory so that it can fail for + * testing error return pathways + */ + void *temp = malloc(1); + if (temp == NULL) return NULL; + free(temp); + /* In reality we just ref the nsurl */ + keys++; + return nsurl_ref((nsurl *)key); +} + +static void +key_destroy(void *key) +{ + keys--; + nsurl_unref((nsurl *)key); +} + +static uint32_t +key_hash(void *key) +{ + /* Deliberately bad hash. + * returns 0, 1, 2, or 3 to force bucket chaining + */ + return nsurl_hash((nsurl *)key) & 3; +} + +static bool +key_eq(void *key1, void *key2) +{ + return nsurl_compare((nsurl *)key1, (nsurl*)key2, NSURL_COMPLETE); +} + +static void * +value_alloc(void *key) +{ + hashmap_test_value_t *ret = malloc(sizeof(hashmap_test_value_t)); + + if (ret == NULL) + return NULL; + + ret->key = (nsurl *)key; + + values++; + + return ret; +} + +static void +value_destroy(void *value) +{ + hashmap_test_value_t *val = value; + + /* Do nothing for now */ + + free(val); + values--; +} + +static hashmap_parameters_t test_params = { + .key_clone = key_clone, + .key_hash = key_hash, + .key_eq = key_eq, + .key_destroy = key_destroy, + .value_alloc = value_alloc, + .value_destroy = value_destroy, +}; + +/* Iteration helpers */ + +static size_t iteration_counter = 0; +static size_t iteration_stop = 0; +static char iteration_ctx = 0; + +static bool +hashmap_test_iterator_cb(void *key, void *value, void *ctx) +{ + ck_assert(ctx == &iteration_ctx); + iteration_counter++; + return iteration_counter == iteration_stop; +} + +/* Fixtures for basic tests */ + +static hashmap_t *test_hashmap = NULL; + +static void +basic_fixture_create(void) +{ + corestring_create(); + + test_hashmap = hashmap_create(&test_params); + + ck_assert(test_hashmap != NULL); + ck_assert_int_eq(keys, 0); + ck_assert_int_eq(values, 0); +} + +static void +basic_fixture_teardown(void) +{ + hashmap_destroy(test_hashmap); + test_hashmap = NULL; + + ck_assert_int_eq(keys, 0); + ck_assert_int_eq(values, 0); + + corestring_teardown(); +} + +/* basic api tests */ + +START_TEST(empty_hashmap_create_destroy) +{ + ck_assert_int_eq(hashmap_count(test_hashmap), 0); +} +END_TEST + +START_TEST(check_not_present) +{ + /* We're checking for a key which should not be present */ + ck_assert(hashmap_lookup(test_hashmap, corestring_nsurl_about_blank) == NULL); +} +END_TEST + +START_TEST(insert_works) +{ + hashmap_test_value_t *value = hashmap_insert(test_hashmap, corestring_nsurl_about_blank); + ck_assert(value != NULL); + ck_assert(value->key == corestring_nsurl_about_blank); + ck_assert_int_eq(hashmap_count(test_hashmap), 1); +} +END_TEST + +START_TEST(remove_not_present) +{ + ck_assert(hashmap_remove(test_hashmap, corestring_nsurl_about_blank) == false); +} +END_TEST + +START_TEST(insert_then_remove) +{ + hashmap_test_value_t *value = hashmap_insert(test_hashmap, corestring_nsurl_about_blank); + ck_assert(value != NULL); + ck_assert(value->key == corestring_nsurl_about_blank); + ck_assert_int_eq(keys, 1); + ck_assert_int_eq(values, 1); + ck_assert_int_eq(hashmap_count(test_hashmap), 1); + ck_assert(hashmap_remove(test_hashmap, corestring_nsurl_about_blank) == true); + ck_assert_int_eq(keys, 0); + ck_assert_int_eq(values, 0); + ck_assert_int_eq(hashmap_count(test_hashmap), 0); +} +END_TEST + +START_TEST(insert_then_lookup) +{ + hashmap_test_value_t *value = hashmap_insert(test_hashmap, corestring_nsurl_about_blank); + ck_assert(value != NULL); + ck_assert(value->key == corestring_nsurl_about_blank); + ck_assert(hashmap_lookup(test_hashmap, corestring_nsurl_about_blank) == value); +} +END_TEST + +START_TEST(iterate_empty) +{ + iteration_stop = iteration_counter = 0; + ck_assert(hashmap_iterate(test_hashmap, hashmap_test_iterator_cb, &iteration_ctx) == false); + ck_assert_int_eq(iteration_counter, 0); +} +END_TEST + +START_TEST(iterate_one) +{ + iteration_stop = iteration_counter = 0; + hashmap_test_value_t *value = hashmap_insert(test_hashmap, corestring_nsurl_about_blank); + ck_assert(value != NULL); + ck_assert(hashmap_iterate(test_hashmap, hashmap_test_iterator_cb, &iteration_ctx) == false); + ck_assert_int_eq(iteration_counter, 1); +} +END_TEST + +START_TEST(iterate_one_and_stop) +{ + iteration_stop = 1; + iteration_counter = 0; + hashmap_test_value_t *value = hashmap_insert(test_hashmap, corestring_nsurl_about_blank); + ck_assert(value != NULL); + ck_assert(hashmap_iterate(test_hashmap, hashmap_test_iterator_cb, &iteration_ctx) == true); + ck_assert_int_eq(iteration_counter, 1); +} +END_TEST + +static TCase *basic_api_case_create(void) +{ + TCase *tc; + tc = tcase_create("Basic API"); + + tcase_add_unchecked_fixture(tc, + basic_fixture_create, + basic_fixture_teardown); + + tcase_add_test(tc, empty_hashmap_create_destroy); + tcase_add_test(tc, check_not_present); + tcase_add_test(tc, insert_works); + tcase_add_test(tc, remove_not_present); + tcase_add_test(tc, insert_then_remove); + tcase_add_test(tc, insert_then_lookup); + + tcase_add_test(tc, iterate_empty); + tcase_add_test(tc, iterate_one); + tcase_add_test(tc, iterate_one_and_stop); + + return tc; +} + +/* Chain verification test suite */ + +typedef struct { + const char *url; + nsurl *nsurl; +} case_pair; + +/* The hobbled hash has only 4 values + * By having at least 12 test cases, we can be confident that + * at worst they'll all be on one chain, but at best there'll + * be four chains of 3 entries which means we should be able + * to validate prevptr and next in all cases. + */ +static case_pair chain_pairs[] = { + { "https://www.google.com/", NULL }, + { "https://www.google.co.uk/", NULL }, + { "https://www.netsurf-browser.org/", NULL }, + { "http://www.google.com/", NULL }, + { "http://www.google.co.uk/", NULL }, + { "http://www.netsurf-browser.org/", NULL }, + { "file:///tmp/test.html", NULL }, + { "file:///tmp/inner.html", NULL }, + { "about:blank", NULL }, + { "about:welcome", NULL }, + { "about:testament", NULL }, + { "resources:default.css", NULL }, + { NULL, NULL } +}; + +static void +chain_fixture_create(void) +{ + case_pair *chain_case = chain_pairs; + basic_fixture_create(); + + while (chain_case->url != NULL) { + ck_assert(nsurl_create(chain_case->url, &chain_case->nsurl) == NSERROR_OK); + chain_case++; + } + +} + +static void +chain_fixture_teardown(void) +{ + case_pair *chain_case = chain_pairs; + + while (chain_case->url != NULL) { + nsurl_unref(chain_case->nsurl); + chain_case->nsurl = NULL; + chain_case++; + } + + basic_fixture_teardown(); +} + +START_TEST(chain_add_remove_all) +{ + case_pair *chain_case; + + for (chain_case = chain_pairs; + chain_case->url != NULL; + chain_case++) { + ck_assert(hashmap_lookup(test_hashmap, chain_case->nsurl) == NULL); + ck_assert(hashmap_insert(test_hashmap, chain_case->nsurl) != NULL); + ck_assert(hashmap_lookup(test_hashmap, chain_case->nsurl) != NULL); + ck_assert(hashmap_remove(test_hashmap, chain_case->nsurl) == true); + } + + ck_assert_int_eq(keys, 0); + ck_assert_int_eq(values, 0); +} +END_TEST + +START_TEST(chain_add_all_remove_all) +{ + case_pair *chain_case; + + for (chain_case = chain_pairs; + chain_case->url != NULL; + chain_case++) { + ck_assert(hashmap_lookup(test_hashmap, chain_case->nsurl) == NULL); + ck_assert(hashmap_insert(test_hashmap, chain_case->nsurl) != NULL); + } + + for (chain_case = chain_pairs; + chain_case->url != NULL; + chain_case++) { + ck_assert(hashmap_remove(test_hashmap, chain_case->nsurl) == true); + } + + ck_assert_int_eq(keys, 0); + ck_assert_int_eq(values, 0); +} +END_TEST + +START_TEST(chain_add_all_twice_remove_all) +{ + case_pair *chain_case; + + for (chain_case = chain_pairs; + chain_case->url != NULL; + chain_case++) { + ck_assert(hashmap_lookup(test_hashmap, chain_case->nsurl) == NULL); + ck_assert(hashmap_insert(test_hashmap, chain_case->nsurl) != NULL); + } + + for (chain_case = chain_pairs; + chain_case->url != NULL; + chain_case++) { + ck_assert(hashmap_lookup(test_hashmap, chain_case->nsurl) != NULL); + ck_assert(hashmap_insert(test_hashmap, chain_case->nsurl) != NULL); + } + + for (chain_case = chain_pairs; + chain_case->url != NULL; + chain_case++) { + ck_assert(hashmap_remove(test_hashmap, chain_case->nsurl) == true); + } + + ck_assert_int_eq(keys, 0); + ck_assert_int_eq(values, 0); +} +END_TEST + +START_TEST(chain_add_all_twice_remove_all_iterate) +{ + case_pair *chain_case; + size_t chain_count = 0; + + for (chain_case = chain_pairs; + chain_case->url != NULL; + chain_case++) { + ck_assert(hashmap_lookup(test_hashmap, chain_case->nsurl) == NULL); + ck_assert(hashmap_insert(test_hashmap, chain_case->nsurl) != NULL); + chain_count++; + } + + iteration_counter = 0; + iteration_stop = 0; + ck_assert(hashmap_iterate(test_hashmap, hashmap_test_iterator_cb, &iteration_ctx) == false); + ck_assert_int_eq(iteration_counter, chain_count); + + for (chain_case = chain_pairs; + chain_case->url != NULL; + chain_case++) { + ck_assert(hashmap_lookup(test_hashmap, chain_case->nsurl) != NULL); + ck_assert(hashmap_insert(test_hashmap, chain_case->nsurl) != NULL); + } + + iteration_counter = 0; + iteration_stop = 0; + ck_assert(hashmap_iterate(test_hashmap, hashmap_test_iterator_cb, &iteration_ctx) == false); + ck_assert_int_eq(iteration_counter, chain_count); + ck_assert_int_eq(hashmap_count(test_hashmap), chain_count); + + iteration_counter = 0; + iteration_stop = chain_count; + ck_assert(hashmap_iterate(test_hashmap, hashmap_test_iterator_cb, &iteration_ctx) == true); + ck_assert_int_eq(iteration_counter, chain_count); + + for (chain_case = chain_pairs; + chain_case->url != NULL; + chain_case++) { + ck_assert(hashmap_remove(test_hashmap, chain_case->nsurl) == true); + } + + iteration_counter = 0; + iteration_stop = chain_count; + ck_assert(hashmap_iterate(test_hashmap, hashmap_test_iterator_cb, &iteration_ctx) == false); + ck_assert_int_eq(iteration_counter, 0); + + ck_assert_int_eq(keys, 0); + ck_assert_int_eq(values, 0); + ck_assert_int_eq(hashmap_count(test_hashmap), 0); +} +END_TEST + +#define CHAIN_TEST_MALLOC_COUNT_MAX 60 + +START_TEST(chain_add_all_remove_all_alloc) +{ + bool failed = false; + case_pair *chain_case; + + malloc_limit(_i); + + for (chain_case = chain_pairs; + chain_case->url != NULL; + chain_case++) { + if (hashmap_insert(test_hashmap, chain_case->nsurl) == NULL) { + failed = true; + } + } + + for (chain_case = chain_pairs; + chain_case->url != NULL; + chain_case++) { + if (hashmap_insert(test_hashmap, chain_case->nsurl) == NULL) { + failed = true; + } + } + + for (chain_case = chain_pairs; + chain_case->url != NULL; + chain_case++) { + hashmap_remove(test_hashmap, chain_case->nsurl); + } + + malloc_limit(UINT_MAX); + + ck_assert_int_eq(keys, 0); + ck_assert_int_eq(values, 0); + + if (_i < CHAIN_TEST_MALLOC_COUNT_MAX) { + ck_assert(failed); + } else { + ck_assert(!failed); + } + +} +END_TEST + +static TCase *chain_case_create(void) +{ + TCase *tc; + tc = tcase_create("Bucket Chain tests"); + + tcase_add_unchecked_fixture(tc, + chain_fixture_create, + chain_fixture_teardown); + + tcase_add_test(tc, chain_add_remove_all); + tcase_add_test(tc, chain_add_all_remove_all); + tcase_add_test(tc, chain_add_all_twice_remove_all); + tcase_add_test(tc, chain_add_all_twice_remove_all_iterate); + + tcase_add_loop_test(tc, chain_add_all_remove_all_alloc, 0, CHAIN_TEST_MALLOC_COUNT_MAX + 1); + + return tc; +} + +/* + * hashmap test suite creation + */ +static Suite *hashmap_suite_create(void) +{ + Suite *s; + s = suite_create("Hashmap"); + + suite_add_tcase(s, basic_api_case_create()); + suite_add_tcase(s, chain_case_create()); + + return s; +} + +int main(int argc, char **argv) +{ + int number_failed; + SRunner *sr; + + sr = srunner_create(hashmap_suite_create()); + + srunner_run_all(sr, CK_ENV); + + number_failed = srunner_ntests_failed(sr); + srunner_free(sr); + + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} diff --git a/test/js/class-list.html b/test/js/class-list.html new file mode 100644 index 000000000..4c73283e5 --- /dev/null +++ b/test/js/class-list.html @@ -0,0 +1,29 @@ +<html> + <head> + <title>Class List (and other token lists?)</title> + <style> + .bad { background-color: red; } + .ok { background-color: green; } + </style> + </head> + <body> + <h1>This is a set of demonstrators for the token list Element.classList</h1> + <h2>This first is taken from the MDN for DOMTokenList</h2> + <span id="demo1" class=" d d e f bad"></span> + <script> + var span = document.getElementById("demo1"); + var classes = span.classList; + classes.add("x", "d", "g"); + classes.remove("e", "g"); + classes.toggle("d"); // Toggles d off + classes.toggle("q", false); // Forces q off (won't be present) + classes.toggle("d"); // Toggles d on + classes.toggle("d", true); // Forces d on (won't toggle it off again) + if (classes.contains("d")) { + classes.add("ok") + classes.remove("bad") + span.textContent = "span classList is \"" + classes + '"'; + } + </script> + </body> +</html> diff --git a/test/js/index.html b/test/js/index.html index 2abe954e5..6d2c6541e 100644 --- a/test/js/index.html +++ b/test/js/index.html @@ -104,6 +104,9 @@ <li><a href="assorted-log-doc-write.html">console.log and document.write</a></li> <li><a href="wikipedia-lcm.html">Example from wikipedia</a></li> <li><a href="verify-instanceofness.html">Check instanceof behaviour</a></li> +<li><a href="class-list.html">Class list (and other token lists?)</a></li> +<li><a href="mandelbrot.html">Canvas/ImageData Mandelbrot ploter</a></li> +<li><a href="life.html">Game of Life</a></li> </ul> </body> diff --git a/test/js/life.html b/test/js/life.html new file mode 100644 index 000000000..de54d0aae --- /dev/null +++ b/test/js/life.html @@ -0,0 +1,175 @@ +<html> + <head> + <meta charset="UTF-8" /> + <title>Conway's Game of Life</title> + <link rel="stylesheet" type="text/css" href="resource:internal.css" /> + <style> + canvas#surface { + width: 50vmin; + height: 50vmin; + border: 2px solid black; + } + </style> + </head> + <body class="ns-even-bg ns-even-fg ns-border"> + <h1 class="ns-border">Conway's Game of Life</h1> + <div style="margin: 1em;"> + <div> + Run: <input id="running" type="checkbox" checked/><br /> + Set Size: <input id="width" type="text" size="4" value="50" /> x + <input id="height" type="text" size="4" value="50" /> + <button id="commitsize">Commit</button><br /> + </div> + <div> + <canvas id="surface" width="50" height="50"> + Sorry, you can't play Game of Life if JavaScript is turned off + </canvas> + </div> + <div> + <button id="random">Randomise</button> + </div> + </div> + </body> + <script> + (function () { + const running = document.getElementById("running"); + const iwidth = document.getElementById("width"); + const iheight = document.getElementById("height"); + const surface = document.getElementById("surface"); + const context = surface.getContext("2d"); + var width = surface.width - 10; + var height = surface.height - 10; + var frame = context.createImageData(width, height); + var drawto = context.createImageData(width, height); + var greyto = context.createImageData(width, height); + const greylevel = 31; + + function getOffset(x, y) { + if (x < 0) { + x = width + x; + } + if (y < 0) { + y = height + y; + } + if (x >= width) { + x = x - width; + } + if (y >= height) { + y = y - height; + } + return (y * width + x) * 4; + } + function getCell(x, y) { + const offset = getOffset(x, y); + return frame.data[offset + 3] != 0; + } + function setCell(x, y) { + const offset = getOffset(x, y); + drawto.data[offset + 3] = 255; + greyto.data[offset + 3] = greylevel; + } + function clearCell(x, y) { + const offset = getOffset(x, y); + drawto.data[offset + 3] = 0; + greyto.data[offset + 3] = 0; + } + function countNeighbours(x, y) { + return ( + getCell(x - 1, y - 1) + + getCell(x, y - 1) + + getCell(x + 1, y - 1) + + getCell(x - 1, y) + + getCell(x + 1, y) + + getCell(x - 1, y + 1) + + getCell(x, y + 1) + + getCell(x + 1, y + 1) + ); + } + function flip() { + var temp = frame; + context.putImageData(drawto, 5, 5); + context.putImageData(greyto, 5 - width, 5 - height); /* top left */ + context.putImageData(greyto, 5 - width, 5); /* left */ + context.putImageData(greyto, 5, 5 - height); /* top */ + context.putImageData(greyto, 5 + width, 5 + height); /* bottom right */ + context.putImageData(greyto, 5 + width, 5); /* right */ + context.putImageData(greyto, 5, 5 + height); /* bottom */ + context.putImageData(greyto, 5 + width, 5 - height); /* top right */ + context.putImageData(greyto, 5 - width, 5 + height); /* bottom left */ + frame = drawto; + drawto = temp; + } + /* Game of life is run on a timer */ + setInterval(function () { + if (!running.checked) { + return; + } + console.log("Frame"); + /* To do a frame of GoL we compute by consuming frame and writing to drawto */ + for (var y = 0; y < height; y++) { + for (var x = 0; x < width; x++) { + const neighbours = countNeighbours(x, y); + if (getCell(x, y)) { + if (neighbours == 2 || neighbours == 3) { + setCell(x, y); // live, 2/3 neigh => stay alive + } else { + clearCell(x, y); // live, <2/>3 neigh => dies + } + } else { + if (neighbours == 3) { + setCell(x, y); // dead, 3 neigh => born + } else { + clearCell(x, y); // dead, !3 neigh => stay dead + } + } + } + } + flip(); + }, 100); + const randomise = function () { + var ofs = 3; + for (var y = 0; y < height; y++) { + for (var x = 0; x < width; x++) { + if (Math.random() < 0.5) { + drawto.data[ofs] = 0; + } else { + drawto.data[ofs] = 255; + greyto.data[ofs] = greylevel; + } + ofs += 4; + } + } + flip(); + }; + document.getElementById("random").addEventListener("click", randomise); + document + .getElementById("commitsize") + .addEventListener("click", function () { + const iwval = parseInt(iwidth.value, 10); + const ihval = parseInt(iheight.value, 10); + console.log(width, height, "->", iwval, ihval); + if ( + (iwval != width || ihval != height) && + iwval >= 10 && + iwval <= 200 && + ihval >= 10 && + ihval <= 200 + ) { + console.log("yes"); + surface.height = ihval + 10; + context.height = ihval + 10; + height = ihval; + surface.width = iwval + 10; + context.width = iwval + 10; + width = iwval; + frame = context.createImageData(width, height); + drawto = context.createImageData(width, height); + greyto = context.createImageData(width, height); + resetGrey(); + randomise(); + } + }); + randomise(); + })(); + </script> +</html> diff --git a/test/js/mandelbrot.html b/test/js/mandelbrot.html new file mode 100644 index 000000000..38f77eff5 --- /dev/null +++ b/test/js/mandelbrot.html @@ -0,0 +1,31 @@ +<html> + <head> + <title>JS Mandelbrot</title> + <script src="https://nerget.com/mandelbrot.js"></script> + <script> + var drawn = false; + var dimension = 2; + var cx = -dimension / 2 + 0.5; + var cy = -dimension / 2; + + function log(msg) { + document.getElementById("log").innerHTML += msg + "<br/>"; + } + + function draw() { + var forceSlowPath = document.getElementById('forceSlowPath').checked; + drawMandelbrot(document.getElementById('canvas').getContext('2d'), 200, 200, + cx + dimension / 2, cy + dimension / 2, dimension, 500, forceSlowPath); + drawn = true; + } + + </script> + </head> + <body> + <canvas id="canvas" width="200" height="200" style="border: 1px solid black;"></canvas> + <br /> + <input id="forceSlowPath" type="checkbox">Use slow path.</input> <br /> + <a href="javascript:draw()">Start</a> + <div id="log"></div> + </body> +</html> diff --git a/test/messages.c b/test/messages.c index ae82d1ede..3ec770a56 100644 --- a/test/messages.c +++ b/test/messages.c @@ -118,8 +118,7 @@ START_TEST(message_get_buff_test) ck_assert_int_eq(res, NSERROR_OK); buf = messages_get_buff("DefinitelyNotAKey"); - ck_assert_str_eq(buf, "DefinitelyNotAKey"); - free(buf); + ck_assert(buf == NULL); buf = messages_get_buff("NoMemory"); ck_assert_str_eq(buf, "NetSurf is running out of memory. Please free some memory and try again."); diff --git a/test/monkey-see-monkey-do b/test/monkey-see-monkey-do index 8de29fa53..72b8685ec 100755 --- a/test/monkey-see-monkey-do +++ b/test/monkey-see-monkey-do @@ -1,26 +1,52 @@ #!/usr/bin/python3 -# If you have any poo, fling it now! +''' +NetSurf automated test runner -BASE_PATH="https://test.netsurf-browser.org/cgi-bin/monkey-index.cgi" -MONKEY_PATH="./nsmonkey" +This script retrives a test plan from the NetSurf infrastructure and + executes it using the monkey frontend +''' -# Otherwise let's begin... +# If you have any poo, fling it now! import sys import getopt -import yaml - import multiprocessing as mp - -from urllib import request +from urllib import request, parse from io import StringIO - +import yaml import monkey_driver as driver +# Otherwise let's begin... + +BASE_PATH = "https://test.netsurf-browser.org/cgi-bin/monkey-index.cgi" +MONKEY_PATH = "./nsmonkey" + mp.set_start_method('fork') -def child_run_test(parts): +def decode_trace_line(l): + from re import findall, match + from subprocess import getstatusoutput + + caps = findall(r'./nsmonkey\(\+(0x[0-9a-f]+)\)', l); + if not caps: + return l + + exitcode, output = getstatusoutput( + "addr2line -e {} -a -p -f -C {} 2>/dev/null".format( + MONKEY_PATH, caps[0])) + if exitcode != 0: + return './nsmonkey(+{})'.format(caps[0]) + + m = match(r'0x(.+): (.+) at (.+):(.+)', output) + + return '{}:{}({})[0x{}]'.format( + m.group(3), m.group(4), m.group(2), m.group(1)) + +def decode_trace(s): + return "\n".join(decode_trace_line(l) for l in s.split("\n")) + +def child_run_test(verbose, parts): outcapture = StringIO() errcapture = StringIO() oldout = sys.stdout @@ -34,29 +60,31 @@ def child_run_test(parts): sys.stderr = olderr print("FAIL:") print("STDOUT:\n{}\n".format(outcapture.getvalue())) - print("STDERR:\n{}\n".format(errcapture.getvalue())) + print("STDERR:\n{}\n".format(decode_trace(errcapture.getvalue()))) print("RERAISE:") raise else: sys.stdout = oldout sys.stderr = olderr - if verbose == True: + if verbose: print("STDOUT:\n{}\n".format(outcapture.getvalue())) -def run_test(parts): - p = mp.Process(target=child_run_test, args=(parts, )) +def run_test(verbose, parts): + p = mp.Process(target=child_run_test, args=(verbose, parts, )) p.start() p.join() return p.exitcode def print_usage(): print('Usage:') - print(' ' + sys.argv[0] + ' [-v] [-h]') + print(' ' + sys.argv[0] + ' [-v] [-h] [-d <division>] [-g group]') def parse_argv(argv): - verbose=False + verbose = False + division = None + group = None try: - opts, args = getopt.getopt(argv,"hv",[]) + opts, args = getopt.getopt(argv, "hvd:g:", []) except getopt.GetoptError: print_usage() sys.exit(2) @@ -65,32 +93,51 @@ def parse_argv(argv): print_usage() sys.exit() elif opt in ("-v", "--verbose"): - verbose=True - return verbose - -verbose = parse_argv(sys.argv[1:]) - -print("Fetching tests...") -index = request.urlopen(BASE_PATH) -index = index.read() -print("Parsing tests...") -test_set = yaml.load_all(index) - -print("Running tests...") -ret = 0 -for test in test_set: - if test["kind"] == 'group': - print("Start group: {}".format(test["group"])) - print(" [ {} ]".format(test["description"])) - elif test["kind"] == 'test': - print(" => Run test: {}".format(test["filename"])) - ret = run_test(test["content"]) - if ret != 0: - break - -if ret != 0: - print("FAIL") - sys.exit(1) -else: - print("PASS") - sys.exit(0) + verbose = True + elif opt == '-d': + division = arg + elif opt == '-g': + group = arg + + return verbose, division, group + +def main(): + verbose, division, group = parse_argv(sys.argv[1:]) + + print("Fetching tests...") + data_dict = {} + if division is not None: + data_dict['division'] = division + if group is not None: + data_dict['group'] = group + + data = parse.urlencode(data_dict).encode() + req = request.Request(BASE_PATH, data=data) + index = request.urlopen(req) + index = index.read() + + print("Parsing tests...") + test_set = yaml.load_all(index, Loader=yaml.SafeLoader) + + print("Running tests...") + ret = 0 + for test in test_set: + if test["kind"] == 'group': + print("Start group: {}".format(test["group"])) + print(" [ {} ]".format(test["description"])) + elif test["kind"] == 'test': + print(" => Run test: {}".format(test["filename"])) + ret = run_test(verbose, test["content"]) + if ret != 0: + break + + if ret != 0: + print("FAIL") + sys.exit(1) + else: + print("PASS") + sys.exit(0) + + +if __name__ == "__main__": + main() diff --git a/test/monkey-tests/start-stop-no-js.yaml b/test/monkey-tests/start-stop-no-js.yaml index 0a681cffc..028e08f8e 100644 --- a/test/monkey-tests/start-stop-no-js.yaml +++ b/test/monkey-tests/start-stop-no-js.yaml @@ -1,8 +1,7 @@ title: start and stop browser without JS -group: basic +group: initial steps: - action: launch - args: + options: - enable_javascript=0 - action: quit - diff --git a/test/monkey-tests/state-test.yaml b/test/monkey-tests/state-test.yaml new file mode 100644 index 000000000..6f25a78d4 --- /dev/null +++ b/test/monkey-tests/state-test.yaml @@ -0,0 +1,69 @@ +title: Page state info test +group: basic +steps: +- action: launch + language: en +- action: window-new + tag: win1 +- action: navigate + window: win1 + url: about:config +- action: block + conditions: + - window: win1 + status: complete +- action: page-info-state + window: win1 + match: INTERNAL +- action: navigate + window: win1 + url: file:/// +- action: block + conditions: + - window: win1 + status: complete +- action: page-info-state + window: win1 + match: LOCAL +- action: navigate + window: win1 + url: http://test.netsurf-browser.org/html/trivial-document.html +- action: block + conditions: + - window: win1 + status: complete +- action: page-info-state + window: win1 + match: INSECURE +- action: navigate + window: win1 + url: https://test.netsurf-browser.org/html/trivial-document.html +- action: block + conditions: + - window: win1 + status: complete +- action: page-info-state + window: win1 + match: SECURE +- action: navigate + window: win1 + url: https://test.netsurf-browser.org/html/trivial-document-with-png.html +- action: block + conditions: + - window: win1 + status: complete +- action: page-info-state + window: win1 + match: SECURE +- action: navigate + window: win1 + url: https://test.netsurf-browser.org/html/trivial-document-with-http-png.html +- action: block + conditions: + - window: win1 + status: complete +- action: page-info-state + window: win1 + match: SECURE_ISSUES +- action: quit + diff --git a/test/monkey_driver.py b/test/monkey_driver.py index 7f8a430c0..9b810d2a6 100755 --- a/test/monkey_driver.py +++ b/test/monkey_driver.py @@ -22,6 +22,7 @@ runs tests in monkey as defined in a yaml file # pylint: disable=locally-disabled, missing-docstring +import os import sys import getopt import time @@ -34,7 +35,6 @@ class DriverBrowser(Browser): def __init__(self, *args, **kwargs): super(DriverBrowser, self).__init__(*args, **kwargs) self.auth = [] - self.cert = [] def add_auth(self, url, realm, username, password): self.auth.append((url, realm, username, password)) @@ -86,46 +86,6 @@ class DriverBrowser(Browser): print("401: No candidate found, cancelling login box") logwin.destroy() - def add_cert(self, url): - # add sll certificate error exception - self.cert.append(url) - - def remove_cert(self, url): - keep = [] - - def matches(first, second): - if first is None or second is None: - return True - return first == second - - for iurl in self.cert: - if not matches(url, iurl): - keep.append(iurl) - self.cert = keep - - def handle_ready_sslcert(self, cwin): - - def matches(first, second): - if first is None or second is None: - return True - return first == second - - candidates = [] - for url in self.cert: - score = 0 - if matches(url, cwin.url): - score += 1 - if score > 0: - candidates.append((score, url)) - if candidates: - candidates.sort() - (score, url) = candidates[-1] - print("SSLCert: Found candidate {} with score {}".format(url, score)) - cwin.go() - else: - print("SSLCert: No candidate found, cancelling sslcert box") - cwin.destroy() - def print_usage(): print('Usage:') @@ -174,7 +134,7 @@ def load_test_plan(path): plan = [] with open(path, 'r') as stream: try: - plan = (yaml.load(stream)) + plan = (yaml.load(stream, Loader=yaml.CSafeLoader)) except Exception as exc: print(exc) return plan @@ -211,18 +171,20 @@ def conds_met(ctx, conds): elif 'window' in cond.keys(): status = cond['status'] window = cond['window'] - assert status == "complete" # TODO: Add more status support? + assert status == "complete" or status == "loading" # TODO: Add more status support? if window == "*all*": - # all windows must be not throbbing + # all windows must be complete, or any still loading throbbing = False for win in ctx['windows'].items(): if win[1].throbbing: throbbing = True - if not throbbing: + # throbbing and want loading => true + # not throbbing and want complete => true + if (status == "loading") == throbbing: return True else: win = ctx['windows'][window] - if win.throbbing is False: + if win.throbbing == (status == "loading"): return True else: raise AssertionError("Unknown condition: {}".format(repr(cond))) @@ -232,14 +194,35 @@ def conds_met(ctx, conds): def run_test_step_action_launch(ctx, step): print(get_indent(ctx) + "Action: " + step["action"]) + + # ensure browser is not already launched assert ctx.get('browser') is None assert ctx.get('windows') is None + + # build command line switches list + monkey_cmd = [ctx["monkey"]] + for option in step.get('launch-options', []): + monkey_cmd.append("--{}".format(option)) + print(get_indent(ctx) + " " + "Command line: " + repr(monkey_cmd)) + + # build command environment + monkey_env = os.environ.copy() + for envkey, envvalue in step.get('environment', {}).items(): + monkey_env[envkey] = envvalue + print(get_indent(ctx) + " " + envkey + "=" + envvalue) + if 'language' in step.keys(): + monkey_env['LANGUAGE'] = step['language'] + + # create browser object ctx['browser'] = DriverBrowser( - monkey_cmd=[ctx["monkey"]], + monkey_cmd=monkey_cmd, + monkey_env=monkey_env, quiet=True, wrapper=ctx.get("wrapper")) assert_browser(ctx) ctx['windows'] = dict() + + # set user options for option in step.get('options', []): print(get_indent(ctx) + " " + option) ctx['browser'].pass_options(option) @@ -265,7 +248,7 @@ def run_test_step_action_window_close(ctx, step): tag = step['window'] assert ctx['windows'].get(tag) is not None win = ctx['windows'].pop(tag) - timeout = int(step.get('timeout', 5)) + timeout = int(step.get('timeout', 30)) win.kill() win.wait_until_dead(timeout=timeout) assert not win.alive @@ -311,7 +294,7 @@ def run_test_step_action_reload(ctx, step): def run_test_step_action_sleep_ms(ctx, step): print(get_indent(ctx) + "Action: " + step["action"]) - conds = step['conditions'] + conds = step.get('conditions', {}) sleep_time = step['time'] sleep = 0 have_repeat = False @@ -352,32 +335,50 @@ def run_test_step_action_repeat(ctx, step): print(get_indent(ctx) + "Action: " + step["action"]) tag = step['tag'] assert ctx['repeats'].get(tag) is None + # initialise the loop continue conditional ctx['repeats'][tag] = {"loop": True, } - if 'min' in step.keys(): - ctx['repeats'][tag]["i"] = step["min"] - else: - ctx['repeats'][tag]["i"] = 0 - - if 'step' in step.keys(): - ctx['repeats'][tag]["step"] = step["step"] - else: - ctx['repeats'][tag]["step"] = 1 - if 'values' in step.keys(): + # value iterator ctx['repeats'][tag]['values'] = step["values"] + ctx['repeats'][tag]["max"] = len(step["values"]) + ctx['repeats'][tag]["i"] = 0 + ctx['repeats'][tag]["step"] = 1 else: + # numeric iterator ctx['repeats'][tag]['values'] = None + if 'min' in step.keys(): + ctx['repeats'][tag]["i"] = step["min"] + else: + ctx['repeats'][tag]["i"] = 0 + + if 'step' in step.keys(): + ctx['repeats'][tag]["step"] = step["step"] + else: + ctx['repeats'][tag]["step"] = 1 + + if 'max' in step.keys(): + ctx['repeats'][tag]["max"] = step["max"] + else: + ctx['repeats'][tag]["max"] = None + while ctx['repeats'][tag]["loop"]: ctx['repeats'][tag]["start"] = time.time() ctx["depth"] += 1 + + # run through steps for this iteration for stp in step["steps"]: run_test_step(ctx, stp) + + # increment iterator ctx['repeats'][tag]["i"] += ctx['repeats'][tag]["step"] - if ctx['repeats'][tag]['values'] is not None: - if ctx['repeats'][tag]["i"] >= len(ctx['repeats'][tag]['values']): + + # check for end condition + if ctx['repeats'][tag]["max"] is not None: + if ctx['repeats'][tag]["i"] >= ctx['repeats'][tag]["max"]: ctx['repeats'][tag]["loop"] = False + ctx["depth"] -= 1 @@ -435,13 +436,26 @@ def run_test_step_action_plot_check(ctx, step): print(get_indent(ctx) + "Action: " + step["action"]) assert_browser(ctx) win = ctx['windows'][step['window']] + + if 'area' in step.keys(): + if step["area"] == "extent": + # ought to capture the extent updates and use that, instead use a + # big area and have the browser clip it + area=["0","0","1000","1000000"] + else: + area = [step["area"]] + else: + area = None + + # get the list of checks if 'checks' in step.keys(): checks = step['checks'] else: checks = {} + all_text_list = [] bitmaps = [] - for plot in win.redraw(): + for plot in win.redraw(coords=area): if plot[0] == 'TEXT': all_text_list.extend(plot[6:]) if plot[0] == 'BITMAP': @@ -536,23 +550,6 @@ def run_test_step_action_remove_auth(ctx, step): step.get("username"), step.get("password")) -def run_test_step_action_add_cert(ctx, step): - print(get_indent(ctx) + "Action:" + step["action"]) - assert_browser(ctx) - browser = ctx['browser'] - browser.add_cert(step.get("url")) - - -def run_test_step_action_remove_cert(ctx, step): - - # pylint: disable=locally-disabled, invalid-name - - print(get_indent(ctx) + "Action:" + step["action"]) - assert_browser(ctx) - browser = ctx['browser'] - browser.remove_cert(step.get("url")) - - def run_test_step_action_clear_log(ctx, step): print(get_indent(ctx) + "Action: " + step["action"]) assert_browser(ctx) @@ -588,11 +585,23 @@ def run_test_step_action_js_exec(ctx, step): win.js_exec(cmd) +def run_test_step_action_page_info_state(ctx, step): + print(get_indent(ctx) + "Action: " + step["action"]) + assert_browser(ctx) + tag = step['window'] + win = ctx['windows'].get(tag) + assert win is not None + match = step['match'] + assert win.page_info_state == match + + def run_test_step_action_quit(ctx, step): print(get_indent(ctx) + "Action: " + step["action"]) assert_browser(ctx) browser = ctx.pop('browser') assert browser.quit_and_wait() + # clean up context as all windows have gone away after browser quit + ctx.pop('windows') STEP_HANDLERS = { @@ -614,11 +623,11 @@ STEP_HANDLERS = { "wait-loading": run_test_step_action_wait_loading, "add-auth": run_test_step_action_add_auth, "remove-auth": run_test_step_action_remove_auth, - "add-cert": run_test_step_action_add_cert, - "remove-cert": run_test_step_action_remove_cert, "clear-log": run_test_step_action_clear_log, "wait-log": run_test_step_action_wait_log, "js-exec": run_test_step_action_js_exec, + "page-info-state": + run_test_step_action_page_info_state, "quit": run_test_step_action_quit, } diff --git a/test/monkeyfarmer.py b/test/monkeyfarmer.py index dcc32175f..905fd9a81 100644 --- a/test/monkeyfarmer.py +++ b/test/monkeyfarmer.py @@ -32,16 +32,58 @@ import socket import subprocess import time import errno +import sys + +class StderrEcho(asyncore.dispatcher): + def __init__(self, sockend): + asyncore.dispatcher.__init__(self, sock=sockend) + self.incoming = b"" + + def handle_connect(self): + pass + + def handle_close(self): + # the pipe to the monkey process has closed + self.close() + + def handle_read(self): + try: + got = self.recv(8192) + if not got: + return + except socket.error as error: + if error.errno == errno.EAGAIN or error.errno == errno.EWOULDBLOCK: + return + else: + raise + + self.incoming += got + if b"\n" in self.incoming: + lines = self.incoming.split(b"\n") + self.incoming = lines.pop() + for line in lines: + try: + line = line.decode('utf-8') + except UnicodeDecodeError: + print("WARNING: Unicode decode error") + line = line.decode('utf-8', 'replace') + + sys.stderr.write("{}\n".format(line)) + class MonkeyFarmer(asyncore.dispatcher): # pylint: disable=locally-disabled, too-many-instance-attributes - def __init__(self, monkey_cmd, online, quiet=False, *, wrapper=None): + def __init__(self, monkey_cmd, monkey_env, online, quiet=False, *, wrapper=None): (mine, monkeys) = socket.socketpair() asyncore.dispatcher.__init__(self, sock=mine) + (mine2, monkeyserr) = socket.socketpair() + + self._errwrapper = StderrEcho(mine2) + if wrapper is not None: new_cmd = list(wrapper) new_cmd.extend(monkey_cmd) @@ -49,11 +91,14 @@ class MonkeyFarmer(asyncore.dispatcher): self.monkey = subprocess.Popen( monkey_cmd, + env=monkey_env, stdin=monkeys, stdout=monkeys, - close_fds=[mine]) + stderr=monkeyserr, + close_fds=[mine, mine2]) monkeys.close() + monkeyserr.close() self.buffer = b"" self.incoming = b"" @@ -115,7 +160,11 @@ class MonkeyFarmer(asyncore.dispatcher): self.buffer += cmd.encode('utf-8') def monkey_says(self, line): - line = line.decode('utf-8') + try: + line = line.decode('utf-8') + except UnicodeDecodeError: + print("WARNING: Unicode decode error") + line = line.decode('utf-8', 'replace') if not self.quiet: print("<<< {}".format(line)) self.discussion.append(("<", line)) @@ -158,15 +207,15 @@ class Browser: # pylint: disable=locally-disabled, too-many-instance-attributes, dangerous-default-value, invalid-name - def __init__(self, monkey_cmd=["./nsmonkey"], quiet=False, *, wrapper=None): + def __init__(self, monkey_cmd=["./nsmonkey"], monkey_env=None, quiet=False, *, wrapper=None): self.farmer = MonkeyFarmer( monkey_cmd=monkey_cmd, + monkey_env=monkey_env, online=self.on_monkey_line, quiet=quiet, wrapper=wrapper) self.windows = {} self.logins = {} - self.sslcerts = {} self.current_draw_target = None self.started = False self.stopped = False @@ -238,18 +287,6 @@ class Browser: if win.alive and win.ready: self.handle_ready_login(win) - def handle_SSLCERT(self, action, _lwin, winid, *args): - if action == "VERIFY": - new_win = SSLCertWindow(self, winid, *args) - self.sslcerts[winid] = new_win - self.handle_ready_sslcert(new_win) - else: - win = self.sslcerts.get(winid, None) - if win is None: - print(" Unknown ssl cert window id {}".format(winid)) - else: - win.handle(action, *args) - def handle_PLOT(self, *args): if self.current_draw_target is not None: self.current_draw_target.handle_plot(*args) @@ -272,44 +309,6 @@ class Browser: # Override this method to do useful stuff lwin.destroy() - def handle_ready_sslcert(self, cwin): - - # pylint: disable=locally-disabled, no-self-use - - # Override this method to do useful stuff - cwin.destroy() - - -class SSLCertWindow: - - # pylint: disable=locally-disabled, invalid-name - - def __init__(self, browser, winid, _url, *url): - self.alive = True - self.browser = browser - self.winid = winid - self.url = " ".join(url) - - def handle(self, action, _str="STR"): - if action == "DESTROY": - self.alive = False - else: - raise AssertionError("Unknown action {} for sslcert window".format(action)) - - def _wait_dead(self): - while self.alive: - self.browser.farmer.loop(once=True) - - def go(self): - assert self.alive - self.browser.farmer.tell_monkey("SSLCERT GO {}".format(self.winid)) - self._wait_dead() - - def destroy(self): - assert self.alive - self.browser.farmer.tell_monkey("SSLCERT DESTROY {}".format(self.winid)) - self._wait_dead() - class LoginWindow: @@ -406,6 +405,7 @@ class BrowserWindow: self.plotted = [] self.plotting = False self.log_entries = [] + self.page_info_state = "UNKNOWN" def kill(self): self.browser.farmer.tell_monkey("WINDOW DESTROY %s" % self.winid) @@ -415,6 +415,10 @@ class BrowserWindow: while self.alive: self.browser.farmer.loop(once=True) if (time.time() - now) > timeout: + print("*** Timed out waiting for window to be destroyed") + print("*** URL was: {}".format(self.url)) + print("*** Title was: {}".format(self.title)) + print("*** Status was: {}".format(self.status)) break def go(self, url, referer=None): @@ -519,6 +523,9 @@ class BrowserWindow: def handle_window_CONSOLE_LOG(self, _src, src, folding, level, *msg): self.log_entries.append((src, folding == "FOLDABLE", level, " ".join(msg))) + def handle_window_PAGE_STATUS(self, _status, status): + self.page_info_state = status + def load_page(self, url=None, referer=None): if url is not None: self.go(url, referer) @@ -622,9 +629,6 @@ def farmer_test(): lwin.send_password("bar") lwin.go() - def handle_ready_sslcert(self, cwin): - cwin.destroy() - fbbrowser = FooBarLogin(quiet=True) win = fbbrowser.new_window() win.load_page("https://httpbin.org/basic-auth/foo/bar") diff --git a/test/nsoption.c b/test/nsoption.c index 8f2388a5b..33da1f7e0 100644 --- a/test/nsoption.c +++ b/test/nsoption.c @@ -33,6 +33,10 @@ #include "utils/log.h" #include "utils/nsoption.h" +#ifndef TESTROOT +#define TESTROOT "/tmp" +#endif + const char *test_choices_path = "test/data/Choices"; const char *test_choices_short_path = "test/data/Choices-short"; const char *test_choices_all_path = "test/data/Choices-all"; @@ -49,7 +53,9 @@ static char *testnam(char *out) { static int count = 0; static char name[64]; - snprintf(name, 64, "/tmp/nsoptiontest%d", count); + int pid; + pid=getpid(); + snprintf(name, 64, TESTROOT"/nsoptiontest%d%d", pid, count); count++; return name; } @@ -241,7 +247,7 @@ struct format_test_vec_s format_test_vec[] = { }, { NSOPTION_sys_colour_ActiveBorder, - "<tr><th>sys_colour_ActiveBorder</th><td>colour</td><td>default</td><td><span style=\"background-color: #d3d3d3; color: #000000; font-family:Monospace; \">#D3D3D3</span></td></tr>", + "<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>", "sys_colour_ActiveBorder:d3d3d3" }, }; diff --git a/test/utils.c b/test/utils.c index 3d5319a28..9fe6747c3 100644 --- a/test/utils.c +++ b/test/utils.c @@ -37,22 +37,31 @@ #define SLEN(x) (sizeof((x)) - 1) struct test_pairs { - const unsigned long test; + const unsigned long long int test; const char* res; }; static const struct test_pairs human_friendly_bytesize_test_vec[] = { - { 0, "0.00Bytes" }, - { 1024, "1024.00Bytes" }, - { 1025, "1.00kBytes" }, - { 1048576, "1024.00kBytes" }, - { 1048577, "1.00MBytes" }, - { 1073741824, "1024.00MBytes" }, - { 1073741888, "1024.00MBytes" }, /* spot the rounding error */ - { 1073741889, "1.00GBytes" }, - { 2147483648, "2.00GBytes" }, - { 3221225472, "3.00GBytes" }, - { 4294967295, "4.00GBytes" }, + { 0ULL, "0Bytes" }, + { 0x2AULL, "42Bytes" }, + { 0x400ULL, "1024Bytes" }, + { 0x401ULL, "1.00KiBytes" }, + { 0xA9AEULL, "42.42KiBytes" }, + { 0x100000ULL, "1024.00KiBytes" }, + { 0x100001ULL, "1.00MiBytes" }, + { 0x2A6B852ULL, "42.42MiBytes" }, + { 0x40000000ULL, "1024.00MiBytes" }, + { 0x40000001ULL, "1.00GiBytes" }, + { 0x80000000ULL, "2.00GiBytes" }, + { 0xC0000000ULL, "3.00GiBytes" }, + { 0x100000000ULL, "4.00GiBytes" }, + { 0x10000000000ULL, "1024.00GiBytes" }, + { 0x10000000001ULL, "1.00TiBytes" }, + { 0x4000000000000ULL, "1024.00TiBytes" }, + { 0x4000000000001ULL, "1.00PiBytes" }, + { 0x1000000000000000ULL, "1024.00PiBytes" }, + { 0x1000000000000100ULL, "1.00EiBytes" }, /* precision loss */ + { 0xFFFFFFFFFFFFFFFFULL, "16.00EiBytes" }, }; /** |