From aacd6f3998f5b35287d9dbb37d9fec878f3f6e62 Mon Sep 17 00:00:00 2001 From: David Hummel <6109326+hummeltech@users.noreply.github.com> Date: Mon, 16 Feb 2026 08:27:14 -0700 Subject: [PATCH 01/10] Disable FreeBSD using latest repository --- .github/workflows/build-and-test.yml | 12 ++++++------ docs/build/building_on_freebsd.md | 6 +++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 9ece3cf2..159b2e51 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -272,12 +272,12 @@ jobs: memory: 4096 ssh_shell: sh - # Mapnik is not in the `quarterly` repository (2023.10.12) - - name: Use "latest" repository - run: | - sudo mkdir -p /usr/local/etc/pkg/repos - sed 's#/quarterly#/latest#g' /etc/pkg/FreeBSD.conf | sudo tee /usr/local/etc/pkg/repos/FreeBSD.conf - sudo pkg upgrade --yes + ## In case Mapnik is not in the `quarterly` repository + # - name: Use "latest" repository + # run: | + # sudo mkdir -p /usr/local/etc/pkg/repos + # sed 's#/quarterly#/latest#g' /etc/pkg/FreeBSD.conf | sudo tee /usr/local/etc/pkg/repos/FreeBSD.conf + # sudo pkg upgrade --yes - name: Install dependencies uses: ./.github/actions/dependencies/install diff --git a/docs/build/building_on_freebsd.md b/docs/build/building_on_freebsd.md index bfc4804b..f9b81a56 100644 --- a/docs/build/building_on_freebsd.md +++ b/docs/build/building_on_freebsd.md @@ -9,9 +9,9 @@ Please see our [Continuous Integration script](/.github/workflows/build-and-test ```shell #!/usr/bin/env sh -# Mapnik is not in the `quarterly` repository (2023.10.12) -sudo mkdir -p /usr/local/etc/pkg/repos -sudo sed 's#/quarterly#/latest#g' /etc/pkg/FreeBSD.conf > /usr/local/etc/pkg/repos/FreeBSD.conf +## In case Mapnik is not in the `quarterly` repository +# sudo mkdir -p /usr/local/etc/pkg/repos +# sed 's#/quarterly#/latest#g' /etc/pkg/FreeBSD.conf | sudo tee /usr/local/etc/pkg/repos/FreeBSD.conf # Update installed packages sudo pkg upgrade --yes From 0abbf8122090729f175c7098f8fbbb094ceb753a Mon Sep 17 00:00:00 2001 From: David Hummel <6109326+hummeltech@users.noreply.github.com> Date: Thu, 29 Jan 2026 07:55:34 -0700 Subject: [PATCH 02/10] Improving tile URI processing logic and other minor improvements --- src/g_logger.c | 12 ++--- src/gen_tile.cpp | 14 +++--- src/mod_tile.c | 123 +++++++++++++++++++++++++++++++++++++++-------- 3 files changed, 117 insertions(+), 32 deletions(-) diff --git a/src/g_logger.c b/src/g_logger.c index bd882044..d9093c40 100644 --- a/src/g_logger.c +++ b/src/g_logger.c @@ -79,31 +79,31 @@ void g_logger(int log_level, const char *format, ...) switch (log_level) { // Levels >= G_LOG_LEVEL_ERROR will terminate the program case G_LOG_LEVEL_ERROR: - g_log_writer_standard_streams(log_level, log_fields, 1, NULL); + g_log_writer_standard_streams((GLogLevelFlags)log_level, log_fields, 1, NULL); break; // Levels <= G_LOG_LEVEL_INFO will only show when using G_MESSAGES_DEBUG case G_LOG_LEVEL_INFO: - g_log_writer_standard_streams(log_level, log_fields, 1, NULL); + g_log_writer_standard_streams((GLogLevelFlags)log_level, log_fields, 1, NULL); break; default: - g_log_writer_default(log_level, log_fields, 1, NULL); + g_log_writer_default((GLogLevelFlags)log_level, log_fields, 1, NULL); } } else if (g_log_writer_is_journald(fileno(stderr))) { switch (log_level) { // Levels >= G_LOG_LEVEL_ERROR will terminate the program case G_LOG_LEVEL_ERROR: - g_log_writer_journald(log_level, log_fields, 1, NULL); + g_log_writer_journald((GLogLevelFlags)log_level, log_fields, 1, NULL); break; // Levels <= G_LOG_LEVEL_INFO will only show when using G_MESSAGES_DEBUG case G_LOG_LEVEL_INFO: - g_log_writer_journald(log_level, log_fields, 1, NULL); + g_log_writer_journald((GLogLevelFlags)log_level, log_fields, 1, NULL); break; default: - g_log_writer_default(log_level, log_fields, 1, NULL); + g_log_writer_default((GLogLevelFlags)log_level, log_fields, 1, NULL); } } else { setlogmask(LOG_UPTO(LOG_INFO)); diff --git a/src/gen_tile.cpp b/src/gen_tile.cpp index d91769d5..3f8a8649 100644 --- a/src/gen_tile.cpp +++ b/src/gen_tile.cpp @@ -114,7 +114,7 @@ struct projectionconfig *get_projection(const char *srs) if (strstr(srs, "+proj=merc +a=6378137 +b=6378137") != NULL) { g_logger(G_LOG_LEVEL_DEBUG, "Using web mercator projection settings"); - prj = (struct projectionconfig *)malloc(sizeof(struct projectionconfig)); + prj = static_cast(malloc(sizeof(struct projectionconfig))); prj->bound_x0 = -20037508.3428; prj->bound_x1 = 20037508.3428; prj->bound_y0 = -20037508.3428; @@ -123,7 +123,7 @@ struct projectionconfig *get_projection(const char *srs) prj->aspect_y = 1; } else if (strcmp(srs, "+proj=eqc +lat_ts=0 +lat_0=0 +lon_0=0 +x_0=0 +y_0=0 +ellps=WGS84 +datum=WGS84 +units=m +no_defs") == 0) { g_logger(G_LOG_LEVEL_DEBUG, "Using plate carree projection settings"); - prj = (struct projectionconfig *)malloc(sizeof(struct projectionconfig)); + prj = static_cast(malloc(sizeof(struct projectionconfig))); prj->bound_x0 = -20037508.3428; prj->bound_x1 = 20037508.3428; prj->bound_y0 = -10018754.1714; @@ -132,7 +132,7 @@ struct projectionconfig *get_projection(const char *srs) prj->aspect_y = 1; } else if (strcmp(srs, "+proj=tmerc +lat_0=49 +lon_0=-2 +k=0.9996012717 +x_0=400000 +y_0=-100000 +ellps=airy +datum=OSGB36 +units=m +no_defs") == 0) { g_logger(G_LOG_LEVEL_DEBUG, "Using bng projection settings"); - prj = (struct projectionconfig *)malloc(sizeof(struct projectionconfig)); + prj = static_cast(malloc(sizeof(struct projectionconfig))); prj->bound_x0 = 0; prj->bound_y0 = 0; prj->bound_x1 = 700000; @@ -141,7 +141,7 @@ struct projectionconfig *get_projection(const char *srs) prj->aspect_y = 2; } else { g_logger(G_LOG_LEVEL_WARNING, "Unknown projection string, using web mercator as never the less. %s", srs); - prj = (struct projectionconfig *)malloc(sizeof(struct projectionconfig)); + prj = static_cast(malloc(sizeof(struct projectionconfig))); prj->bound_x0 = -20037508.3428; prj->bound_x1 = 20037508.3428; prj->bound_y0 = -20037508.3428; @@ -187,7 +187,7 @@ static void load_fonts(const char *font_dir, int recurse) static void parameterize_map_max_connections(Map &m, int num_threads) { unsigned int i; - char *tmp = (char *)malloc(20); + char *tmp = static_cast(malloc(20)); for (i = 0; i < m.layer_count(); i++) { layer &l = m.get_layer(i); @@ -283,7 +283,7 @@ static enum protoCmd render(struct xmlmapconfig *map, int x, int y, int z, char for (yy = 0; yy < render_size_ty; yy++) { for (xx = 0; xx < render_size_tx; xx++) { - mapnik::image_view> vw1(xx * map->tilesize, yy * map->tilesize, map->tilesize, map->tilesize, buf); + mapnik::image_view> vw1(xx * map->tilesize, yy * map->tilesize, map->tilesize, map->tilesize, buf); struct mapnik::image_view_any vw(vw1); tiles.set(xx, yy, save_to_string(vw, map->output_format)); } @@ -292,7 +292,7 @@ static enum protoCmd render(struct xmlmapconfig *map, int x, int y, int z, char return cmdDone; // OK } -#else // METATILE +#else // METATILE static enum protoCmd render(Map &m, const char *tile_dir, char *xmlname, projection &prj, int x, int y, int z, char *outputFormat) { char filename[PATH_MAX]; diff --git a/src/mod_tile.c b/src/mod_tile.c index 08983ffc..f8d7704c 100644 --- a/src/mod_tile.c +++ b/src/mod_tile.c @@ -94,9 +94,6 @@ struct storage_backends { int noBackends; }; -static int error_message(request_rec *r, const char *format, ...) -__attribute__((format(printf, 2, 3))); - static int error_message(request_rec *r, const char *format, ...) { va_list ap; @@ -1105,9 +1102,45 @@ static int tile_storage_hook(request_rec *r) return HTTP_NOT_FOUND; } +/** + * Safely converts a string to an integer. + * Returns APR_SUCCESS if the whole string was a valid number. + */ +static apr_status_t safe_parse_int(const char *str, int *result) +{ + char *endptr; + + if (!str || *str == '\0') { + return APR_EINVAL; + } + + // We use base 10 for coordinates + apr_int64_t val = apr_strtoi64(str, &endptr, 10); + + // Check if we reached the end of the string. + // If *endptr isn't '\0', it means there was non-numeric garbage. + if (*endptr != '\0') { + return APR_EINVAL; + } + + *result = (int)val; + return APR_SUCCESS; +} + +// helper to split the extension from the filename (e.g., "12.png" -> "png") +static void get_extension(char *filename, char *ext_dest, size_t dest_len) +{ + char *dot = strrchr(filename, '.'); + + if (dot) { + apr_cpystrn(ext_dest, dot + 1, dest_len); + *dot = '\0'; // Truncate the filename at the dot to leave just the number + } +} + static int tile_translate(request_rec *r) { - int i, n, limit, oob; + int basuri_len, i, limit, oob; char option[11]; char extension[256]; @@ -1141,18 +1174,19 @@ static int tile_translate(request_rec *r) for (i = 0; i < scfg->configs->nelts; ++i) { tile_config_rec *tile_config = &tile_configs[i]; + basuri_len = strlen(tile_config->baseuri); ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "tile_translate: testing baseuri(%s) name(%s) extension(%s)", tile_config->baseuri, tile_config->xmlname, tile_config->fileExtension); - if (!strncmp(tile_config->baseuri, r->uri, strlen(tile_config->baseuri))) { + if (!strncmp(tile_config->baseuri, r->uri, basuri_len)) { struct tile_request_data *rdata = (struct tile_request_data *)apr_pcalloc(r->pool, sizeof(struct tile_request_data)); struct protocol *cmd = (struct protocol *)apr_pcalloc(r->pool, sizeof(struct protocol)); bzero(cmd, sizeof(struct protocol)); bzero(rdata, sizeof(struct tile_request_data)); - if (!strncmp(r->uri + strlen(tile_config->baseuri), "tile-layer.json", strlen("tile-layer.json"))) { + if (!strncmp(r->uri + basuri_len, "tile-layer.json", strlen("tile-layer.json"))) { ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "tile_translate: Requesting tileJSON for tilelayer %s", tile_config->xmlname); r->handler = "tile_json"; rdata->layerNumber = i; @@ -1162,24 +1196,75 @@ static int tile_translate(request_rec *r) char parameters[XMLCONFIG_MAX]; - if (tile_config->enableOptions) { + // 1. Get the part of the URI after the base + char *path_info = apr_pstrdup(r->pool, r->uri + basuri_len); + + // 2. Tokenize into an array + // We split by "/" to get parts like ["layername", "12", "123", "456.png", "option"] + apr_array_header_t *parts = apr_array_make(r->pool, 6, sizeof(char *)); + char *last; + char *token = apr_strtok(path_info, "/", &last); + + while (token) { + *(char **)apr_array_push(parts) = token; + token = apr_strtok(NULL, "/", &last); + } + + char **elts = (char **)parts->elts; + int n_parts = parts->nelts; + + // 3. Path processing logic + if (tile_config->enableOptions && n_parts >= 4) { + // Expected: /parameter_options/z/x/y.ext/option cmd->ver = PROTO_VER; - n = sscanf(r->uri + strlen(tile_config->baseuri), "%40[^/]/%d/%d/%d.%255[a-z]/%10s", parameters, &(cmd->z), &(cmd->x), &(cmd->y), extension, option); + apr_cpystrn(parameters, elts[0], 41); - if (n < 5) { - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "tile_translate: Invalid URL for tilelayer %s with options", tile_config->xmlname); + // Parse Z, X, Y safely + if (safe_parse_int(elts[1], &(cmd->z)) != APR_SUCCESS || + safe_parse_int(elts[2], &(cmd->x)) != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "tile_translate: Invalid Z/X coordinates: %s/%s", elts[1], elts[2]); return DECLINED; } - } else { + + // Special handling for elts[3] which is "y.ext" + char *y_str = apr_pstrdup(r->pool, elts[3]); + get_extension(y_str, extension, 256); // This null-terminates y_str at the dot + + if (safe_parse_int(y_str, &(cmd->y)) != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "tile_translate: Invalid Y coordinate: %s", y_str); + return DECLINED; + } + + if (n_parts >= 5) { + apr_cpystrn(option, elts[4], 11); + } + } else if (!tile_config->enableOptions && n_parts >= 3) { + // Expected: /z/x/y.ext/option cmd->ver = 2; - n = sscanf(r->uri + strlen(tile_config->baseuri), "%d/%d/%d.%255[a-z]/%10s", &(cmd->z), &(cmd->x), &(cmd->y), extension, option); + parameters[0] = '\0'; - if (n < 4) { - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "tile_translate: Invalid URL for tilelayer %s without options", tile_config->xmlname); + // Parse Z, X, Y safely + if (safe_parse_int(elts[0], &(cmd->z)) != APR_SUCCESS || + safe_parse_int(elts[1], &(cmd->x)) != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "tile_translate: Invalid Z/X coordinates: %s/%s", elts[0], elts[1]); return DECLINED; } - parameters[0] = 0; + // Special handling for elts[2] which is "y.ext" + char *y_str = apr_pstrdup(r->pool, elts[2]); + get_extension(y_str, extension, 256); // This null-terminates y_str at the dot + + if (safe_parse_int(y_str, &(cmd->y)) != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "tile_translate: Invalid Y coordinate: %s", y_str); + return DECLINED; + } + + if (n_parts >= 4) { + apr_cpystrn(option, elts[3], 11); + } + } else { + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "tile_translate: Invalid tile path structure"); + return DECLINED; } if (strcmp(extension, tile_config->fileExtension) != 0) { @@ -1205,9 +1290,9 @@ static int tile_translate(request_rec *r) return HTTP_NOT_FOUND; } - strcpy(cmd->xmlname, tile_config->xmlname); - strcpy(cmd->mimetype, tile_config->mimeType); - strcpy(cmd->options, parameters); + apr_cpystrn(cmd->xmlname, tile_config->xmlname, XMLCONFIG_MAX); + apr_cpystrn(cmd->mimetype, tile_config->mimeType, XMLCONFIG_MAX); + apr_cpystrn(cmd->options, parameters, XMLCONFIG_MAX); // Store a copy for later rdata->cmd = cmd; @@ -1228,7 +1313,7 @@ static int tile_translate(request_rec *r) r->filename = NULL; - if ((tile_config->enableOptions && (n == 6)) || (!tile_config->enableOptions && (n == 5))) { + if ((tile_config->enableOptions && (n_parts == 5)) || (!tile_config->enableOptions && (n_parts == 4))) { if (!strcmp(option, "status")) { r->handler = "tile_status"; } else if (!strcmp(option, "dirty")) { From 3ee2123c6bb45d2d4fb429badd87c601fd43395a Mon Sep 17 00:00:00 2001 From: David Hummel <6109326+hummeltech@users.noreply.github.com> Date: Mon, 2 Feb 2026 11:46:39 -0700 Subject: [PATCH 03/10] Improving testing --- Makefile.am | 52 +- src/CMakeLists.txt | 22 +- src/renderd_config.c | 4 +- tests/CMakeLists.txt | 24 +- tests/catch_main.cpp | 4 +- tests/render_list_test.cpp | 115 ++++ tests/renderd_config_test.cpp | 537 ------------------- tests/renderd_config_test_helper.cpp | 47 -- tests/unit_test_renderd_config.cpp | 754 +++++++++++++++++++++++++++ 9 files changed, 913 insertions(+), 646 deletions(-) delete mode 100644 tests/renderd_config_test.cpp delete mode 100644 tests/renderd_config_test_helper.cpp create mode 100644 tests/unit_test_renderd_config.cpp diff --git a/Makefile.am b/Makefile.am index 7f0355a4..f46e980a 100644 --- a/Makefile.am +++ b/Makefile.am @@ -26,13 +26,12 @@ bin_PROGRAMS = \ render_speedtest noinst_PROGRAMS = \ gen_tile_test \ - renderd_config_test_helper \ - renderd_config_test \ renderd_test \ render_expired_test \ render_list_test \ render_old_test \ - render_speedtest_test + render_speedtest_test \ + unit_test_renderd_config man_MANS = \ docs/man/renderd.1 \ @@ -60,14 +59,14 @@ renderd_CXXFLAGS = $(MAPNIK_CFLAGS) renderd_DATA = etc/renderd/renderd.conf renderd_LDADD = $(PTHREAD_CFLAGS) $(MAPNIK_LDFLAGS) $(STORE_LDFLAGS) $(INIPARSER_LDFLAGS) -render_speedtest_SOURCES = \ - src/render_speedtest.cpp \ - src/g_logger.c \ +render_expired_SOURCES = \ + src/render_expired.c \ src/protocol_helper.c \ src/render_submit_queue.c \ src/renderd_config.c \ - src/sys_utils.c -render_speedtest_LDADD = $(PTHREAD_CFLAGS) $(GLIB_LIBS) $(INIPARSER_LDFLAGS) + src/sys_utils.c \ + $(STORE_SOURCES) +render_expired_LDADD = $(PTHREAD_CFLAGS) $(STORE_LDFLAGS) $(INIPARSER_LDFLAGS) render_list_SOURCES = \ src/render_list.c \ @@ -78,15 +77,6 @@ render_list_SOURCES = \ $(STORE_SOURCES) render_list_LDADD = $(PTHREAD_CFLAGS) $(STORE_LDFLAGS) $(INIPARSER_LDFLAGS) -render_expired_SOURCES = \ - src/render_expired.c \ - src/protocol_helper.c \ - src/render_submit_queue.c \ - src/renderd_config.c \ - src/sys_utils.c \ - $(STORE_SOURCES) -render_expired_LDADD = $(PTHREAD_CFLAGS) $(STORE_LDFLAGS) $(INIPARSER_LDFLAGS) - render_old_SOURCES = \ src/render_old.c \ src/g_logger.c \ @@ -97,6 +87,15 @@ render_old_SOURCES = \ src/sys_utils.c render_old_LDADD = $(PTHREAD_CFLAGS) $(GLIB_LIBS) $(INIPARSER_LDFLAGS) +render_speedtest_SOURCES = \ + src/render_speedtest.cpp \ + src/g_logger.c \ + src/protocol_helper.c \ + src/render_submit_queue.c \ + src/renderd_config.c \ + src/sys_utils.c +render_speedtest_LDADD = $(PTHREAD_CFLAGS) $(GLIB_LIBS) $(INIPARSER_LDFLAGS) + #convert_meta_SOURCES = src/dir_utils.c src/store.c src/convert_meta.c noinst_LIBRARIES = catch_main.o catch_test_common.o @@ -110,16 +109,6 @@ gen_tile_test_CFLAGS = -DMAIN_ALREADY_DEFINED gen_tile_test_CXXFLAGS = $(renderd_CXXFLAGS) gen_tile_test_LDADD = $(renderd_LDADD) catch_test_common.o -renderd_config_test_helper_SOURCES = \ - tests/renderd_config_test_helper.cpp \ - src/g_logger.c \ - src/renderd_config.c -renderd_config_test_helper_LDADD = $(GLIB_LIBS) $(INIPARSER_LDFLAGS) catch_main.o - -renderd_config_test_SOURCES = \ - tests/renderd_config_test.cpp -renderd_config_test_LDADD = $(GLIB_LIBS) catch_main.o catch_test_common.o - renderd_test_SOURCES = \ tests/renderd_test.cpp renderd_test_LDADD = $(GLIB_LIBS) catch_main.o catch_test_common.o @@ -140,18 +129,23 @@ render_speedtest_test_SOURCES = \ tests/render_speedtest_test.cpp render_speedtest_test_LDADD = $(GLIB_LIBS) catch_main.o catch_test_common.o +unit_test_renderd_config_SOURCES = \ + tests/unit_test_renderd_config.cpp +unit_test_renderd_config_CXXFLAGS = -Isrc +unit_test_renderd_config_LDADD = $(GLIB_LIBS) $(INIPARSER_LDFLAGS) catch_main.o catch_test_common.o + CLEANFILES=*.slo mod_tile.la stderr.out src/*.slo src/*.lo src/.libs/* src/*.la COMMA=, -test: gen_tile_test renderd_config_test_helper renderd_config_test renderd_test render_expired_test render_list_test render_old_test render_speedtest_test +test: gen_tile_test renderd_test render_expired_test render_list_test render_old_test render_speedtest_test unit_test_renderd_config ./gen_tile_test - ./renderd_config_test ./renderd_test ./render_expired_test ./render_list_test ./render_old_test ./render_speedtest_test + ./unit_test_renderd_config all-local: $(APXS) -c $(DEF_LDLIBS) $(AM_CFLAGS) \ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 69a0f873..c36f3166 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -219,23 +219,27 @@ target_include_directories(gen_tile_test PRIVATE ${PROJECT_SOURCE_DIR}/tests) target_link_libraries(gen_tile_test ${gen_tile_test_LIBS}) set_target_properties(gen_tile_test PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/tests) + #----------------------------------------------------------------------------- # -# renderd_config_test_helper +# unit_test_renderd_config # #----------------------------------------------------------------------------- -set(renderd_config_test_helper_SRCS - ${PROJECT_SOURCE_DIR}/tests/renderd_config_test_helper.cpp - g_logger.c - renderd_config.c +set(unit_test_renderd_config_SRCS + $ + ${PROJECT_SOURCE_DIR}/tests/unit_test_renderd_config.cpp ) -set(renderd_config_test_helper_LIBS +set(unit_test_renderd_config_LIBS ${GLIB_LIBRARIES} ${INIPARSER_LIBRARIES} ) -add_executable(renderd_config_test_helper ${renderd_config_test_helper_SRCS}) -target_link_libraries(renderd_config_test_helper ${renderd_config_test_helper_LIBS}) -set_target_properties(renderd_config_test_helper PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/tests) +add_executable(unit_test_renderd_config ${unit_test_renderd_config_SRCS}) +set_target_properties(unit_test_renderd_config PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/tests) +target_compile_definitions(unit_test_renderd_config PRIVATE + RENDERD_CONF="${PROJECT_SOURCE_DIR}/etc/renderd/renderd.conf.examples" +) +target_include_directories(unit_test_renderd_config PRIVATE ${PROJECT_SOURCE_DIR}/src ${PROJECT_SOURCE_DIR}/tests) +target_link_libraries(unit_test_renderd_config ${unit_test_renderd_config_LIBS}) endif() diff --git a/src/renderd_config.c b/src/renderd_config.c index 51a5e65b..a4f7ceed 100644 --- a/src/renderd_config.c +++ b/src/renderd_config.c @@ -15,7 +15,9 @@ * along with this program; If not, see http://www.gnu.org/licenses/. */ +#ifndef _GNU_SOURCE #define _GNU_SOURCE +#endif #include #include @@ -520,7 +522,7 @@ void process_config_file(const char *config_file_name, int active_renderd_sectio } if (config.ipport > 0) { - g_logger(log_level, "\trenderd: ip socket = '%s':%i", config.iphostname, config.ipport); + g_logger(log_level, "\trenderd: ip socket = '%s:%i'", config.iphostname, config.ipport); } else { g_logger(log_level, "\trenderd: unix socketname = '%s'", config.socketname); } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 5881a0e0..00b29a65 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -221,8 +221,8 @@ add_test( ) add_test( - NAME renderd_config_test - COMMAND renderd_config_test + NAME unit_test_renderd_config + COMMAND unit_test_renderd_config ) foreach(STORAGE_BACKEND_INDEX RANGE ${STORAGE_BACKENDS_LENGTH}) @@ -1252,15 +1252,6 @@ target_include_directories(catch_test_common_o PRIVATE ${GLIB_INCLUDE_DIRS}) # Added by ${PROJECT_SOURCE_DIR}/src/CMakeLists.txt # (in order to reduce redundant source and library definitions) -#----------------------------------------------------------------------------- -# -# renderd_config_test_helper -# -#----------------------------------------------------------------------------- - -# Added by ${PROJECT_SOURCE_DIR}/src/CMakeLists.txt -# (in order to reduce redundant source and library definitions) - #----------------------------------------------------------------------------- # # Additional options for targets added hereafter @@ -1328,14 +1319,3 @@ add_executable(renderd_test $ renderd_test.cpp ) - -#----------------------------------------------------------------------------- -# -# renderd_config_test -# -#----------------------------------------------------------------------------- - -add_executable(renderd_config_test - $ - renderd_config_test.cpp -) diff --git a/tests/catch_main.cpp b/tests/catch_main.cpp index 5e79fb18..9f23b2ff 100644 --- a/tests/catch_main.cpp +++ b/tests/catch_main.cpp @@ -20,4 +20,6 @@ #include "catch/catch.hpp" #include "catch_test_common.hpp" -int foreground = 1; +extern "C" { + int foreground = 1; +} diff --git a/tests/render_list_test.cpp b/tests/render_list_test.cpp index 581e6f7b..f5cda307 100644 --- a/tests/render_list_test.cpp +++ b/tests/render_list_test.cpp @@ -248,6 +248,31 @@ TEST_CASE("render_list min/max int generator", "min/max int generator testing") int status = run_command(test_binary, argv); REQUIRE(WEXITSTATUS(status) == 0); } + + SECTION(option + " option is negative", "should return 1") { + std::vector argv = {option, "-1"}; + + int status = run_command(test_binary, argv); + REQUIRE(WEXITSTATUS(status) == 1); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("must be >=")); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("(-1 was provided)")); + } + + SECTION(option + " option is float", "should return 1") { + std::vector argv = {option, "1.23456789"}; + + int status = run_command(test_binary, argv); + REQUIRE(WEXITSTATUS(status) == 1); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("must be an integer (1.23456789 was provided)")); + } + + SECTION(option + " option is not an integer", "should return 1") { + std::vector argv = {option, "invalid"}; + + int status = run_command(test_binary, argv); + REQUIRE(WEXITSTATUS(status) == 1); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("must be an integer (invalid was provided)")); + } } TEST_CASE("render_list min/max lat generator", "min/max double generator testing") @@ -269,6 +294,51 @@ TEST_CASE("render_list min/max lat generator", "min/max double generator testing int status = run_command(test_binary, argv); REQUIRE(WEXITSTATUS(status) == 0); } + + SECTION(option + " option is too large", "should return 1") { + std::vector argv = {option, std::to_string(max + .1)}; + + int status = run_command(test_binary, argv); + REQUIRE(WEXITSTATUS(status) == 1); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("must be <= 85.051100 (85.151100 was provided)")); + } + + SECTION(option + " option is too small", "should return 1") { + std::vector argv = {option, std::to_string(min - .1)}; + + int status = run_command(test_binary, argv); + REQUIRE(WEXITSTATUS(status) == 1); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("must be >= -85.051100 (-85.151100 was provided)")); + } + + SECTION(option + " option is not a double", "should return 1") { + std::vector argv = {option, "invalid"}; + + int status = run_command(test_binary, argv); + REQUIRE(WEXITSTATUS(status) == 1); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("must be a double (invalid was provided)")); + } + + SECTION(option + " option is positive with --help", "should return 0") { + std::vector argv = {option, std::to_string(max), "--help"}; + + int status = run_command(test_binary, argv); + REQUIRE(WEXITSTATUS(status) == 0); + } + + SECTION(option + " option is negative with --help", "should return 0") { + std::vector argv = {option, std::to_string(min), "--help"}; + + int status = run_command(test_binary, argv); + REQUIRE(WEXITSTATUS(status) == 0); + } + + SECTION(option + " option is double with --help", "should return 0") { + std::vector argv = {option, "1.23456789", "--help"}; + + int status = run_command(test_binary, argv); + REQUIRE(WEXITSTATUS(status) == 0); + } } TEST_CASE("render_list min/max lon generator", "min/max double generator testing") @@ -290,4 +360,49 @@ TEST_CASE("render_list min/max lon generator", "min/max double generator testing int status = run_command(test_binary, argv); REQUIRE(WEXITSTATUS(status) == 0); } + + SECTION(option + " option is too large", "should return 1") { + std::vector argv = {option, std::to_string(max + .1)}; + + int status = run_command(test_binary, argv); + REQUIRE(WEXITSTATUS(status) == 1); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("must be <= 180.000000 (180.100000 was provided)")); + } + + SECTION(option + " option is too small", "should return 1") { + std::vector argv = {option, std::to_string(min - .1)}; + + int status = run_command(test_binary, argv); + REQUIRE(WEXITSTATUS(status) == 1); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("must be >= -180.000000 (-180.100000 was provided)")); + } + + SECTION(option + " option is not a double", "should return 1") { + std::vector argv = {option, "invalid"}; + + int status = run_command(test_binary, argv); + REQUIRE(WEXITSTATUS(status) == 1); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("must be a double (invalid was provided)")); + } + + SECTION(option + " option is positive with --help", "should return 0") { + std::vector argv = {option, std::to_string(max), "--help"}; + + int status = run_command(test_binary, argv); + REQUIRE(WEXITSTATUS(status) == 0); + } + + SECTION(option + " option is negative with --help", "should return 0") { + std::vector argv = {option, std::to_string(min), "--help"}; + + int status = run_command(test_binary, argv); + REQUIRE(WEXITSTATUS(status) == 0); + } + + SECTION(option + " option is double with --help", "should return 0") { + std::vector argv = {option, "1.23456789", "--help"}; + + int status = run_command(test_binary, argv); + REQUIRE(WEXITSTATUS(status) == 0); + } } diff --git a/tests/renderd_config_test.cpp b/tests/renderd_config_test.cpp deleted file mode 100644 index 7f5f2ed1..00000000 --- a/tests/renderd_config_test.cpp +++ /dev/null @@ -1,537 +0,0 @@ -/* - * Copyright (c) 2007 - 2023 by mod_tile contributors (see AUTHORS file) - * - * This program 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; either version 2 - * of the License, or (at your option) any later version. - * - * This program 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/. - */ - -#include -#include -#include - -#include "catch/catch.hpp" -#include "catch_test_common.hpp" -#include "config.h" -#include "render_config.h" -#include "renderd.h" - -#ifdef __FreeBSD__ -#include -#endif - -#ifndef PROJECT_BINARY_DIR -#define PROJECT_BINARY_DIR "." -#endif - -#ifndef RENDERD_CONF -#define RENDERD_CONF "./etc/renderd/renderd.conf.examples" -#endif - -// Only render_list uses all functions in renderd_config.c -std::string test_binary = (std::string)PROJECT_BINARY_DIR + "/" + "render_list"; -extern std::string err_log_lines; - -TEST_CASE("renderd_config min/max int", "min/max int generator testing") -{ - std::string option = GENERATE("--max-load", "--max-x", "--max-y", "--max-zoom", "--min-x", "--min-y", "--min-zoom", "--num-threads"); - - SECTION(option + " option is positive with --help", "should return 0") { - std::vector argv = {option, "1", "--help"}; - - int status = run_command(test_binary, argv); - REQUIRE(WEXITSTATUS(status) == 0); - } - - SECTION(option + " option is negative", "should return 1") { - std::vector argv = {option, "-1"}; - - int status = run_command(test_binary, argv); - REQUIRE(WEXITSTATUS(status) == 1); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("must be >=")); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("(-1 was provided)")); - } - - SECTION(option + " option is float", "should return 1") { - std::vector argv = {option, "1.23456789"}; - - int status = run_command(test_binary, argv); - REQUIRE(WEXITSTATUS(status) == 1); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("must be an integer (1.23456789 was provided)")); - } - - SECTION(option + " option is not an integer", "should return 1") { - std::vector argv = {option, "invalid"}; - - int status = run_command(test_binary, argv); - REQUIRE(WEXITSTATUS(status) == 1); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("must be an integer (invalid was provided)")); - } -} - -TEST_CASE("renderd_config min/max double lat generator", "min/max double generator testing") -{ - std::string option = GENERATE("--max-lat", "--min-lat"); - double min = -85.051100; - double max = 85.051100; - - SECTION(option + " option is too large", "should return 1") { - std::vector argv = {option, std::to_string(max + .1)}; - - int status = run_command(test_binary, argv); - REQUIRE(WEXITSTATUS(status) == 1); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("must be <= 85.051100 (85.151100 was provided)")); - } - - SECTION(option + " option is too small", "should return 1") { - std::vector argv = {option, std::to_string(min - .1)}; - - int status = run_command(test_binary, argv); - REQUIRE(WEXITSTATUS(status) == 1); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("must be >= -85.051100 (-85.151100 was provided)")); - } - - SECTION(option + " option is not a double", "should return 1") { - std::vector argv = {option, "invalid"}; - - int status = run_command(test_binary, argv); - REQUIRE(WEXITSTATUS(status) == 1); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("must be a double (invalid was provided)")); - } - - SECTION(option + " option is positive with --help", "should return 0") { - std::vector argv = {option, std::to_string(max), "--help"}; - - int status = run_command(test_binary, argv); - REQUIRE(WEXITSTATUS(status) == 0); - } - - SECTION(option + " option is negative with --help", "should return 0") { - std::vector argv = {option, std::to_string(min), "--help"}; - - int status = run_command(test_binary, argv); - REQUIRE(WEXITSTATUS(status) == 0); - } - - SECTION(option + " option is double with --help", "should return 0") { - std::vector argv = {option, "1.23456789", "--help"}; - - int status = run_command(test_binary, argv); - REQUIRE(WEXITSTATUS(status) == 0); - } -} - -TEST_CASE("renderd_config min/max double lon generator", "min/max double generator testing") -{ - std::string option = GENERATE("--max-lon", "--min-lon"); - double min = -180.000000; - double max = 180.000000; - - SECTION(option + " option is too large", "should return 1") { - std::vector argv = {option, std::to_string(max + .1)}; - - int status = run_command(test_binary, argv); - REQUIRE(WEXITSTATUS(status) == 1); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("must be <= 180.000000 (180.100000 was provided)")); - } - - SECTION(option + " option is too small", "should return 1") { - std::vector argv = {option, std::to_string(min - .1)}; - - int status = run_command(test_binary, argv); - REQUIRE(WEXITSTATUS(status) == 1); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("must be >= -180.000000 (-180.100000 was provided)")); - } - - SECTION(option + " option is not a double", "should return 1") { - std::vector argv = {option, "invalid"}; - - int status = run_command(test_binary, argv); - REQUIRE(WEXITSTATUS(status) == 1); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("must be a double (invalid was provided)")); - } - - SECTION(option + " option is positive with --help", "should return 0") { - std::vector argv = {option, std::to_string(max), "--help"}; - - int status = run_command(test_binary, argv); - REQUIRE(WEXITSTATUS(status) == 0); - } - - SECTION(option + " option is negative with --help", "should return 0") { - std::vector argv = {option, std::to_string(min), "--help"}; - - int status = run_command(test_binary, argv); - REQUIRE(WEXITSTATUS(status) == 0); - } - - SECTION(option + " option is double with --help", "should return 0") { - std::vector argv = {option, "1.23456789", "--help"}; - - int status = run_command(test_binary, argv); - REQUIRE(WEXITSTATUS(status) == 0); - } -} - -TEST_CASE("renderd_config config parser", "specific testing") -{ - SECTION("renderd.conf with too many map sections", "should return 7") { - std::string renderd_conf = std::tmpnam(nullptr); - std::ofstream renderd_conf_file; - renderd_conf_file.open(renderd_conf); - renderd_conf_file << "[mapnik]\n[renderd]\n"; - - for (int i = 0; i <= XMLCONFIGS_MAX; i++) { - renderd_conf_file << "[map" + std::to_string(i) + "]\n"; - } - - renderd_conf_file.close(); - - std::vector argv = {"--config", renderd_conf}; - - int status = run_command(test_binary, argv); - std::remove(renderd_conf.c_str()); - REQUIRE(WEXITSTATUS(status) == 7); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Can't handle more than " + std::to_string(XMLCONFIGS_MAX) + " map config sections")); - } - - SECTION("renderd.conf without map sections", "should return 1") { - std::string renderd_conf = std::tmpnam(nullptr); - std::ofstream renderd_conf_file; - renderd_conf_file.open(renderd_conf); - renderd_conf_file << "[mapnik]\n[renderd]\n"; - renderd_conf_file.close(); - - std::vector argv = {"--config", renderd_conf}; - - int status = run_command(test_binary, argv); - std::remove(renderd_conf.c_str()); - REQUIRE(WEXITSTATUS(status) == 1); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("No map config sections were found in file: " + renderd_conf)); - } - - SECTION("renderd.conf without mapnik section", "should return 1") { - std::string renderd_conf = std::tmpnam(nullptr); - std::ofstream renderd_conf_file; - renderd_conf_file.open(renderd_conf); - renderd_conf_file << "[map]\n[renderd]\n"; - renderd_conf_file.close(); - - std::vector argv = {"--config", renderd_conf}; - - int status = run_command(test_binary, argv); - std::remove(renderd_conf.c_str()); - REQUIRE(WEXITSTATUS(status) == 1); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("No mapnik config section was found in file: " + renderd_conf)); - } - - SECTION("renderd.conf with invalid renderd sections", "should return 7") { - std::string renderd_conf_renderd_section_name = "renderdinvalid"; - - std::string renderd_conf = std::tmpnam(nullptr); - std::ofstream renderd_conf_file; - renderd_conf_file.open(renderd_conf); - renderd_conf_file << "[mapnik]\n[map]\n[" + renderd_conf_renderd_section_name + "]\n"; - renderd_conf_file.close(); - - std::vector argv = {"--config", renderd_conf}; - - int status = run_command(test_binary, argv); - std::remove(renderd_conf.c_str()); - REQUIRE(WEXITSTATUS(status) == 7); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Invalid renderd section name: " + renderd_conf_renderd_section_name)); - } - - SECTION("renderd.conf with too many renderd sections", "should return 7") { - std::string renderd_conf = std::tmpnam(nullptr); - std::ofstream renderd_conf_file; - renderd_conf_file.open(renderd_conf); - renderd_conf_file << "[mapnik]\n[map]\n"; - - for (int i = 0; i <= MAX_SLAVES; i++) { - renderd_conf_file << "[renderd" + std::to_string(i) + "]\n"; - } - - renderd_conf_file.close(); - - std::vector argv = {"--config", renderd_conf}; - - int status = run_command(test_binary, argv); - std::remove(renderd_conf.c_str()); - REQUIRE(WEXITSTATUS(status) == 7); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Can't handle more than " + std::to_string(MAX_SLAVES) + " renderd config sections")); - } - - SECTION("renderd.conf without renderd sections", "should return 1") { - std::string renderd_conf = std::tmpnam(nullptr); - std::ofstream renderd_conf_file; - renderd_conf_file.open(renderd_conf); - renderd_conf_file << "[map]\n[mapnik]\n"; - renderd_conf_file.close(); - - std::vector argv = {"--config", renderd_conf}; - - int status = run_command(test_binary, argv); - std::remove(renderd_conf.c_str()); - REQUIRE(WEXITSTATUS(status) == 1); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("No renderd config sections were found in file: " + renderd_conf)); - } - - SECTION("renderd.conf map section scale too small", "should return 7") { - std::string renderd_conf = std::tmpnam(nullptr); - std::ofstream renderd_conf_file; - renderd_conf_file.open(renderd_conf); - renderd_conf_file << "[mapnik]\n[renderd]\n"; - renderd_conf_file << "[map]\nscale=0.0\n"; - renderd_conf_file.close(); - - std::vector argv = {"--config", renderd_conf}; - - int status = run_command(test_binary, argv); - std::remove(renderd_conf.c_str()); - REQUIRE(WEXITSTATUS(status) == 7); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Specified scale factor (0.000000) is too small, must be greater than or equal to 0.100000.")); - } - - SECTION("renderd.conf map section scale too large", "should return 7") { - std::string renderd_conf = std::tmpnam(nullptr); - std::ofstream renderd_conf_file; - renderd_conf_file.open(renderd_conf); - renderd_conf_file << "[mapnik]\n[renderd]\n"; - renderd_conf_file << "[map]\nscale=8.1\n"; - renderd_conf_file.close(); - - std::vector argv = {"--config", renderd_conf}; - - int status = run_command(test_binary, argv); - std::remove(renderd_conf.c_str()); - REQUIRE(WEXITSTATUS(status) == 7); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Specified scale factor (8.100000) is too large, must be less than or equal to 8.000000.")); - } - - SECTION("renderd.conf map section maxzoom too small", "should return 7") { - std::string renderd_conf = std::tmpnam(nullptr); - std::ofstream renderd_conf_file; - renderd_conf_file.open(renderd_conf); - renderd_conf_file << "[mapnik]\n[renderd]\n"; - renderd_conf_file << "[map]\nmaxzoom=-1\n"; - renderd_conf_file.close(); - - std::vector argv = {"--config", renderd_conf}; - - int status = run_command(test_binary, argv); - std::remove(renderd_conf.c_str()); - REQUIRE(WEXITSTATUS(status) == 7); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Specified max zoom (-1) is too small, must be greater than or equal to 0.")); - } - - SECTION("renderd.conf map section maxzoom too large", "should return 7") { - std::string renderd_conf = std::tmpnam(nullptr); - std::ofstream renderd_conf_file; - renderd_conf_file.open(renderd_conf); - renderd_conf_file << "[mapnik]\n[renderd]\n"; - renderd_conf_file << "[map]\nmaxzoom=" << MAX_ZOOM + 1 << "\n"; - renderd_conf_file.close(); - - std::vector argv = {"--config", renderd_conf}; - - int status = run_command(test_binary, argv); - std::remove(renderd_conf.c_str()); - REQUIRE(WEXITSTATUS(status) == 7); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Specified max zoom (" + std::to_string(MAX_ZOOM + 1) + ") is too large, must be less than or equal to " + std::to_string(MAX_ZOOM) + ".")); - } - - SECTION("renderd.conf map section minzoom too small", "should return 7") { - std::string renderd_conf = std::tmpnam(nullptr); - std::ofstream renderd_conf_file; - renderd_conf_file.open(renderd_conf); - renderd_conf_file << "[mapnik]\n[renderd]\n"; - renderd_conf_file << "[map]\nminzoom=-1\n"; - renderd_conf_file.close(); - - std::vector argv = {"--config", renderd_conf}; - - int status = run_command(test_binary, argv); - std::remove(renderd_conf.c_str()); - REQUIRE(WEXITSTATUS(status) == 7); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Specified min zoom (-1) is too small, must be greater than or equal to 0.")); - } - - SECTION("renderd.conf map section minzoom too large", "should return 7") { - std::string renderd_conf = std::tmpnam(nullptr); - std::ofstream renderd_conf_file; - renderd_conf_file.open(renderd_conf); - renderd_conf_file << "[mapnik]\n[renderd]\n"; - renderd_conf_file << "[map]\nminzoom=" << MAX_ZOOM + 1 << "\n"; - renderd_conf_file.close(); - - std::vector argv = {"--config", renderd_conf}; - - int status = run_command(test_binary, argv); - std::remove(renderd_conf.c_str()); - REQUIRE(WEXITSTATUS(status) == 7); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Specified min zoom (" + std::to_string(MAX_ZOOM + 1) + ") is larger than max zoom (" + std::to_string(MAX_ZOOM) + ").")); - } - - SECTION("renderd.conf map section type has too few parts", "should return 7") { - std::string renderd_conf_map_type = "a"; - - std::string renderd_conf = std::tmpnam(nullptr); - std::ofstream renderd_conf_file; - renderd_conf_file.open(renderd_conf); - renderd_conf_file << "[mapnik]\n[renderd]\n"; - renderd_conf_file << "[map]\ntype=" + renderd_conf_map_type + "\n"; - renderd_conf_file.close(); - - std::vector argv = {"--config", renderd_conf}; - - int status = run_command(test_binary, argv); - std::remove(renderd_conf.c_str()); - REQUIRE(WEXITSTATUS(status) == 7); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Specified type (" + renderd_conf_map_type + ") has too few parts, there must be at least 2, e.g., 'png image/png'.")); - } - - SECTION("renderd.conf map section type has too many parts", "should return 7") { - std::string renderd_conf_map_type = "a b c d"; - - std::string renderd_conf = std::tmpnam(nullptr); - std::ofstream renderd_conf_file; - renderd_conf_file.open(renderd_conf); - renderd_conf_file << "[mapnik]\n[renderd]\n"; - renderd_conf_file << "[map]\ntype=" + renderd_conf_map_type + "\n"; - renderd_conf_file.close(); - - std::vector argv = {"--config", renderd_conf}; - - int status = run_command(test_binary, argv); - std::remove(renderd_conf.c_str()); - REQUIRE(WEXITSTATUS(status) == 7); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Specified type (" + renderd_conf_map_type + ") has too many parts, there must be no more than 3, e.g., 'png image/png png256'.")); - } - - SECTION("renderd.conf renderd section socketname is too long", "should return 7") { - int renderd_socketname_maxlen = sizeof(((struct sockaddr_un *)0)->sun_path); - std::string renderd_socketname = "/" + std::string(renderd_socketname_maxlen, 'A'); - - std::string renderd_conf = std::tmpnam(nullptr); - std::ofstream renderd_conf_file; - renderd_conf_file.open(renderd_conf); - renderd_conf_file << "[mapnik]\n[map]\n"; - renderd_conf_file << "[renderd]\nsocketname=" << renderd_socketname << "\n"; - renderd_conf_file.close(); - - std::vector argv = {"--config", renderd_conf}; - - int status = run_command(test_binary, argv); - std::remove(renderd_conf.c_str()); - REQUIRE(WEXITSTATUS(status) == 7); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Specified socketname (" + renderd_socketname + ") exceeds maximum allowed length of " + std::to_string(renderd_socketname_maxlen) + ".")); - } - - SECTION("renderd.conf duplicate renderd section names", "should return 7") { - std::string renderd_conf = std::tmpnam(nullptr); - std::ofstream renderd_conf_file; - renderd_conf_file.open(renderd_conf); - renderd_conf_file << "[mapnik]\n[map]\n"; - renderd_conf_file << "[renderd0]\n[renderd]\n"; - renderd_conf_file.close(); - - std::vector argv = {"--config", renderd_conf}; - - int status = run_command(test_binary, argv); - std::remove(renderd_conf.c_str()); - REQUIRE(WEXITSTATUS(status) == 7); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Duplicate renderd config section names for section 0: renderd0 & renderd")); - } -} - - -TEST_CASE("renderd_config_test_helper", "specific testing") -{ - std::string test_binary = (std::string) "." + "/" + "renderd_config_test_helper"; - - SECTION("valid renderd.conf file with valid active renderd section", "should return 0") { - std::vector argv = {RENDERD_CONF, std::to_string(0)}; - - int status = run_command(test_binary, argv); - REQUIRE(WEXITSTATUS(status) == 0); - } - - SECTION("valid renderd.conf file with valid active renderd section (process_renderd_sections)", "should return 0") { - std::vector argv = {RENDERD_CONF, std::to_string(0), "process_renderd_sections"}; - - int status = run_command(test_binary, argv); - REQUIRE(WEXITSTATUS(status) == 0); - } - - SECTION("valid renderd.conf file with valid active renderd section (process_mapnik_section)", "should return 0") { - std::vector argv = {RENDERD_CONF, std::to_string(0), "process_mapnik_section"}; - - int status = run_command(test_binary, argv); - REQUIRE(WEXITSTATUS(status) == 0); - } - - SECTION("valid renderd.conf file with valid active renderd section (process_map_sections)", "should return 0") { - std::vector argv = {RENDERD_CONF, std::to_string(0), "process_map_sections"}; - - int status = run_command(test_binary, argv); - REQUIRE(WEXITSTATUS(status) == 0); - } - - SECTION("valid renderd.conf file with invalid active renderd section", "should return 1") { - std::vector argv = {RENDERD_CONF, std::to_string(MAX_SLAVES)}; - - int status = run_command(test_binary, argv); - REQUIRE(WEXITSTATUS(status) == 1); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Active renderd section (" + std::to_string(MAX_SLAVES) + ") must be between 0 and " + std::to_string(MAX_SLAVES - 1) + ".")); - } - - SECTION("valid renderd.conf file with nonexistent active renderd section", "should return 1") { - std::vector argv = {RENDERD_CONF, std::to_string(MAX_SLAVES - 1)}; - - int status = run_command(test_binary, argv); - REQUIRE(WEXITSTATUS(status) == 1); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Active renderd section (" + std::to_string(MAX_SLAVES - 1) + ") does not exist.")); - } - - SECTION("nonexistent renderd.conf file with valid active renderd section", "should return 1") { - std::vector argv = {"doesnotexist", std::to_string(0)}; - - int status = run_command(test_binary, argv); - REQUIRE(WEXITSTATUS(status) == 1); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Failed to load config file (process_config_file): 'doesnotexist'")); - } - - SECTION("nonexistent renderd.conf file with valid active renderd section (process_renderd_sections)", "should return 1") { - std::vector argv = {"doesnotexist", std::to_string(0), "process_renderd_sections"}; - - int status = run_command(test_binary, argv); - REQUIRE(WEXITSTATUS(status) == 1); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Failed to load config file (process_renderd_sections): 'doesnotexist'")); - } - - SECTION("nonexistent renderd.conf file with valid active renderd section (process_mapnik_section)", "should return 1") { - std::vector argv = {"doesnotexist", std::to_string(0), "process_mapnik_section"}; - - int status = run_command(test_binary, argv); - REQUIRE(WEXITSTATUS(status) == 1); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Failed to load config file (process_mapnik_section): 'doesnotexist'")); - } - - SECTION("nonexistent renderd.conf file with valid active renderd section (process_map_sections)", "should return 1") { - std::vector argv = {"doesnotexist", std::to_string(0), "process_map_sections"}; - - int status = run_command(test_binary, argv); - REQUIRE(WEXITSTATUS(status) == 1); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Failed to load config file (process_map_sections): 'doesnotexist'")); - } -} diff --git a/tests/renderd_config_test_helper.cpp b/tests/renderd_config_test_helper.cpp deleted file mode 100644 index 00eea759..00000000 --- a/tests/renderd_config_test_helper.cpp +++ /dev/null @@ -1,47 +0,0 @@ -#include "config.h" -#include "g_logger.h" -#include "renderd_config.h" - -#include - -int main(int argc, char **argv) -{ - char *config_file_name = (char *)RENDERD_CONFIG; - char *process_function = (char *)"process_config_file"; - int active_renderd_section_num = 0; - - foreground = 1; - - if (argc > 1) { - config_file_name = argv[1]; - } - - if (argc > 2) { - active_renderd_section_num = atoi(argv[2]); - } - - if (argc > 3) { - process_function = argv[3]; - } - - if (strcmp(process_function, "process_config_file") == 0) { - process_config_file(config_file_name, active_renderd_section_num, G_LOG_LEVEL_WARNING); - } - - if (strcmp(process_function, "process_renderd_sections") == 0) { - process_renderd_sections(NULL, config_file_name, config_slaves); - } - - if (strcmp(process_function, "process_mapnik_section") == 0) { - process_mapnik_section(NULL, config_file_name, &config_slaves[active_renderd_section_num]); - } - - if (strcmp(process_function, "process_map_sections") == 0) { - process_map_sections(NULL, config_file_name, maps, "", 0); - } - - free_map_sections(maps); - free_renderd_sections(config_slaves); - - exit(0); -} diff --git a/tests/unit_test_renderd_config.cpp b/tests/unit_test_renderd_config.cpp new file mode 100644 index 00000000..be4d562b --- /dev/null +++ b/tests/unit_test_renderd_config.cpp @@ -0,0 +1,754 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "catch/catch.hpp" +#include "catch_test_common.hpp" +#include "renderd_config.h" + +#ifndef RENDERD_CONF +#define RENDERD_CONF "./etc/renderd/renderd.conf.examples" +#endif + +int exit_status = 0; +static bool fail_next = false; +static jmp_buf exit_jump; +extern std::string err_log_lines; + +extern "C" { + int mocked_asprintf(char **strp, const char *fmt, ...) + { + if (fail_next) { + fail_next = false; + *strp = nullptr; + return -1; + } + + va_list args; + va_start(args, fmt); + int result = vasprintf(strp, fmt, args); + va_end(args); + return result; + } + + void mocked_exit(int status) + { + exit_status = status; + longjmp(exit_jump, 1); + } + + void mocked_g_logger(int log_level, const char *format, ...) + { + char *log_message; + va_list args; + + va_start(args, format); + + vasprintf(&log_message, format, args); + + va_end(args); + + err_log_lines.append(log_message); + err_log_lines.append("\n"); + + free(log_message); + } + + char *mocked_strndup(const char *s, size_t n) + { + if (fail_next) { + fail_next = false; + return nullptr; + } + + return strndup(s, n); + } + +#define asprintf mocked_asprintf +#define exit mocked_exit +#define g_logger mocked_g_logger +#define strndup mocked_strndup + +#include "renderd_config.c" + +#undef asprintf +#undef exit +#undef g_logger +#undef strndup +} + +TEST_CASE("renderd_config function failure exit handling", "[renderd_config] [copy_string] [name_with_section]") +{ + err_log_lines.clear(); + exit_status = 0; + + SECTION("copy_string handles strndup failure by exiting", "should return 7") { + const char *src = "test"; + const char *dest; + + fail_next = true; + + if (setjmp(exit_jump) == 0) { + copy_string(src, &dest, strlen(src)); + FAIL("copy_string should have called exit() but didn't"); + } else { + SUCCEED("Captured expected exit() call due to strndup failure"); + } + + fail_next = false; + + REQUIRE(exit_status == 7); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("copy_string: strndup error")); + } + + SECTION("copy_string with valid src and valid dest", "should succeed") { + const char *src = "test"; + const char *dest = nullptr; + + REQUIRE(nullptr == dest); + copy_string(src, &dest, strlen(src)); + REQUIRE(strcmp(src, dest) == 0); + } + + + SECTION("name_with_section handles asprintf failure by exiting") { + const char *name = "socketname"; + const char *section = "renderd"; + char *value = nullptr; + + REQUIRE(nullptr == value); + value = name_with_section(section, name); + REQUIRE((std::string(section) + ":" + name) == value); + } + + SECTION("name_with_section handles asprintf failure by exiting", "should return 7") { + const char *section = "renderd"; + const char *name = "socketname"; + + fail_next = true; + + if (setjmp(exit_jump) == 0) { + name_with_section(section, name); + FAIL("name_with_section should have called exit() but didn't"); + } else { + SUCCEED("Captured expected exit() call due to asprintf failure"); + } + + fail_next = false; + + REQUIRE(exit_status == 7); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("name_with_section: asprintf error")); + } +} + +TEST_CASE("renderd.conf file processing exit handling", "[renderd_config] [process_config_file] [process_renderd_sections] [process_mapnik_section] [process_map_sections]") +{ + err_log_lines.clear(); + exit_status = 0; + + SECTION("valid renderd.conf file with invalid active renderd section", "should return 1") { + if (setjmp(exit_jump) == 0) { + process_config_file(RENDERD_CONF, MAX_SLAVES, 0); + FAIL("process_config_file should have called exit(), but did not"); + } else { + SUCCEED("process_config_file captured expected exit() call"); + } + + REQUIRE(exit_status == 1); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Active renderd section (" + std::to_string(MAX_SLAVES) + ") must be between 0 and " + std::to_string(MAX_SLAVES - 1) + ".")); + } + + SECTION("valid renderd.conf file with nonexistent active renderd section", "should return 1") { + if (setjmp(exit_jump) == 0) { + process_config_file(RENDERD_CONF, (MAX_SLAVES - 1), 0); + FAIL("process_config_file should have called exit(), but did not"); + } else { + SUCCEED("process_config_file captured expected exit() call"); + } + + REQUIRE(exit_status == 1); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Active renderd section (" + std::to_string(MAX_SLAVES - 1) + ") does not exist.")); + } + + SECTION("nonexistent renderd.conf file with valid active renderd section", "should return 1") { + if (setjmp(exit_jump) == 0) { + process_config_file("doesnotexist", 0, 0); + FAIL("process_config_file should have called exit(), but did not"); + } else { + SUCCEED("process_config_file captured expected exit() call"); + } + + REQUIRE(exit_status == 1); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Failed to load config file (process_config_file): 'doesnotexist'")); + } + + SECTION("nonexistent renderd.conf file with valid active renderd section (process_renderd_sections)", "should return 1") { + if (setjmp(exit_jump) == 0) { + process_renderd_sections(NULL, "doesnotexist", config_slaves); + FAIL("process_renderd_sections should have called exit(), but did not"); + } else { + SUCCEED("process_renderd_sections captured expected exit() call"); + } + + REQUIRE(exit_status == 1); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Failed to load config file (process_renderd_sections): 'doesnotexist'")); + } + + SECTION("nonexistent renderd.conf file with valid active renderd section (process_mapnik_section)", "should return 1") { + if (setjmp(exit_jump) == 0) { + process_mapnik_section(NULL, "doesnotexist", config_slaves); + FAIL("process_mapnik_section should have called exit(), but did not"); + } else { + SUCCEED("process_mapnik_section captured expected exit() call"); + } + + REQUIRE(exit_status == 1); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Failed to load config file (process_mapnik_section): 'doesnotexist'")); + } + + SECTION("nonexistent renderd.conf file with valid active renderd section (process_map_sections)", "should return 1") { + if (setjmp(exit_jump) == 0) { + process_map_sections(NULL, "doesnotexist", maps, "", 0); + FAIL("process_map_sections should have called exit(), but did not"); + } else { + SUCCEED("process_map_sections captured expected exit() call"); + } + + REQUIRE(exit_status == 1); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Failed to load config file (process_map_sections): 'doesnotexist'")); + } + + SECTION("valid renderd.conf file with valid active renderd section (process_config_file)", "should return 0") { + if (setjmp(exit_jump) == 0) { + process_config_file(RENDERD_CONF, 0, 0); + SUCCEED("process_config_file did not call exit(), as expected"); + } else { + FAIL("process_config_file captured unexpected exit() call"); + } + + REQUIRE(exit_status == 0); + } + + SECTION("valid renderd.conf file with valid active renderd section (process_renderd_sections)", "should return 0") { + if (setjmp(exit_jump) == 0) { + process_renderd_sections(NULL, RENDERD_CONF, config_slaves); + SUCCEED("process_renderd_sections did not call exit(), as expected"); + } else { + FAIL("process_renderd_sections captured unexpected exit() call"); + } + + REQUIRE(exit_status == 0); + } + + SECTION("valid renderd.conf file with valid active renderd section (process_mapnik_section)", "should return 0") { + if (setjmp(exit_jump) == 0) { + process_mapnik_section(NULL, RENDERD_CONF, config_slaves); + SUCCEED("process_mapnik_section did not call exit(), as expected"); + } else { + FAIL("process_mapnik_section captured unexpected exit() call"); + } + + REQUIRE(exit_status == 0); + } + + SECTION("valid renderd.conf file with valid active renderd section (process_map_sections)", "should return 0") { + if (setjmp(exit_jump) == 0) { + process_map_sections(NULL, RENDERD_CONF, maps, "", 0); + SUCCEED("process_map_sections did not call exit(), as expected"); + } else { + FAIL("process_map_sections captured unexpected exit() call"); + } + + REQUIRE(exit_status == 0); + } + + std::string renderd_conf_path = std::string(P_tmpdir) + "/renderd.conf_XXXXXX"; + int fd = mkstemp(&renderd_conf_path[0]); + + if (fd == -1) { + FAIL("mkstemp failed"); + } + + close(fd); + std::ofstream renderd_conf_file(renderd_conf_path); + + SECTION("renderd.conf with too many map sections", "should return 7") { + renderd_conf_file << "[mapnik]\n[renderd]\n"; + + for (int i = 0; i <= XMLCONFIGS_MAX; i++) { + renderd_conf_file << "[map" + std::to_string(i) + "]\n"; + } + + renderd_conf_file.close(); + + if (setjmp(exit_jump) == 0) { + process_config_file(renderd_conf_path.c_str(), 0, 0); + FAIL("process_config_file should have called exit(), but did not"); + } else { + SUCCEED("process_config_file captured expected exit() call"); + } + + REQUIRE(exit_status == 7); + + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Can't handle more than " + std::to_string(XMLCONFIGS_MAX) + " map config sections")); + } + + SECTION("renderd.conf without map sections", "should return 1") { + renderd_conf_file << "[mapnik]\n[renderd]\n"; + renderd_conf_file.close(); + + if (setjmp(exit_jump) == 0) { + process_config_file(renderd_conf_path.c_str(), 0, 0); + FAIL("process_config_file should have called exit(), but did not"); + } else { + SUCCEED("process_config_file captured expected exit() call"); + } + + REQUIRE(exit_status == 1); + + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("No map config sections were found in file: " + renderd_conf_path)); + } + + SECTION("renderd.conf without mapnik section", "should return 1") { + renderd_conf_file << "[map]\n[renderd]\n"; + renderd_conf_file.close(); + + if (setjmp(exit_jump) == 0) { + process_config_file(renderd_conf_path.c_str(), 0, 0); + FAIL("process_config_file should have called exit(), but did not"); + } else { + SUCCEED("process_config_file captured expected exit() call"); + } + + REQUIRE(exit_status == 1); + + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("No mapnik config section was found in file: " + renderd_conf_path)); + } + + SECTION("renderd.conf with invalid renderd sections", "should return 7") { + std::string renderd_conf_renderd_section_name = "renderdinvalid"; + + renderd_conf_file << "[mapnik]\n[map]\n[" + renderd_conf_renderd_section_name + "]\n"; + renderd_conf_file.close(); + + if (setjmp(exit_jump) == 0) { + process_config_file(renderd_conf_path.c_str(), 0, 0); + FAIL("process_config_file should have called exit(), but did not"); + } else { + SUCCEED("process_config_file captured expected exit() call"); + } + + REQUIRE(exit_status == 7); + + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Invalid renderd section name: " + renderd_conf_renderd_section_name)); + } + + SECTION("renderd.conf with too many renderd sections", "should return 7") { + renderd_conf_file << "[mapnik]\n[map]\n"; + + for (int i = 0; i <= MAX_SLAVES; i++) { + renderd_conf_file << "[renderd" + std::to_string(i) + "]\n"; + } + + renderd_conf_file.close(); + + if (setjmp(exit_jump) == 0) { + process_config_file(renderd_conf_path.c_str(), 0, 0); + FAIL("process_config_file should have called exit(), but did not"); + } else { + SUCCEED("process_config_file captured expected exit() call"); + } + + REQUIRE(exit_status == 7); + + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Can't handle more than " + std::to_string(MAX_SLAVES) + " renderd config sections")); + } + + SECTION("renderd.conf without renderd sections", "should return 1") { + renderd_conf_file << "[map]\n[mapnik]\n"; + renderd_conf_file.close(); + + if (setjmp(exit_jump) == 0) { + process_config_file(renderd_conf_path.c_str(), 0, 0); + FAIL("process_config_file should have called exit(), but did not"); + } else { + SUCCEED("process_config_file captured expected exit() call"); + } + + REQUIRE(exit_status == 1); + + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("No renderd config sections were found in file: " + renderd_conf_path)); + } + + SECTION("renderd.conf map section scale too small", "should return 7") { + renderd_conf_file << "[mapnik]\n[renderd]\n"; + renderd_conf_file << "[map]\nscale=0.0\n"; + renderd_conf_file.close(); + + if (setjmp(exit_jump) == 0) { + process_config_file(renderd_conf_path.c_str(), 0, 0); + FAIL("process_config_file should have called exit(), but did not"); + } else { + SUCCEED("process_config_file captured expected exit() call"); + } + + REQUIRE(exit_status == 7); + + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Specified scale factor (0.000000) is too small, must be greater than or equal to 0.100000.")); + } + + SECTION("renderd.conf map section scale too large", "should return 7") { + renderd_conf_file << "[mapnik]\n[renderd]\n"; + renderd_conf_file << "[map]\nscale=8.1\n"; + renderd_conf_file.close(); + + if (setjmp(exit_jump) == 0) { + process_config_file(renderd_conf_path.c_str(), 0, 0); + FAIL("process_config_file should have called exit(), but did not"); + } else { + SUCCEED("process_config_file captured expected exit() call"); + } + + REQUIRE(exit_status == 7); + + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Specified scale factor (8.100000) is too large, must be less than or equal to 8.000000.")); + } + + SECTION("renderd.conf map section maxzoom too small", "should return 7") { + renderd_conf_file << "[mapnik]\n[renderd]\n"; + renderd_conf_file << "[map]\nmaxzoom=-1\n"; + renderd_conf_file.close(); + + if (setjmp(exit_jump) == 0) { + process_config_file(renderd_conf_path.c_str(), 0, 0); + FAIL("process_config_file should have called exit(), but did not"); + } else { + SUCCEED("process_config_file captured expected exit() call"); + } + + REQUIRE(exit_status == 7); + + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Specified max zoom (-1) is too small, must be greater than or equal to 0.")); + } + + SECTION("renderd.conf map section maxzoom too large", "should return 7") { + renderd_conf_file << "[mapnik]\n[renderd]\n"; + renderd_conf_file << "[map]\nmaxzoom=" << MAX_ZOOM + 1 << "\n"; + renderd_conf_file.close(); + + if (setjmp(exit_jump) == 0) { + process_config_file(renderd_conf_path.c_str(), 0, 0); + FAIL("process_config_file should have called exit(), but did not"); + } else { + SUCCEED("process_config_file captured expected exit() call"); + } + + REQUIRE(exit_status == 7); + + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Specified max zoom (" + std::to_string(MAX_ZOOM + 1) + ") is too large, must be less than or equal to " + std::to_string(MAX_ZOOM) + ".")); + } + + SECTION("renderd.conf map section minzoom too small", "should return 7") { + renderd_conf_file << "[mapnik]\n[renderd]\n"; + renderd_conf_file << "[map]\nminzoom=-1\n"; + renderd_conf_file.close(); + + if (setjmp(exit_jump) == 0) { + process_config_file(renderd_conf_path.c_str(), 0, 0); + FAIL("process_config_file should have called exit(), but did not"); + } else { + SUCCEED("process_config_file captured expected exit() call"); + } + + REQUIRE(exit_status == 7); + + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Specified min zoom (-1) is too small, must be greater than or equal to 0.")); + } + + SECTION("renderd.conf map section minzoom too large", "should return 7") { + renderd_conf_file << "[mapnik]\n[renderd]\n"; + renderd_conf_file << "[map]\nminzoom=" << MAX_ZOOM + 1 << "\n"; + renderd_conf_file.close(); + + if (setjmp(exit_jump) == 0) { + process_config_file(renderd_conf_path.c_str(), 0, 0); + FAIL("process_config_file should have called exit(), but did not"); + } else { + SUCCEED("process_config_file captured expected exit() call"); + } + + REQUIRE(exit_status == 7); + + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Specified min zoom (" + std::to_string(MAX_ZOOM + 1) + ") is larger than max zoom (" + std::to_string(MAX_ZOOM) + ").")); + } + + SECTION("renderd.conf map section type has too few parts", "should return 7") { + std::string renderd_conf_map_type = "a"; + + renderd_conf_file << "[mapnik]\n[renderd]\n"; + renderd_conf_file << "[map]\ntype=" + renderd_conf_map_type + "\n"; + renderd_conf_file.close(); + + if (setjmp(exit_jump) == 0) { + process_config_file(renderd_conf_path.c_str(), 0, 0); + FAIL("process_config_file should have called exit(), but did not"); + } else { + SUCCEED("process_config_file captured expected exit() call"); + } + + REQUIRE(exit_status == 7); + + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Specified type (" + renderd_conf_map_type + ") has too few parts, there must be at least 2, e.g., 'png image/png'.")); + } + + SECTION("renderd.conf map section type has too many parts", "should return 7") { + std::string renderd_conf_map_type = "a b c d"; + + renderd_conf_file << "[mapnik]\n[renderd]\n"; + renderd_conf_file << "[map]\ntype=" + renderd_conf_map_type + "\n"; + renderd_conf_file.close(); + + if (setjmp(exit_jump) == 0) { + process_config_file(renderd_conf_path.c_str(), 0, 0); + FAIL("process_config_file should have called exit(), but did not"); + } else { + SUCCEED("process_config_file captured expected exit() call"); + } + + REQUIRE(exit_status == 7); + + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Specified type (" + renderd_conf_map_type + ") has too many parts, there must be no more than 3, e.g., 'png image/png png256'.")); + } + + SECTION("renderd.conf map section type has two parts", "should return 0") { + renderd_conf_file << "[mapnik]\n[map]\ntype=png image/png\n"; + renderd_conf_file << "[renderd]\n"; + renderd_conf_file.close(); + + if (setjmp(exit_jump) == 0) { + process_config_file(renderd_conf_path.c_str(), 0, 0); + SUCCEED("process_config_file did not call exit(), as expected"); + } else { + FAIL("process_config_file captured unexpected exit() call"); + } + + REQUIRE(exit_status == 0); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Read map:type: 'png image/png'")); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Read map:type:file_extension: 'png'")); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Read map:type:mime_type: 'image/png'")); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Read map:type:output_format: 'png256'")); + } + + SECTION("renderd.conf renderd section socketname is too long", "should return 7") { + int renderd_socketname_maxlen = sizeof(((struct sockaddr_un *)0)->sun_path); + std::string renderd_socketname = "/" + std::string(renderd_socketname_maxlen, 'A'); + + renderd_conf_file << "[mapnik]\n[map]\n"; + renderd_conf_file << "[renderd]\nsocketname=" << renderd_socketname << "\n"; + renderd_conf_file.close(); + + if (setjmp(exit_jump) == 0) { + process_config_file(renderd_conf_path.c_str(), 0, 0); + FAIL("process_config_file should have called exit(), but did not"); + } else { + SUCCEED("process_config_file captured expected exit() call"); + } + + REQUIRE(exit_status == 7); + + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Specified socketname (" + renderd_socketname + ") exceeds maximum allowed length of " + std::to_string(renderd_socketname_maxlen) + ".")); + } + + SECTION("renderd.conf duplicate renderd section names", "should return 7") { + renderd_conf_file << "[mapnik]\n[map]\n"; + renderd_conf_file << "[renderd0]\n[renderd]\n"; + renderd_conf_file.close(); + + if (setjmp(exit_jump) == 0) { + process_config_file(renderd_conf_path.c_str(), 0, 0); + FAIL("process_config_file should have called exit(), but did not"); + } else { + SUCCEED("process_config_file captured expected exit() call"); + } + + REQUIRE(exit_status == 7); + + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Duplicate renderd config section names for section 0: renderd0 & renderd")); + } + + SECTION("renderd.conf renderd section num_threads is -1", "should return 0") { + renderd_conf_file << "[mapnik]\n[map]\n"; + renderd_conf_file << "[renderd0]\nnum_threads=-1\n"; + renderd_conf_file.close(); + + if (setjmp(exit_jump) == 0) { + process_config_file(renderd_conf_path.c_str(), 0, 0); + SUCCEED("process_config_file did not call exit(), as expected"); + } else { + FAIL("process_config_file captured unexpected exit() call"); + } + + REQUIRE(exit_status == 0); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("renderd(0): num_threads = '" + std::to_string(sysconf(_SC_NPROCESSORS_ONLN)) + "'")); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("renderd: num_threads = '" + std::to_string(sysconf(_SC_NPROCESSORS_ONLN)) + "'")); + } + + SECTION("renderd.conf slave renderd sections' num_threads sum equals num_slave_threads", "should return 0") { + renderd_conf_file << "[mapnik]\n[map]\n"; + renderd_conf_file << "[renderd0]\nnum_threads=-1\n[renderd1]\nnum_threads=2\n[renderd2]\nnum_threads=2\n"; + renderd_conf_file.close(); + + if (setjmp(exit_jump) == 0) { + process_config_file(renderd_conf_path.c_str(), 0, 0); + SUCCEED("process_config_file did not call exit(), as expected"); + } else { + FAIL("process_config_file captured unexpected exit() call"); + } + + REQUIRE(exit_status == 0); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("renderd: num_slave_threads = '4'")); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("renderd(1): num_threads = '2'")); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("renderd(2): num_threads = '2'")); + } + + SECTION("renderd.conf renderd section not using unix socketname", "should return 0") { + renderd_conf_file << "[mapnik]\n[map]\n"; + renderd_conf_file << "[renderd0]\niphostname=hostname\nipport=9999\n"; + renderd_conf_file.close(); + + if (setjmp(exit_jump) == 0) { + process_config_file(renderd_conf_path.c_str(), 0, 0); + SUCCEED("process_config_file did not call exit(), as expected"); + } else { + FAIL("process_config_file captured unexpected exit() call"); + } + + REQUIRE(exit_status == 0); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("renderd(0): ip socket = 'hostname:9999'")); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("renderd: ip socket = 'hostname:9999'")); + } + + std::remove(renderd_conf_path.c_str()); +} + +TEST_CASE("renderd_config minimum and maximum function exit handling", "[renderd_config] [min_max_double_opt] [min_max_int_opt]") +{ + const char *opt_type_name = "value"; + double dmax = 1.15; + double dmin = 1.10; + + err_log_lines.clear(); + exit_status = 0; + + SECTION("min_max_double_opt success", "should return 0") { + const char *opt_arg = "1.125"; + + if (setjmp(exit_jump) == 0) { + min_max_double_opt(opt_arg, opt_type_name, dmin, dmax); + SUCCEED("min_max_double_opt did not call exit(), as expected"); + } else { + FAIL("min_max_double_opt captured unexpected exit() call"); + } + + REQUIRE(exit_status == 0); + REQUIRE(err_log_lines == ""); + } + + SECTION("min_max_double_opt exceeds max", "should return 1") { + const char *opt_arg = "2"; + + if (setjmp(exit_jump) == 0) { + min_max_double_opt(opt_arg, opt_type_name, dmin, dmax); + FAIL("min_max_double_opt should have called exit() but didn't"); + } else { + SUCCEED("Captured expected exit() call due to asprintf failure"); + } + REQUIRE(exit_status == 1); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Invalid " + std::string(opt_type_name) + ", must be <= " + std::to_string(dmax) + " (" + std::string(opt_arg) + " was provided)")); + } + + SECTION("min_max_double_opt less than min", "should return 1") { + const char *opt_arg = "1"; + + if (setjmp(exit_jump) == 0) { + min_max_double_opt(opt_arg, opt_type_name, dmin, dmax); + FAIL("min_max_double_opt should have called exit() but didn't"); + } else { + SUCCEED("Captured expected exit() call due to asprintf failure"); + } + REQUIRE(exit_status == 1); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Invalid " + std::string(opt_type_name) + ", must be >= " + std::to_string(dmin) + " (" + std::string(opt_arg) + " was provided)")); + } + + SECTION("min_max_double_opt exceeds max", "should return 1") { + const char *opt_arg = "fail"; + + if (setjmp(exit_jump) == 0) { + min_max_double_opt(opt_arg, opt_type_name, dmin, dmax); + FAIL("min_max_double_opt should have called exit() but didn't"); + } else { + SUCCEED("Captured expected exit() call due to asprintf failure"); + } + REQUIRE(exit_status == 1); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Invalid " + std::string(opt_type_name) + ", must be a double (" + std::string(opt_arg) + " was provided)")); + } + + + SECTION("min_max_int_opt success", "should return 0") { + const char *opt_arg = "1"; + + if (setjmp(exit_jump) == 0) { + min_max_int_opt(opt_arg, opt_type_name, dmin, dmax); + SUCCEED("min_max_int_opt did not call exit(), as expected"); + } else { + FAIL("min_max_int_opt captured unexpected exit() call"); + } + + REQUIRE(exit_status == 0); + REQUIRE(err_log_lines == ""); + } + + SECTION("min_max_int_opt exceeds max", "should return 1") { + const char *opt_arg = "2"; + + if (setjmp(exit_jump) == 0) { + min_max_int_opt(opt_arg, opt_type_name, dmin, dmax); + FAIL("min_max_int_opt should have called exit() but didn't"); + } else { + SUCCEED("Captured expected exit() call due to asprintf failure"); + } + REQUIRE(exit_status == 1); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Invalid " + std::string(opt_type_name) + ", must be <= " + std::to_string((int)dmax) + " (" + std::string(opt_arg) + " was provided)")); + } + + SECTION("min_max_int_opt less than min", "should return 1") { + const char *opt_arg = "0"; + + if (setjmp(exit_jump) == 0) { + min_max_int_opt(opt_arg, opt_type_name, dmin, dmax); + FAIL("min_max_int_opt should have called exit() but didn't"); + } else { + SUCCEED("Captured expected exit() call due to asprintf failure"); + } + REQUIRE(exit_status == 1); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Invalid " + std::string(opt_type_name) + ", must be >= " + std::to_string((int)dmin) + " (" + std::string(opt_arg) + " was provided)")); + } + + SECTION("min_max_int_opt exceeds max", "should return 1") { + const char *opt_arg = "fail"; + + if (setjmp(exit_jump) == 0) { + min_max_int_opt(opt_arg, opt_type_name, dmin, dmax); + FAIL("min_max_int_opt should have called exit() but didn't"); + } else { + SUCCEED("Captured expected exit() call due to asprintf failure"); + } + REQUIRE(exit_status == 1); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Invalid " + std::string(opt_type_name) + ", must be an integer (" + std::string(opt_arg) + " was provided)")); + } +} From 15ca1cc5491f8eb9bddbf1e0c4ed733dfc97e914 Mon Sep 17 00:00:00 2001 From: David Hummel <6109326+hummeltech@users.noreply.github.com> Date: Fri, 6 Feb 2026 11:46:35 -0700 Subject: [PATCH 04/10] Improving tile URI processing logic and other minor improvements --- src/mod_tile.c | 54 +++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 49 insertions(+), 5 deletions(-) diff --git a/src/mod_tile.c b/src/mod_tile.c index f8d7704c..215e12df 100644 --- a/src/mod_tile.c +++ b/src/mod_tile.c @@ -1772,6 +1772,36 @@ static int tile_handler_serve(request_rec *r) return DECLINED; } +static apr_status_t mod_tile_shutdown_handler(void *data) +{ + server_rec *s = (server_rec *)data; + tile_server_conf *scfg = (tile_server_conf *)ap_get_module_config(s->module_config, &tile_module); + tile_config_rec *tile_configs = (tile_config_rec *)scfg->configs->elts; + int tile_configs_count = scfg->configs->nelts; + + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, + "Cleaning up %i configs for server %s", tile_configs_count, s->server_hostname); + + for (int i = 0; i < tile_configs_count; ++i) { + tile_config_rec *tile_config = &tile_configs[i]; + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, + "Freeing tile config for %s", tile_config->xmlname); + free((void *)*tile_config->hostnames); + free((void *)tile_config->hostnames); + free((void *)tile_config->attribution); + free((void *)tile_config->baseuri); + free((void *)tile_config->cors); + free((void *)tile_config->description); + free((void *)tile_config->fileExtension); + free((void *)tile_config->mimeType); + free((void *)tile_config->store); + free((void *)tile_config->xmlname); + free(tile_config); + } + + return APR_SUCCESS; +} + /* * This routine is called in the parent, so we'll set up the shared * memory segment and mutex here. @@ -1940,6 +1970,10 @@ static int mod_tile_post_config(apr_pool_t *pconf, apr_pool_t *plog, #endif /* MOD_TILE_SET_MUTEX_PERMS */ + for (server_rec *virt = s; virt != NULL; virt = virt->next) { + apr_pool_cleanup_register(pconf, (void *)virt, mod_tile_shutdown_handler, apr_pool_cleanup_null); + } + return OK; } @@ -2009,6 +2043,8 @@ static const char *_add_tile_config(cmd_parms *cmd, // Set attribution to default if (attribution_len == 0) { attribution = apr_pstrdup(cmd->pool, DEFAULT_ATTRIBUTION); + } else { + attribution = apr_pstrndup(cmd->pool, attribution, PATH_MAX); } // Ensure URI string ends with a trailing slash @@ -2016,11 +2052,15 @@ static const char *_add_tile_config(cmd_parms *cmd, baseuri = apr_pstrdup(cmd->pool, "/"); } else if (baseuri[baseuri_len - 1] != '/') { baseuri = apr_psprintf(cmd->pool, "%s/", baseuri); + } else { + baseuri = apr_pstrndup(cmd->pool, baseuri, PATH_MAX); } // If cors is empty, set it to NULL if (cors_len == 0) { cors = NULL; + } else { + cors = apr_pstrndup(cmd->pool, cors, PATH_MAX); } // If server_alias is set, increment hostnames_len @@ -2031,6 +2071,8 @@ static const char *_add_tile_config(cmd_parms *cmd, // Set tile_dir to default if (tile_dir_len == 0) { tile_dir = apr_pstrndup(cmd->pool, scfg->tile_dir, PATH_MAX); + } else { + tile_dir = apr_pstrndup(cmd->pool, tile_dir, PATH_MAX); } char **hostnames = (char **)apr_pcalloc(cmd->pool, sizeof(char *) * hostnames_len); @@ -2065,16 +2107,16 @@ static const char *_add_tile_config(cmd_parms *cmd, tilecfg->attribution = attribution; tilecfg->baseuri = baseuri; tilecfg->cors = cors; - tilecfg->description = description; + tilecfg->description = apr_pstrndup(cmd->pool, description, PATH_MAX); tilecfg->enableOptions = enableOptions; - tilecfg->fileExtension = fileExtension; + tilecfg->fileExtension = apr_pstrndup(cmd->pool, fileExtension, PATH_MAX); tilecfg->hostnames = hostnames; tilecfg->maxzoom = maxzoom; - tilecfg->mimeType = mimeType; + tilecfg->mimeType = apr_pstrndup(cmd->pool, mimeType, PATH_MAX); tilecfg->minzoom = minzoom; tilecfg->noHostnames = hostnames_len; tilecfg->store = tile_dir; - tilecfg->xmlname = name; + tilecfg->xmlname = apr_pstrndup(cmd->pool, name, PATH_MAX); if (maxzoom > global_max_zoom) { global_max_zoom = maxzoom; @@ -2161,7 +2203,7 @@ static const char *load_tile_config(cmd_parms *cmd, void *mconfig, const char *c xmlconfigitem maps[XMLCONFIGS_MAX]; - process_map_sections(NULL, config_file_name, maps, "", 0); + process_map_sections(NULL, config_file_name, maps, RENDERD_TILE_DIR, 0); for (int i = 0; i < XMLCONFIGS_MAX; i++) { if (maps[i].xmlname != NULL) { @@ -2176,6 +2218,8 @@ static const char *load_tile_config(cmd_parms *cmd, void *mconfig, const char *c } } + free_map_sections(maps); + return NULL; } From 71c893f898ebcec8173643774a1aceea7bf36819 Mon Sep 17 00:00:00 2001 From: David Hummel <6109326+hummeltech@users.noreply.github.com> Date: Tue, 3 Feb 2026 11:45:23 -0700 Subject: [PATCH 05/10] Moving more functions into catch_test_common --- tests/catch_test_common.cpp | 54 ++++++++++++++++++++++++++++ tests/catch_test_common.hpp | 7 ++++ tests/unit_test_renderd_config.cpp | 57 +++--------------------------- 3 files changed, 65 insertions(+), 53 deletions(-) diff --git a/tests/catch_test_common.cpp b/tests/catch_test_common.cpp index 179f7eef..888b5651 100644 --- a/tests/catch_test_common.cpp +++ b/tests/catch_test_common.cpp @@ -17,6 +17,7 @@ #include #include +#include #include #include #include @@ -27,6 +28,9 @@ extern int foreground; +int exit_status = 0; +bool fail_next = false; +jmp_buf exit_jump; std::string err_log_lines, out_log_lines; captured_stdio captured_stderr; @@ -179,3 +183,53 @@ std::tuple end_capture(bool print) return std::tuple(get_captured_stderr(print), get_captured_stdout(print)); } + +extern "C" { + int mocked_asprintf(char **strp, const char *fmt, ...) + { + if (fail_next) { + fail_next = false; + *strp = nullptr; + return -1; + } + + va_list args; + va_start(args, fmt); + int result = vasprintf(strp, fmt, args); + va_end(args); + return result; + } + + void mocked_exit(int status) + { + exit_status = status; + longjmp(exit_jump, 1); + } + + void mocked_g_logger(int log_level, const char *format, ...) + { + char *log_message; + va_list args; + + va_start(args, format); + + vasprintf(&log_message, format, args); + + va_end(args); + + err_log_lines.append(log_message); + err_log_lines.append("\n"); + + free(log_message); + } + + char *mocked_strndup(const char *s, size_t n) + { + if (fail_next) { + fail_next = false; + return nullptr; + } + + return strndup(s, n); + } +} diff --git a/tests/catch_test_common.hpp b/tests/catch_test_common.hpp index 1678190b..9745dc4b 100644 --- a/tests/catch_test_common.hpp +++ b/tests/catch_test_common.hpp @@ -40,4 +40,11 @@ std::tuple end_capture(bool print = false); int run_command(const std::string &file, std::vector argv = {}, const std::string &input = ""); +extern "C" { + int mocked_asprintf(char **strp, const char *fmt, ...); + void mocked_exit(int status); + void mocked_g_logger(int log_level, const char *format, ...); + char *mocked_strndup(const char *s, size_t n); +} + #endif diff --git a/tests/unit_test_renderd_config.cpp b/tests/unit_test_renderd_config.cpp index be4d562b..1a2ac742 100644 --- a/tests/unit_test_renderd_config.cpp +++ b/tests/unit_test_renderd_config.cpp @@ -1,4 +1,3 @@ -#include #include #include #include @@ -15,60 +14,12 @@ #define RENDERD_CONF "./etc/renderd/renderd.conf.examples" #endif -int exit_status = 0; -static bool fail_next = false; -static jmp_buf exit_jump; +extern int exit_status; +extern bool fail_next; +extern jmp_buf exit_jump; extern std::string err_log_lines; extern "C" { - int mocked_asprintf(char **strp, const char *fmt, ...) - { - if (fail_next) { - fail_next = false; - *strp = nullptr; - return -1; - } - - va_list args; - va_start(args, fmt); - int result = vasprintf(strp, fmt, args); - va_end(args); - return result; - } - - void mocked_exit(int status) - { - exit_status = status; - longjmp(exit_jump, 1); - } - - void mocked_g_logger(int log_level, const char *format, ...) - { - char *log_message; - va_list args; - - va_start(args, format); - - vasprintf(&log_message, format, args); - - va_end(args); - - err_log_lines.append(log_message); - err_log_lines.append("\n"); - - free(log_message); - } - - char *mocked_strndup(const char *s, size_t n) - { - if (fail_next) { - fail_next = false; - return nullptr; - } - - return strndup(s, n); - } - #define asprintf mocked_asprintf #define exit mocked_exit #define g_logger mocked_g_logger @@ -611,9 +562,9 @@ TEST_CASE("renderd.conf file processing exit handling", "[renderd_config] [proce } REQUIRE(exit_status == 0); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("renderd: num_slave_threads = '4'")); REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("renderd(1): num_threads = '2'")); REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("renderd(2): num_threads = '2'")); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("renderd: num_slave_threads = '4'")); } SECTION("renderd.conf renderd section not using unix socketname", "should return 0") { From c2a4fb49c6e1b4063a541dea9b261eeb24a8c37e Mon Sep 17 00:00:00 2001 From: David Hummel <6109326+hummeltech@users.noreply.github.com> Date: Wed, 4 Feb 2026 07:37:39 -0700 Subject: [PATCH 06/10] Continue adding unit tests --- .github/actions/cmake/test/action.yml | 2 +- includes/renderd_config.h | 6 +- src/CMakeLists.txt | 87 +++++- src/convert_meta.c | 2 +- src/mod_tile.c | 34 -- src/parameterize_style.cpp | 3 +- src/protocol_helper.c | 4 +- src/render_expired.c | 2 +- src/render_list.c | 2 +- src/render_old.c | 2 +- src/render_speedtest.cpp | 2 +- src/renderd.c | 22 +- src/renderd_config.c | 92 +++--- src/store.c | 5 +- src/store_file.c | 11 +- src/store_null.c | 2 +- tests/CMakeLists.txt | 20 ++ tests/catch_test_common.cpp | 324 ++++++++++++++----- tests/catch_test_common.hpp | 19 +- tests/gen_tile_test.cpp | 89 +----- tests/unit_test_cache_expire.cpp | 157 ++++++++++ tests/unit_test_metatile.cpp | 174 +++++++++++ tests/unit_test_parameterize_style.cpp | 100 ++++++ tests/unit_test_protocol_helper.cpp | 216 +++++++++++++ tests/unit_test_renderd_config.cpp | 411 +++++++++++++++++-------- 25 files changed, 1395 insertions(+), 393 deletions(-) create mode 100644 tests/unit_test_cache_expire.cpp create mode 100644 tests/unit_test_metatile.cpp create mode 100644 tests/unit_test_parameterize_style.cpp create mode 100644 tests/unit_test_protocol_helper.cpp diff --git a/.github/actions/cmake/test/action.yml b/.github/actions/cmake/test/action.yml index d6afbf75..4add026f 100644 --- a/.github/actions/cmake/test/action.yml +++ b/.github/actions/cmake/test/action.yml @@ -1,7 +1,7 @@ --- inputs: options: - default: --exclude-regex 'clear_dirs_.+|remove_tile_.+' --output-on-failure + default: --exclude-regex 'clear_dirs_.+|remove_tile_.+' --output-on-failure --repeat until-pass:3 runs: using: composite diff --git a/includes/renderd_config.h b/includes/renderd_config.h index ccdd1f61..85b868b5 100644 --- a/includes/renderd_config.h +++ b/includes/renderd_config.h @@ -32,15 +32,15 @@ extern "C" { #endif int num_slave_threads; -renderd_config config; +renderd_config *config; renderd_config config_slaves[MAX_SLAVES]; xmlconfigitem maps[XMLCONFIGS_MAX]; double min_max_double_opt(const char *opt_arg, const char *opt_type_name, double minimum, double maximum); int min_max_int_opt(const char *opt_arg, const char *opt_type_name, int minimum, int maximum); -void free_map_section(xmlconfigitem map_section); +void free_map_section(xmlconfigitem *map_section); void free_map_sections(xmlconfigitem *map_sections); -void free_renderd_section(renderd_config renderd_section); +void free_renderd_section(renderd_config *renderd_section); void free_renderd_sections(renderd_config *renderd_sections); void process_config_file(const char *config_file_name, int active_renderd_section_num, int log_level); void process_map_sections(dictionary *ini, const char *config_file_name, xmlconfigitem *maps_dest, const char *default_tile_dir, int num_threads); diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index c36f3166..03640376 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -212,12 +212,97 @@ set(gen_tile_test_SRCS ) set(gen_tile_test_LIBS ${renderd_LIBS} + ${STORE_LIBRARIES} ) add_executable(gen_tile_test ${gen_tile_test_SRCS}) +set_target_properties(gen_tile_test PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/tests) target_compile_definitions(gen_tile_test PRIVATE MAIN_ALREADY_DEFINED) target_include_directories(gen_tile_test PRIVATE ${PROJECT_SOURCE_DIR}/tests) target_link_libraries(gen_tile_test ${gen_tile_test_LIBS}) -set_target_properties(gen_tile_test PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/tests) + + +#----------------------------------------------------------------------------- +# +# unit_test_cache_expire +# +#----------------------------------------------------------------------------- + +set(unit_test_cache_expire_SRCS + $ + ${PROJECT_SOURCE_DIR}/tests/unit_test_cache_expire.cpp +) +set(unit_test_cache_expire_LIBS + ${GLIB_LIBRARIES} +) +add_executable(unit_test_cache_expire ${unit_test_cache_expire_SRCS}) +set_target_properties(unit_test_cache_expire PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/tests) +target_include_directories(unit_test_cache_expire PRIVATE ${PROJECT_SOURCE_DIR}/src ${PROJECT_SOURCE_DIR}/tests) +target_link_libraries(unit_test_cache_expire ${unit_test_cache_expire_LIBS}) + + +#----------------------------------------------------------------------------- +# +# unit_test_metatile +# +#----------------------------------------------------------------------------- + +set(unit_test_metatile_SRCS + $ + ${PROJECT_SOURCE_DIR}/tests/unit_test_metatile.cpp +) +set(unit_test_metatile_LIBS + ${GLIB_LIBRARIES} +) +add_executable(unit_test_metatile ${unit_test_metatile_SRCS}) +set_target_properties(unit_test_metatile PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/tests) +target_include_directories(unit_test_metatile PRIVATE ${PROJECT_SOURCE_DIR}/src ${PROJECT_SOURCE_DIR}/tests) +target_link_libraries(unit_test_metatile ${unit_test_metatile_LIBS}) + + +#----------------------------------------------------------------------------- +# +# unit_test_parameterize_style +# +#----------------------------------------------------------------------------- + +set(unit_test_parameterize_style_SRCS + $ + ${PROJECT_SOURCE_DIR}/tests/unit_test_parameterize_style.cpp +) +set(unit_test_parameterize_style_LIBS + ${GLIB_LIBRARIES} + ${LIBMAPNIK_LIBRARIES} +) +add_executable(unit_test_parameterize_style ${unit_test_parameterize_style_SRCS}) +set_target_properties(unit_test_parameterize_style PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/tests) +target_compile_definitions(unit_test_parameterize_style PRIVATE + MAPNIK_XML="${PROJECT_SOURCE_DIR}/utils/example-map/mapnik.xml" +) +target_include_directories(unit_test_parameterize_style PRIVATE ${PROJECT_SOURCE_DIR}/src ${PROJECT_SOURCE_DIR}/tests) +target_link_libraries(unit_test_parameterize_style ${unit_test_parameterize_style_LIBS}) + + +#----------------------------------------------------------------------------- +# +# unit_test_protocol_helper +# +#----------------------------------------------------------------------------- + +set(unit_test_protocol_helper_SRCS + $ + ${PROJECT_SOURCE_DIR}/tests/unit_test_protocol_helper.cpp +) +set(unit_test_protocol_helper_LIBS + ${GLIB_LIBRARIES} + ${INIPARSER_LIBRARIES} +) +add_executable(unit_test_protocol_helper ${unit_test_protocol_helper_SRCS}) +set_target_properties(unit_test_protocol_helper PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/tests) +target_compile_definitions(unit_test_protocol_helper PRIVATE + RENDERD_CONF="${PROJECT_SOURCE_DIR}/etc/renderd/renderd.conf.examples" +) +target_include_directories(unit_test_protocol_helper PRIVATE ${PROJECT_SOURCE_DIR}/src ${PROJECT_SOURCE_DIR}/tests) +target_link_libraries(unit_test_protocol_helper ${unit_test_protocol_helper_LIBS}) #----------------------------------------------------------------------------- diff --git a/src/convert_meta.c b/src/convert_meta.c index ef2c1d0f..a7873f81 100644 --- a/src/convert_meta.c +++ b/src/convert_meta.c @@ -127,7 +127,7 @@ static void descend(const char *search) int main(int argc, char **argv) { int z, c; - const char *map = "default"; + const char *map = XMLCONFIG_DEFAULT; while (1) { int option_index = 0; diff --git a/src/mod_tile.c b/src/mod_tile.c index 215e12df..1723a9c1 100644 --- a/src/mod_tile.c +++ b/src/mod_tile.c @@ -1772,36 +1772,6 @@ static int tile_handler_serve(request_rec *r) return DECLINED; } -static apr_status_t mod_tile_shutdown_handler(void *data) -{ - server_rec *s = (server_rec *)data; - tile_server_conf *scfg = (tile_server_conf *)ap_get_module_config(s->module_config, &tile_module); - tile_config_rec *tile_configs = (tile_config_rec *)scfg->configs->elts; - int tile_configs_count = scfg->configs->nelts; - - ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, - "Cleaning up %i configs for server %s", tile_configs_count, s->server_hostname); - - for (int i = 0; i < tile_configs_count; ++i) { - tile_config_rec *tile_config = &tile_configs[i]; - ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, - "Freeing tile config for %s", tile_config->xmlname); - free((void *)*tile_config->hostnames); - free((void *)tile_config->hostnames); - free((void *)tile_config->attribution); - free((void *)tile_config->baseuri); - free((void *)tile_config->cors); - free((void *)tile_config->description); - free((void *)tile_config->fileExtension); - free((void *)tile_config->mimeType); - free((void *)tile_config->store); - free((void *)tile_config->xmlname); - free(tile_config); - } - - return APR_SUCCESS; -} - /* * This routine is called in the parent, so we'll set up the shared * memory segment and mutex here. @@ -1970,10 +1940,6 @@ static int mod_tile_post_config(apr_pool_t *pconf, apr_pool_t *plog, #endif /* MOD_TILE_SET_MUTEX_PERMS */ - for (server_rec *virt = s; virt != NULL; virt = virt->next) { - apr_pool_cleanup_register(pconf, (void *)virt, mod_tile_shutdown_handler, apr_pool_cleanup_null); - } - return OK; } diff --git a/src/parameterize_style.cpp b/src/parameterize_style.cpp index 43cca310..6be62600 100644 --- a/src/parameterize_style.cpp +++ b/src/parameterize_style.cpp @@ -91,7 +91,6 @@ parameterize_function_ptr init_parameterization_function(const char *function_na return parameterize_map_language; } else { g_logger(G_LOG_LEVEL_WARNING, "unknown parameterization function for '%s'", function_name); + return NULL; } - - return NULL; } diff --git a/src/protocol_helper.c b/src/protocol_helper.c index 71a1ff09..2da25166 100644 --- a/src/protocol_helper.c +++ b/src/protocol_helper.c @@ -83,11 +83,11 @@ int recv_cmd(struct protocol * cmd, int fd, int block) break; case 2: - ret2 = recv(fd, ((void*)cmd) + sizeof(struct protocol_v1), sizeof(struct protocol_v2) - sizeof(struct protocol_v1), block ? MSG_WAITALL : MSG_DONTWAIT); + ret2 = recv(fd, ((char *)cmd) + sizeof(struct protocol_v1), sizeof(struct protocol_v2) - sizeof(struct protocol_v1), block ? MSG_WAITALL : MSG_DONTWAIT); break; case 3: - ret2 = recv(fd, ((void*)cmd) + sizeof(struct protocol_v1), sizeof(struct protocol) - sizeof(struct protocol_v1), block ? MSG_WAITALL : MSG_DONTWAIT); + ret2 = recv(fd, ((char *)cmd) + sizeof(struct protocol_v1), sizeof(struct protocol) - sizeof(struct protocol_v1), block ? MSG_WAITALL : MSG_DONTWAIT); break; } diff --git a/src/render_expired.c b/src/render_expired.c index ff58753f..73e8aa11 100644 --- a/src/render_expired.c +++ b/src/render_expired.c @@ -296,7 +296,7 @@ int main(int argc, char **argv) } if (!socketname_passed) { - socketname = strndup(config.socketname, PATH_MAX); + socketname = strndup(config->socketname, PATH_MAX); socketname_passed = 1; } diff --git a/src/render_list.c b/src/render_list.c index 80358b89..ef42cf93 100644 --- a/src/render_list.c +++ b/src/render_list.c @@ -350,7 +350,7 @@ int main(int argc, char **argv) } if (!socketname_passed) { - socketname = strndup(config.socketname, PATH_MAX); + socketname = strndup(config->socketname, PATH_MAX); socketname_passed = 1; } diff --git a/src/render_old.c b/src/render_old.c index 5c02756f..13b0bd60 100644 --- a/src/render_old.c +++ b/src/render_old.c @@ -357,7 +357,7 @@ int main(int argc, char **argv) } if (!socketname_passed) { - socketname = strndup(config.socketname, PATH_MAX); + socketname = strndup(config->socketname, PATH_MAX); socketname_passed = 1; } diff --git a/src/render_speedtest.cpp b/src/render_speedtest.cpp index a4511e26..059fc0fa 100644 --- a/src/render_speedtest.cpp +++ b/src/render_speedtest.cpp @@ -274,7 +274,7 @@ int main(int argc, char **argv) } if (!socketname_passed) { - socketname = strndup(config.socketname, PATH_MAX); + socketname = strndup(config->socketname, PATH_MAX); socketname_passed = 1; } } diff --git a/src/renderd.c b/src/renderd.c index 81e0bda1..cf331baa 100644 --- a/src/renderd.c +++ b/src/renderd.c @@ -122,7 +122,7 @@ enum protoCmd rx_request(struct protocol *req, int fd) // Upgrade version 1 and 2 to version 3 if (req->ver == 1) { - strcpy(req->xmlname, "default"); + strcpy(req->xmlname, XMLCONFIG_DEFAULT); } if (req->ver < 3) { @@ -311,7 +311,7 @@ void *stats_writeout_thread(void * arg) int noFailedAttempts = 0; char tmpName[PATH_MAX]; - snprintf(tmpName, sizeof(tmpName), "%s.tmp", config.stats_filename); + snprintf(tmpName, sizeof(tmpName), "%s.tmp", config->stats_filename); g_logger(G_LOG_LEVEL_DEBUG, "Starting stats writeout thread: %lu", (unsigned long) pthread_self()); @@ -364,7 +364,7 @@ void *stats_writeout_thread(void * arg) fclose(statfile); - if (rename(tmpName, config.stats_filename)) { + if (rename(tmpName, config->stats_filename)) { g_logger(G_LOG_LEVEL_WARNING, "Failed to overwrite stats file: %i", errno); noFailedAttempts++; @@ -816,7 +816,7 @@ int main(int argc, char **argv) return 1; } - fd = server_socket_init(&config); + fd = server_socket_init(config); #if 0 @@ -844,7 +844,7 @@ int main(int argc, char **argv) sigaction(SIGTERM, &sigExitAction, NULL); - render_init(config.mapnik_plugins_dir, config.mapnik_font_dir, config.mapnik_font_dir_recurse); + render_init(config->mapnik_plugins_dir, config->mapnik_font_dir, config->mapnik_font_dir_recurse); /* unless the command line said to run in foreground mode, fork and detach from terminal */ if (foreground) { @@ -855,7 +855,7 @@ int main(int argc, char **argv) } /* write pid file */ - FILE *pidfile = fopen(config.pid_filename, "w"); + FILE *pidfile = fopen(config->pid_filename, "w"); if (pidfile) { (void) fprintf(pidfile, "%d\n", getpid()); @@ -863,7 +863,7 @@ int main(int argc, char **argv) } } - if (strnlen(config.stats_filename, PATH_MAX - 1)) { + if (strnlen(config->stats_filename, PATH_MAX - 1)) { if (pthread_create(&stats_thread, NULL, stats_writeout_thread, NULL)) { g_logger(G_LOG_LEVEL_CRITICAL, "Could not spawn stats writeout thread"); close(fd); @@ -873,9 +873,9 @@ int main(int argc, char **argv) g_logger(G_LOG_LEVEL_INFO, "No stats file specified in config. Stats reporting disabled"); } - render_threads = (pthread_t *) malloc(sizeof(pthread_t) * config.num_threads); + render_threads = (pthread_t *) malloc(sizeof(pthread_t) * config->num_threads); - for (i = 0; i < config.num_threads; i++) { + for (i = 0; i < config->num_threads; i++) { if (pthread_create(&render_threads[i], NULL, render_thread, (void *)maps)) { g_logger(G_LOG_LEVEL_CRITICAL, "Could not spawn rendering thread"); close(fd); @@ -901,14 +901,14 @@ int main(int argc, char **argv) for (i = 0; i < MAX_SLAVES; i++) { if (active_renderd_section_num != i && config_slaves[i].num_threads != 0) { g_logger(G_LOG_LEVEL_DEBUG, "Freeing unused renderd config section %i: %s", i, config_slaves[i].name); - free_renderd_section(config_slaves[i]); + free_renderd_section(&config_slaves[i]); } } } process_loop(fd); - unlink(config.socketname); + unlink(config->socketname); free_map_sections(maps); free_renderd_sections(config_slaves); close(fd); diff --git a/src/renderd_config.c b/src/renderd_config.c index a4f7ceed..5bf98ae5 100644 --- a/src/renderd_config.c +++ b/src/renderd_config.c @@ -48,6 +48,16 @@ static char *name_with_section(const char *section, const char *name) int len; char *key; + if (!section) { + g_logger(G_LOG_LEVEL_CRITICAL, "name_with_section: invalid section %s", section); + exit(7); + } + + if (!name) { + g_logger(G_LOG_LEVEL_CRITICAL, "name_with_section: invalid name %s", name); + exit(7); + } + len = asprintf(&key, "%s:%s", section, name); if (len == -1) { @@ -106,52 +116,52 @@ static void process_config_string(const dictionary *ini, const char *section, co free(key); } -void free_map_section(xmlconfigitem map_section) +void free_map_section(xmlconfigitem *map_section) { - free((void *)map_section.attribution); - free((void *)map_section.cors); - free((void *)map_section.description); - free((void *)map_section.file_extension); - free((void *)map_section.host); - free((void *)map_section.htcpip); - free((void *)map_section.mime_type); - free((void *)map_section.output_format); - free((void *)map_section.parameterization); - free((void *)map_section.server_alias); - free((void *)map_section.tile_dir); - free((void *)map_section.xmlfile); - free((void *)map_section.xmlname); - free((void *)map_section.xmluri); - bzero(&map_section, sizeof(xmlconfigitem)); + free((void *)map_section->attribution); + free((void *)map_section->cors); + free((void *)map_section->description); + free((void *)map_section->file_extension); + free((void *)map_section->host); + free((void *)map_section->htcpip); + free((void *)map_section->mime_type); + free((void *)map_section->output_format); + free((void *)map_section->parameterization); + free((void *)map_section->server_alias); + free((void *)map_section->tile_dir); + free((void *)map_section->xmlfile); + free((void *)map_section->xmlname); + free((void *)map_section->xmluri); + bzero(map_section, sizeof(xmlconfigitem)); } void free_map_sections(xmlconfigitem *map_sections) { for (int i = 0; i < XMLCONFIGS_MAX; i++) { if (map_sections[i].xmlname != NULL) { - free_map_section(map_sections[i]); + free_map_section(&map_sections[i]); } } } -void free_renderd_section(renderd_config renderd_section) +void free_renderd_section(renderd_config *renderd_section) { - free((void *)renderd_section.iphostname); - free((void *)renderd_section.mapnik_font_dir); - free((void *)renderd_section.mapnik_plugins_dir); - free((void *)renderd_section.name); - free((void *)renderd_section.pid_filename); - free((void *)renderd_section.socketname); - free((void *)renderd_section.stats_filename); - free((void *)renderd_section.tile_dir); - bzero(&renderd_section, sizeof(renderd_config)); + free((void *)renderd_section->iphostname); + free((void *)renderd_section->mapnik_font_dir); + free((void *)renderd_section->mapnik_plugins_dir); + free((void *)renderd_section->name); + free((void *)renderd_section->pid_filename); + free((void *)renderd_section->socketname); + free((void *)renderd_section->stats_filename); + free((void *)renderd_section->tile_dir); + bzero(renderd_section, sizeof(renderd_config)); } void free_renderd_sections(renderd_config *renderd_sections) { for (int i = 0; i < MAX_SLAVES; i++) { if (renderd_sections[i].num_threads != 0) { - free_renderd_section(renderd_sections[i]); + free_renderd_section(&renderd_sections[i]); } } } @@ -459,7 +469,7 @@ void process_config_file(const char *config_file_name, int active_renderd_sectio { extern int num_slave_threads; - extern renderd_config config; + extern renderd_config *config; extern renderd_config config_slaves[MAX_SLAVES]; extern xmlconfigitem maps[XMLCONFIGS_MAX]; @@ -492,7 +502,7 @@ void process_config_file(const char *config_file_name, int active_renderd_sectio process_map_sections(ini, config_file_name, maps, config_slaves[active_renderd_section_num].tile_dir, config_slaves[active_renderd_section_num].num_threads); iniparser_freedict(ini); - config = config_slaves[active_renderd_section_num]; + config = &config_slaves[active_renderd_section_num]; for (int i = 0; i < MAX_SLAVES; i++) { if (config_slaves[i].num_threads == 0) { @@ -521,28 +531,28 @@ void process_config_file(const char *config_file_name, int active_renderd_sectio g_logger(G_LOG_LEVEL_DEBUG, "\trenderd(%i): tile_dir = '%s'", i, config_slaves[i].tile_dir); } - if (config.ipport > 0) { - g_logger(log_level, "\trenderd: ip socket = '%s:%i'", config.iphostname, config.ipport); + if (config->ipport > 0) { + g_logger(log_level, "\trenderd: ip socket = '%s:%i'", config->iphostname, config->ipport); } else { - g_logger(log_level, "\trenderd: unix socketname = '%s'", config.socketname); + g_logger(log_level, "\trenderd: unix socketname = '%s'", config->socketname); } - g_logger(log_level, "\trenderd: num_threads = '%i'", config.num_threads); + g_logger(log_level, "\trenderd: num_threads = '%i'", config->num_threads); if (active_renderd_section_num == 0 && num_slave_threads > 0) { g_logger(log_level, "\trenderd: num_slave_threads = '%i'", num_slave_threads); } - g_logger(log_level, "\trenderd: pid_file = '%s'", config.pid_filename); + g_logger(log_level, "\trenderd: pid_file = '%s'", config->pid_filename); - if (strnlen(config.stats_filename, PATH_MAX)) { - g_logger(log_level, "\trenderd: stats_file = '%s'", config.stats_filename); + if (strnlen(config->stats_filename, PATH_MAX)) { + g_logger(log_level, "\trenderd: stats_file = '%s'", config->stats_filename); } - g_logger(log_level, "\trenderd: tile_dir = '%s'", config.tile_dir); - g_logger(log_level, "\tmapnik: font_dir = '%s'", config.mapnik_font_dir); - g_logger(log_level, "\tmapnik: font_dir_recurse = '%s'", config.mapnik_font_dir_recurse ? "true" : "false"); - g_logger(log_level, "\tmapnik: plugins_dir = '%s'", config.mapnik_plugins_dir); + g_logger(log_level, "\trenderd: tile_dir = '%s'", config->tile_dir); + g_logger(log_level, "\tmapnik: font_dir = '%s'", config->mapnik_font_dir); + g_logger(log_level, "\tmapnik: font_dir_recurse = '%s'", config->mapnik_font_dir_recurse ? "true" : "false"); + g_logger(log_level, "\tmapnik: plugins_dir = '%s'", config->mapnik_plugins_dir); for (int i = 0; i < XMLCONFIGS_MAX; i++) { if (maps[i].xmlname != NULL) { diff --git a/src/store.c b/src/store.c index b25a09c2..7a0e0331 100644 --- a/src/store.c +++ b/src/store.c @@ -24,10 +24,7 @@ #include "store_null.h" #include "g_logger.h" -/** - * In Apache 2.2, we call the init_storage_backend once per process. For mpm_worker and mpm_event multiple threads therefore use the same - * storage context, and all storage backends need to be thread-safe in order not to cause issues with these mpm's - * +/* * In Apache 2.4, we call the init_storage_backend once per thread, and therefore each thread has its own storage context to work with. */ struct storage_backend * init_storage_backend(const char * options) diff --git a/src/store_file.c b/src/store_file.c index b1409aa7..35ade3be 100644 --- a/src/store_file.c +++ b/src/store_file.c @@ -74,7 +74,7 @@ static int file_tile_read(struct storage_backend * store, const char *xmlconfig, struct meta_layout *m = (struct meta_layout *)malloc(header_len); size_t file_offset, tile_size; - meta_offset = xyzo_to_meta(path, sizeof(path), store->storage_ctx, xmlconfig, options, x, y, z); + meta_offset = xyzo_to_meta(path, sizeof(path), (char *)(store->storage_ctx), xmlconfig, options, x, y, z); fd = open(path, O_RDONLY); @@ -189,7 +189,7 @@ static struct stat_info file_tile_stat(struct storage_backend * store, const cha tile_stat.ctime = st_stat.st_ctime; } - if (tile_stat.mtime < getPlanetTime(store->storage_ctx, xmlconfig)) { + if (tile_stat.mtime < getPlanetTime((char *)(store->storage_ctx), xmlconfig)) { tile_stat.expired = 1; } else { tile_stat.expired = 0; @@ -218,10 +218,11 @@ static int file_metatile_write(struct storage_backend * store, const char *xmlco xyzo_to_meta(meta_path, sizeof(meta_path), (char *)(store->storage_ctx), xmlconfig, options, x, y, z); g_logger(G_LOG_LEVEL_DEBUG, "Creating and writing a metatile to %s", meta_path); - tmp = malloc(sizeof(char) * strlen(meta_path) + 24); + tmp = (char *)malloc(sizeof(char) * strlen(meta_path) + 24); snprintf(tmp, strlen(meta_path) + 24, "%s.%lu", meta_path, (unsigned long) pthread_self()); if (mkdirp(tmp)) { + g_logger(G_LOG_LEVEL_WARNING, "Error creating directory %s: %s", tmp, strerror(errno)); free(tmp); return -1; } @@ -270,7 +271,7 @@ static int file_metatile_expire(struct storage_backend * store, const char *xmlc struct utimbuf touchTime; //TODO: deal with options - xyz_to_meta(name, sizeof(name), store->storage_ctx, xmlconfig, x, y, z); + xyz_to_meta(name, sizeof(name), (char *)(store->storage_ctx), xmlconfig, x, y, z); if (stat(name, &s) == 0) {// 0 is success // tile exists on disk; mark it as expired @@ -306,7 +307,7 @@ static int file_close_storage(struct storage_backend * store) struct storage_backend * init_storage_file(const char * tile_dir) { - struct storage_backend * store = malloc(sizeof(struct storage_backend)); + struct storage_backend * store = (struct storage_backend *)malloc(sizeof(struct storage_backend)); if (store == NULL) { g_logger(G_LOG_LEVEL_ERROR, "init_storage_file: Failed to allocate memory for storage backend"); diff --git a/src/store_null.c b/src/store_null.c index 1d8730c0..8f30b86b 100644 --- a/src/store_null.c +++ b/src/store_null.c @@ -90,7 +90,7 @@ static int close_storage(struct storage_backend * store) struct storage_backend *init_storage_null() { - struct storage_backend *store = malloc(sizeof * store); + struct storage_backend *store = (struct storage_backend *)malloc(sizeof * store); if (store == NULL) { g_logger(G_LOG_LEVEL_ERROR, "init_storage_null: Failed to allocate memory for storage backend"); diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 00b29a65..6e8b80be 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -220,6 +220,26 @@ add_test( COMMAND renderd_test ) +add_test( + NAME unit_test_cache_expire + COMMAND unit_test_cache_expire +) + +add_test( + NAME unit_test_metatile + COMMAND unit_test_metatile +) + +add_test( + NAME unit_test_parameterize_style + COMMAND unit_test_parameterize_style +) + +add_test( + NAME unit_test_protocol_helper + COMMAND unit_test_protocol_helper +) + add_test( NAME unit_test_renderd_config COMMAND unit_test_renderd_config diff --git a/tests/catch_test_common.cpp b/tests/catch_test_common.cpp index 888b5651..a7152344 100644 --- a/tests/catch_test_common.cpp +++ b/tests/catch_test_common.cpp @@ -15,11 +15,20 @@ * along with this program; If not, see http://www.gnu.org/licenses/. */ +#include +#include +#include +#include #include #include +#include #include #include +#include +#include #include +#include +#include #include #include @@ -29,46 +38,37 @@ extern int foreground; int exit_status = 0; -bool fail_next = false; +int mock_errno = 0; + +bool fail_next_asprintf = false; +bool fail_next_connect = false; +bool fail_next_getaddrinfo = false; +bool fail_next_getaddrinfo_empty_res = false; +bool fail_next_malloc = false; +bool fail_next_mkdir = false; +bool fail_next_open = false; +bool fail_next_socket = false; +bool fail_next_strndup = false; +bool fail_next_strtok = false; +bool fail_next_write = false; + jmp_buf exit_jump; std::string err_log_lines, out_log_lines; captured_stdio captured_stderr; captured_stdio captured_stdout; -int run_command(const std::string &file, std::vector argv, const std::string &input) +std::string read_stderr(int buffer_size) { - auto mode = redi::pstreams::pstdout | redi::pstreams::pstderr | redi::pstreams::pstdin; - - argv.insert(argv.begin(), file); - - redi::pstream proc(file, argv, mode); - - std::string line; - err_log_lines = ""; - out_log_lines = ""; - - if (!input.empty()) { - proc << input << redi::peof; - } - - while (std::getline(proc.err(), line)) { - err_log_lines += line; - } - - proc.clear(); - - while (std::getline(proc.out(), line)) { - out_log_lines += line; - } - - return proc.close(); + char buffer[buffer_size]; + read(captured_stderr.pipes[0], buffer, buffer_size); + return buffer; } -std::string read_stderr(int buffer_size) +std::string read_stdout(int buffer_size) { char buffer[buffer_size]; - read(captured_stderr.pipes[0], buffer, buffer_size); + read(captured_stdout.pipes[0], buffer, buffer_size); return buffer; } @@ -91,32 +91,6 @@ void capture_stderr() captured_stderr.pipes[1] = pipes[1]; } -std::string get_captured_stderr(bool print) -{ - // Terminate captured output with a zero - write(captured_stderr.pipes[1], "", 1); - - // Restore stderr - fflush(stderr); - dup2(captured_stderr.temp_fd, fileno(stderr)); - - // Save & return the captured stderr - std::string log_lines = read_stderr(); - - if (print) { - std::cout << "err_log_lines: " << log_lines << "\n"; - } - - return log_lines; -} - -std::string read_stdout(int buffer_size) -{ - char buffer[buffer_size]; - read(captured_stdout.pipes[0], buffer, buffer_size); - return buffer; -} - void capture_stdout() { // Flush stdout first if you've previously printed something @@ -136,6 +110,25 @@ void capture_stdout() captured_stdout.pipes[1] = pipes[1]; } +std::string get_captured_stderr(bool print) +{ + // Terminate captured output with a zero + write(captured_stderr.pipes[1], "", 1); + + // Restore stderr + fflush(stderr); + dup2(captured_stderr.temp_fd, fileno(stderr)); + + // Save & return the captured stderr + std::string log_lines = read_stderr(); + + if (print) { + std::cout << "err_log_lines: " << log_lines << "\n"; + } + + return log_lines; +} + std::string get_captured_stdout(bool print) { // Terminate captured output with a zero @@ -184,22 +177,108 @@ std::tuple end_capture(bool print) return std::tuple(get_captured_stderr(print), get_captured_stdout(print)); } -extern "C" { - int mocked_asprintf(char **strp, const char *fmt, ...) - { - if (fail_next) { - fail_next = false; - *strp = nullptr; - return -1; +int run_command(const std::string &file, std::vector argv, const std::string &input) +{ + auto mode = redi::pstreams::pstdout | redi::pstreams::pstderr | redi::pstreams::pstdin; + + argv.insert(argv.begin(), file); + + redi::pstream proc(file, argv, mode); + + std::string line; + err_log_lines = ""; + out_log_lines = ""; + + if (!input.empty()) { + proc << input << redi::peof; + } + + while (std::getline(proc.err(), line)) { + err_log_lines += line; + } + + proc.clear(); + + while (std::getline(proc.out(), line)) { + out_log_lines += line; + } + + return proc.close(); +} + +std::string create_tile_dir(const std::string &dir_name, const char *tmp_dir) +{ + if (tmp_dir == NULL) { + tmp_dir = P_tmpdir; + } + + std::string tile_dir(tmp_dir); + tile_dir.append("/").append(dir_name); + + mkdir(tile_dir.c_str(), 0777); + + return tile_dir; +} + +int remove_recursive(const char *path) +{ + struct stat st; + + // 1. Get path metadata + if (lstat(path, &st) != 0) { + return -1; + } + + // 2. If it's not a directory, just delete it (file, symlink, etc.) + if (!S_ISDIR(st.st_mode)) { + return unlink(path); + } + + // 3. It's a directory: open it to iterate over contents + DIR *d = opendir(path); + + if (!d) { + return -1; + } + + struct dirent *entry; + + int result = 0; + + while ((entry = readdir(d)) != NULL) { + // Skip the special "." and ".." entries + if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) { + continue; } - va_list args; - va_start(args, fmt); - int result = vasprintf(strp, fmt, args); - va_end(args); - return result; + // Build the full sub-path + char sub_path[PATH_MAX]; + snprintf(sub_path, sizeof(sub_path), "%s/%s", path, entry->d_name); + + // Recursive call + result = remove_recursive(sub_path); + + if (result != 0) { + break; // Stop early on error + } + } + + closedir(d); + + // 4. Once the directory is empty, remove the directory itself + if (result == 0) { + result = rmdir(path); } + return result; +} + +int delete_tile_dir(const std::string &tile_dir) +{ + return remove_recursive(tile_dir.c_str()); +} + +extern "C" { void mocked_exit(int status) { exit_status = status; @@ -223,13 +302,120 @@ extern "C" { free(log_message); } + int mocked_asprintf(char **strp, const char *fmt, ...) + { + if (fail_next_asprintf) { + fail_next_asprintf = false; + *strp = nullptr; + return -1; + } + + va_list args; + va_start(args, fmt); + int result = vasprintf(strp, fmt, args); + va_end(args); + return result; + } + + int mocked_connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen) + { + if (fail_next_connect) { + fail_next_connect = false; + return -1; + } + + return connect(sockfd, addr, addrlen); + } + + int mocked_getaddrinfo(const char *node, const char *service, const struct addrinfo *hints, struct addrinfo **res) + { + if (fail_next_getaddrinfo) { + fail_next_getaddrinfo = false; + return -1; + } + + if (fail_next_getaddrinfo_empty_res) { + fail_next_getaddrinfo_empty_res = false; + *res = nullptr; + return 0; + } + + return getaddrinfo(node, service, hints, res); + } + + void *mocked_malloc(size_t size) + { + if (fail_next_malloc) { + fail_next_malloc = false; + return nullptr; + } + + return malloc(size); + } + + int mocked_mkdir(const char *path, mode_t mode) + { + if (fail_next_mkdir) { + fail_next_mkdir = false; + return -1; + } + + return mkdir(path, mode); + } + + int mocked_open(const char *pathname, int flags, ...) + { + if (fail_next_open) { + fail_next_open = false; + errno = (mock_errno != 0) ? mock_errno : EACCES; + return -1; + } + + va_list args; + va_start(args, flags); + int result = open(pathname, flags, args); + va_end(args); + return result; + } + + int mocked_socket(int domain, int type, int protocol) noexcept + { + if (fail_next_socket) { + fail_next_socket = false; + return -1; + } + + return socket(domain, type, protocol); + } + char *mocked_strndup(const char *s, size_t n) { - if (fail_next) { - fail_next = false; + if (fail_next_strndup) { + fail_next_strndup = false; return nullptr; } return strndup(s, n); } + + char *mocked_strtok(char *s, const char *delim) + { + if (fail_next_strtok) { + fail_next_strtok = false; + return nullptr; + } + + return strtok(s, delim); + } + + ssize_t mocked_write(int fd, const void *buf, size_t count) + { + if (fail_next_write) { + fail_next_write = false; + errno = (mock_errno != 0) ? mock_errno : ENOSPC; + return -1; + } + + return write(fd, buf, count); + } } diff --git a/tests/catch_test_common.hpp b/tests/catch_test_common.hpp index 9745dc4b..c8311c11 100644 --- a/tests/catch_test_common.hpp +++ b/tests/catch_test_common.hpp @@ -15,7 +15,12 @@ * along with this program; If not, see http://www.gnu.org/licenses/. */ +#include +#include #include +#include +#include +#include #include #ifndef CATCH_TEST_COMMON_HPP @@ -40,11 +45,23 @@ std::tuple end_capture(bool print = false); int run_command(const std::string &file, std::vector argv = {}, const std::string &input = ""); +std::string create_tile_dir(const std::string &dir_name = "mod_tile_test", const char *tmp_dir = getenv("TMPDIR")); +int delete_tile_dir(const std::string &tile_dir); + extern "C" { - int mocked_asprintf(char **strp, const char *fmt, ...); void mocked_exit(int status); void mocked_g_logger(int log_level, const char *format, ...); + + int mocked_asprintf(char **strp, const char *fmt, ...); + int mocked_connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen); + int mocked_getaddrinfo(const char *node, const char *service, const struct addrinfo *hints, struct addrinfo **res); + void *mocked_malloc(size_t size); + int mocked_mkdir(const char *path, mode_t mode); + int mocked_open(const char *pathname, int flags, ...); + int mocked_socket(int domain, int type, int protocol) noexcept; char *mocked_strndup(const char *s, size_t n); + char *mocked_strtok(char *s, const char *delim); + ssize_t mocked_write(int fd, const void *buf, size_t count); } #endif diff --git a/tests/gen_tile_test.cpp b/tests/gen_tile_test.cpp index eb4291ac..dae78ef5 100644 --- a/tests/gen_tile_test.cpp +++ b/tests/gen_tile_test.cpp @@ -28,7 +28,6 @@ #include #include #include -#include #include #include #include @@ -70,7 +69,7 @@ struct item *init_render_request(enum protoCmd type) struct item *item = (struct item *)malloc(sizeof(struct item)); bzero(item, sizeof(struct item)); item->req.ver = PROTO_VER; - strcpy(item->req.xmlname, "default"); + strcpy(item->req.xmlname, XMLCONFIG_DEFAULT); item->req.cmd = type; pthread_mutex_lock(&item_counter_lock); item->mx = counter++; @@ -115,25 +114,6 @@ void *fetch_thread(void *arg) return NULL; } -std::string create_tile_dir(const std::string &dir_name = "mod_tile_test", const char *tmp_dir = getenv("TMPDIR")) -{ - if (tmp_dir == NULL) { - tmp_dir = P_tmpdir; - } - - std::string tile_dir(tmp_dir); - tile_dir.append("/").append(dir_name); - - mkdir(tile_dir.c_str(), 0777); - - return tile_dir; -} - -int delete_tile_dir(const std::string &tile_dir) -{ - return rmdir(tile_dir.c_str()); -} - TEST_CASE("renderd/queueing", "request queueing") { SECTION("renderd/queueing/initialisation", "test the initialisation of the request queue") { @@ -585,7 +565,7 @@ TEST_CASE("renderd", "tile generation") int pipefd[2]; pipe(pipefd); struct protocol *req = (struct protocol *)malloc(sizeof(struct protocol)); - std::string expected_mimetype = "image/png", expected_options = "", expected_xmlname = "default"; + std::string expected_mimetype = "image/png", expected_options = "", expected_xmlname = XMLCONFIG_DEFAULT; req->x = 1024; req->y = 1024; @@ -716,7 +696,7 @@ TEST_CASE("storage-backend", "Tile storage backend router") TEST_CASE("file storage-backend", "File Tile storage backend") { std::string tile_dir = create_tile_dir(); - std::string xmlconfig("default"); + std::string xmlconfig(XMLCONFIG_DEFAULT); SECTION("storage/initialise", "should return tile_dir storage_ctx") { struct storage_backend *store = NULL; @@ -1030,7 +1010,7 @@ TEST_CASE("memcached storage-backend", "MemcacheD Tile storage backend") TEST_CASE("null storage-backend", "NULL Tile storage backend") { - std::string xmlconfig("default"); + std::string xmlconfig(XMLCONFIG_DEFAULT); SECTION("storage/initialise", "should return NULL storage_ctx") { struct storage_backend *store = NULL; @@ -1446,67 +1426,6 @@ TEST_CASE("metatile", "Test metatile.cpp") } } -TEST_CASE("protocol_helper", "Test protocol_helper.c") -{ - int block = 0, fd, found, ret; - std::string err_log_lines, out_log_lines; - struct protocol *cmd = (struct protocol *)malloc(sizeof(struct protocol)); - - cmd->x = 1024; - cmd->y = 1024; - cmd->z = 10; - - SECTION("send_cmd/ver invalid version", "should return -1") { - // Version must be 1, 2 or 3 - cmd->ver = 0; - - start_capture(); - ret = send_cmd(cmd, fd); - std::tie(err_log_lines, out_log_lines) = end_capture(); - - REQUIRE(ret == -1); - found = err_log_lines.find("Failed to send render cmd with unknown protocol version 0"); - REQUIRE(found > -1); - - // Version must be 1, 2 or 3 - cmd->ver = 4; - - start_capture(); - ret = send_cmd(cmd, fd); - std::tie(err_log_lines, out_log_lines) = end_capture(); - - REQUIRE(ret == -1); - found = err_log_lines.find("Failed to send render cmd with unknown protocol version 4"); - REQUIRE(found > -1); - } - - SECTION("send_cmd/fd invalid", "should return -1") { - cmd->ver = 1; - - start_capture(); - ret = send_cmd(cmd, fd); - std::tie(err_log_lines, out_log_lines) = end_capture(); - - REQUIRE(ret == -1); - found = err_log_lines.find("Failed to send render cmd on fd"); - REQUIRE(found > -1); - } - - SECTION("recv_cmd/fd invalid debug", "should return -1") { - cmd->ver = 1; - - start_capture(1); - ret = recv_cmd(cmd, fd, block); - std::tie(err_log_lines, out_log_lines) = end_capture(); - - REQUIRE(ret == -1); - found = out_log_lines.find("Failed to read cmd on fd"); - REQUIRE(found > -1); - } - - free(cmd); -} - int main(int argc, char *argv[]) { capture_stderr(); diff --git a/tests/unit_test_cache_expire.cpp b/tests/unit_test_cache_expire.cpp new file mode 100644 index 00000000..bed2a3fb --- /dev/null +++ b/tests/unit_test_cache_expire.cpp @@ -0,0 +1,157 @@ +#include +#include +#include +#include +#include +#include + +#include "catch/catch.hpp" +#include "catch_test_common.hpp" + +extern bool fail_next_connect; +extern bool fail_next_getaddrinfo; +extern bool fail_next_getaddrinfo_empty_res; +extern bool fail_next_malloc; +extern bool fail_next_socket; +extern std::string err_log_lines; + +#define g_logger mocked_g_logger +#define connect mocked_connect +#define getaddrinfo mocked_getaddrinfo +#define malloc mocked_malloc +#define socket mocked_socket + +#include "cache_expire.c" + +#undef g_logger +#undef connect +#undef getaddrinfo +#undef malloc +#undef socket + +int x = 0; +int y = 0; +int z = 0; +std::string host("host"); +std::string uri("/uri/"); + +TEST_CASE("cache_expire_url function failure handling", "[cache_expire_url]") +{ + std::string url("http://" + host + uri + std::to_string(z) + "/" + std::to_string(x) + "/" + std::to_string(y) + ".png"); + + err_log_lines.clear(); + + SECTION("cache_expire_url", "should return") { + int sock = 0; + + cache_expire_url(sock, (char *)url.c_str()); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Failed to send HTCP purge for " + url)); + } + + SECTION("cache_expire_url handles malloc failure", "should return") { + int sock = 0; + + fail_next_malloc = true; + + cache_expire_url(sock, (char *)url.c_str()); + REQUIRE(err_log_lines == ""); + } + + SECTION("cache_expire_url handles negative sock", "should return") { + int sock = -1; + + cache_expire_url(sock, (char *)url.c_str()); + REQUIRE(err_log_lines == ""); + } +} + +TEST_CASE("cache_expire function failure handling", "[cache_expire]") +{ + err_log_lines.clear(); + + SECTION("cache_expire", "should return") { + int sock = 0; + + cache_expire(sock, (char *)host.c_str(), (char *)uri.c_str(), x, y, z); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Failed to send HTCP purge for http://" + host + uri + std::to_string(z) + "/" + std::to_string(x) + "/" + std::to_string(y) + ".png")); + } + + SECTION("cache_expire handles malloc failure", "should return") { + int sock = 0; + + cache_expire(sock, (char *)host.c_str(), (char *)uri.c_str(), x, y, z); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Failed to send HTCP purge for http://" + host + uri + std::to_string(z) + "/" + std::to_string(x) + "/" + std::to_string(y) + ".png")); + } + + SECTION("cache_expire handles negative sock", "should return") { + int sock = -1; + + cache_expire(sock, (char *)host.c_str(), (char *)uri.c_str(), x, y, z); + REQUIRE(err_log_lines == ""); + } +} + +TEST_CASE("init_cache_expire function failure handling", "[init_cache_expire]") +{ + err_log_lines.clear(); + + SECTION("init_cache_expire", "should return") { + std::string htcphost("localhost"); + init_cache_expire((char *)htcphost.c_str()); + REQUIRE(err_log_lines == ""); + } + + SECTION("init_cache_expire with nonexistent host", "should return") { + std::string htcphost("nonexistenthost"); + init_cache_expire((char *)htcphost.c_str()); + REQUIRE_THAT(err_log_lines, + Catch::Matchers::Contains("Failed to lookup HTCP cache host: Address family for hostname not supported") + || + Catch::Matchers::Contains("Failed to lookup HTCP cache host: Name or service not known") + || + Catch::Matchers::Contains("Failed to lookup HTCP cache host: Temporary failure in name resolution") + || + Catch::Matchers::Contains("Failed to lookup HTCP cache host: nodename nor servname provided, or not known") + ); + } + + SECTION("init_cache_expire with failed socket", "should return") { + std::string htcphost("localhost"); + + fail_next_socket = true; + + init_cache_expire((char *)htcphost.c_str()); + REQUIRE(err_log_lines == ""); + } + + SECTION("init_cache_expire with failed getaddrinfo", "should return") { + std::string htcphost("localhost"); + + fail_next_getaddrinfo = true; + + init_cache_expire((char *)htcphost.c_str()); + REQUIRE_THAT(err_log_lines, + Catch::Matchers::Contains("Failed to lookup HTCP cache host: Bad value for ai_flags") + || + Catch::Matchers::Contains("Failed to lookup HTCP cache host: Unknown error") + ); + } + + SECTION("init_cache_expire with empty getaddrinfo response", "should return") { + std::string htcphost("localhost"); + + fail_next_getaddrinfo_empty_res = true; + + init_cache_expire((char *)htcphost.c_str()); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Failed to create HTCP cache socket")); + } + + SECTION("init_cache_expire with failed connect", "should return") { + std::string htcphost("localhost"); + + fail_next_connect = true; + + init_cache_expire((char *)htcphost.c_str()); + REQUIRE(err_log_lines == ""); + } +} diff --git a/tests/unit_test_metatile.cpp b/tests/unit_test_metatile.cpp new file mode 100644 index 00000000..5a2784cf --- /dev/null +++ b/tests/unit_test_metatile.cpp @@ -0,0 +1,174 @@ +#include +#include +#include +#include +#include +#include + +#include "catch/catch.hpp" +#include "catch_test_common.hpp" +#include "metatile.h" + +extern bool fail_next_malloc; +extern bool fail_next_mkdir; +extern bool fail_next_open; +extern bool fail_next_write; +extern std::string err_log_lines; + +#define g_logger mocked_g_logger +#define malloc mocked_malloc +#define mkdir mocked_mkdir +#define open mocked_open +#define write mocked_write + +extern "C" { +#include "cache_expire.c" +#include "store_file.c" +#include "store_file_utils.c" + + struct storage_backend * init_storage_backend(const char * options) + { + struct storage_backend * store = init_storage_file(options); + return store; + } +} + +#include "metatile.cpp" + +#undef g_logger +#undef malloc +#undef mkdir +#undef open +#undef write + +int x = 1024; +int y = 1024; +int z = 10; +std::string host("host"); +std::string metatile_path; +std::string tile_data_string = "DEADBEAF"; +std::string tile_dir; +std::string uri("/uri/"); +std::string xmlconfig = XMLCONFIG_DEFAULT; +struct storage_backend *store; + +metaTile tiles(xmlconfig.c_str(), "", x, y, z); + + +TEST_CASE("metaTile::save", "[metaTile::save] [metaTile::set]") +{ + tile_dir = create_tile_dir("mod_tile.unit_test_metatile"); + store = init_storage_backend(tile_dir.c_str()); + + metatile_path = std::string((char *)store->storage_ctx) + "/" + xmlconfig + "/" + std::to_string(z) + "/0/0/68/0/0.meta"; + + err_log_lines.clear(); + + for (int xx = 0; xx < METATILE; xx++) { + for (int yy = 0; yy < METATILE; yy++) { + std::string tile_set_data(tile_data_string + " " + std::to_string(xx) + " " + std::to_string(yy)); + tiles.set(xx, yy, tile_set_data); + } + } + + SECTION("metaTile::save handles malloc failure", "should return") { + fail_next_malloc = true; + + tiles.save(store); + + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Failed to write metatile. Out of memory")); + } + + SECTION("metaTile::save handles mkdir failure", "should return") { + fail_next_mkdir = true; + + tiles.save(store); + + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Error creating directory " + metatile_path)); + } + + SECTION("metaTile::save handles open failure", "should return") { + fail_next_open = true; + + tiles.save(store); + + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Error creating file " + metatile_path)); + } + + SECTION("metaTile::save handles write failure", "should return") { + fail_next_write = true; + + tiles.save(store); + + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Error writing file " + metatile_path)); + } + + store->metatile_delete(store, xmlconfig.c_str(), 1024, 1024, 10); + store->close_storage(store); + delete_tile_dir(tile_dir); + tiles.clear(); +} + + +TEST_CASE("metaTile::set", "[metaTile::clear] [metaTile::get] [metaTile::set]") +{ + err_log_lines.clear(); + + SECTION("metaTile::set", "then metaTile::get, then metaTile::clear, then metaTile::get") { + for (int xx = 0; xx < METATILE; xx++) { + for (int yy = 0; yy < METATILE; yy++) { + std::string tile_set_data(tile_data_string + " " + std::to_string(xx) + " " + std::to_string(yy)); + tiles.set(xx, yy, tile_set_data); + + std::string tile_get_data = tiles.get(xx, yy); + CHECK(tile_get_data == tile_set_data); + } + } + + tiles.clear(); + + for (int xx = 0; xx < METATILE; xx++) { + for (int yy = 0; yy < METATILE; yy++) { + std::string tile_get_data = tiles.get(xx, yy); + CHECK(tile_get_data == ""); + } + } + } +} + + +TEST_CASE("metaTile::expire_tiles", "[metaTile::expire_tiles]") +{ + err_log_lines.clear(); + + SECTION("metaTile::expire_tile", "should return") { + int sock = 0; + + tiles.expire_tiles(sock, "host", "/uri/"); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Purging metatile via HTCP cache expiry")); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Failed to send HTCP purge for http://" + host + uri + std::to_string(z) + "/" + std::to_string(x) + "/" + std::to_string(y) + ".png")); + } + + SECTION("metaTile::expire_tiles handles negative sock", "should return") { + int sock = -1; + + tiles.expire_tiles(sock, "host", "/uri/"); + REQUIRE(err_log_lines == ""); + } +} + + +TEST_CASE("metaTile::xyz_to_meta_offset", "[metaTile::xyz_to_meta_offset]") +{ + err_log_lines.clear(); + + SECTION("metaTile::xyz_to_meta_offset", "should return") { + int i = 0; + + for (int xx = 0; xx < METATILE; xx++) { + for (int yy = 0; yy < METATILE; yy++) { + CHECK(tiles.xyz_to_meta_offset(xx, yy, z) == i++); + } + } + } +} diff --git a/tests/unit_test_parameterize_style.cpp b/tests/unit_test_parameterize_style.cpp new file mode 100644 index 00000000..f0627fb1 --- /dev/null +++ b/tests/unit_test_parameterize_style.cpp @@ -0,0 +1,100 @@ +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#if MAPNIK_MAJOR_VERSION < 4 +#include +#endif + +#include "catch/catch.hpp" +#include "catch_test_common.hpp" +#include "config.h" +#include "parameterize_style.hpp" + +#ifndef MAPNIK_XML +#define MAPNIK_XML "./utils/example-map/mapnik.xml" +#endif + +#ifndef MAPNIK_PLUGINS_DIR +#define MAPNIK_PLUGINS_DIR "/usr/local/lib64/mapnik/input" +#endif + +extern bool fail_next_strtok; +extern std::string err_log_lines; + +#define g_logger mocked_g_logger +#define strtok mocked_strtok + +#include "parameterize_style.cpp" + +#undef g_logger +#undef strtok + +TEST_CASE("parameterize_map_language function failure exit handling", "[parameterize_map_language]") +{ + const char * parameter = "en,de,_"; + mapnik::datasource_cache::instance().register_datasources(MAPNIK_PLUGINS_DIR); + mapnik::Map map(256, 256); + mapnik::load_map(map, MAPNIK_XML); + mapnik::layer layer = map.get_layer(0); + map.remove_all(); + mapnik::parameters parameters = layer.datasource()->params(); + parameters["table"] = ",name"; + layer.set_datasource(mapnik::datasource_cache::instance().create(parameters)); + map.add_layer(layer); + + err_log_lines.clear(); + + SECTION("parameterize_map_language handles strtok failure", "should return") { + fail_next_strtok = true; + + parameterize_map_language(map, (char *)parameter); + + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Internationalizing map to language parameter: en,de,_")); + } + + SECTION("parameterize_map_language modifies 'table' parameter", "should return") { + layer = map.get_layer(0); + REQUIRE(layer.datasource()->params().get("table") == std::string(",name")); + + parameterize_map_language(map, (char *)parameter); + + layer = map.get_layer(0); + REQUIRE(layer.datasource()->params().get("table") != std::string(",name")); + + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Internationalizing map to language parameter: en,de,_")); + } +} + +TEST_CASE("init_parameterization_function function", "[init_parameterization_function]") +{ + err_log_lines.clear(); + + SECTION("init_parameterization_function with empty function_name", "should return NULL") { + parameterize_function_ptr response = init_parameterization_function(""); + + REQUIRE(response == NULL); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Parameterize_style not specified (or empty string specified)")); + } + + SECTION("init_parameterization_function with non-'language' function_name", "should return NULL") { + parameterize_function_ptr response = init_parameterization_function("doesnotexist"); + + REQUIRE(response == NULL); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("unknown parameterization function for 'doesnotexist'")); + } + + SECTION("init_parameterization_function with 'language' function_name", "should return parameterize_map_language") { + parameterize_function_ptr response = init_parameterization_function("language"); + + REQUIRE(response == parameterize_map_language); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Loading parameterization function for 'language'")); + } +} diff --git a/tests/unit_test_protocol_helper.cpp b/tests/unit_test_protocol_helper.cpp new file mode 100644 index 00000000..77ca2222 --- /dev/null +++ b/tests/unit_test_protocol_helper.cpp @@ -0,0 +1,216 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "catch/catch.hpp" +#include "catch_test_common.hpp" +#include "protocol.h" +#include "protocol_helper.h" + +extern std::string err_log_lines; + +extern "C" { + bool fail_next_recv = false; + int fail_next_recv_reponse_size = -1; + int fail_next_recv_reponse_version = -1; + int fail_next_next_recv_reponse_size = -1; + int fail_next_next_recv_reponse_version = -1; + + ssize_t mocked_recv(int fd, void *buf, size_t n, int flags) + { + if (fail_next_recv) { + fail_next_recv = false; + return -1; + } + + if (fail_next_recv_reponse_size != -1) { + int reponse_size = fail_next_recv_reponse_size; + fail_next_recv_reponse_size = (fail_next_next_recv_reponse_size != -1) ? fail_next_next_recv_reponse_size : -1; + fail_next_next_recv_reponse_size = -1; + + if (fail_next_recv_reponse_version != -1) { + struct protocol *cmd = (struct protocol *)malloc(sizeof(struct protocol)); + cmd->ver = fail_next_recv_reponse_version; + fail_next_recv_reponse_version = (fail_next_next_recv_reponse_version != -1) ? fail_next_next_recv_reponse_version : -1; + fail_next_next_recv_reponse_version = -1; + + memcpy(buf, cmd, reponse_size); + + free(cmd); + } + + return reponse_size; + } + + return recv(fd, buf, n, flags); + } +} + +#define g_logger mocked_g_logger +#define recv mocked_recv + +#include "protocol_helper.c" + +#undef g_logger +#undef recv + +int x = 1024; +int y = 1024; +int z = 10; + +TEST_CASE("protocol_helper", "Test protocol_helper.c") +{ + int block = 1, fd, ret; + int pipefd[2]; + pipe(pipefd); + struct protocol *cmd = (struct protocol *)malloc(sizeof(struct protocol)); + struct protocol rsp; + bzero(&rsp, sizeof(rsp)); + + err_log_lines.clear(); + + cmd->x = x; + cmd->y = y; + cmd->z = z; + + fd = pipefd[1]; + SECTION("send_cmd with invalid version", "should return -1") { + cmd->ver = GENERATE(0, 4); + + ret = send_cmd(cmd, fd); + + REQUIRE(ret == -1); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Failed to send render cmd with unknown protocol version " + std::to_string(cmd->ver))); + } + + SECTION("send_cmd with invalid fd", "should return -1") { + cmd->ver = GENERATE(1, 2, 3); + + ret = send_cmd(cmd, fd); + + REQUIRE(ret == -1); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Failed to send render cmd on fd " + std::to_string(fd))); + } + + fd = pipefd[0]; + SECTION("recv_cmd with invalid version", "should return -1") { + fail_next_recv_reponse_size = sizeof(struct protocol); + fail_next_recv_reponse_version = GENERATE(0, 4); + std::string expected_message = "Failed to receive render cmd with unknown protocol version " + std::to_string(fail_next_recv_reponse_version); + + ret = recv_cmd(&rsp, fd, block); + + REQUIRE(ret == -1); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains(expected_message)); + } + + SECTION("recv_cmd with invalid fd", "should return -1") { + ret = recv_cmd(&rsp, fd, block); + + REQUIRE(ret == -1); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Failed to read cmd on fd " + std::to_string(fd))); + } + + SECTION("recv_cmd with incomplete response", "should return 0") { + fail_next_recv_reponse_size = 1; + + ret = recv_cmd(&rsp, fd, block); + + REQUIRE(ret == 0); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Read incomplete cmd on fd " + std::to_string(fd))); + } + + SECTION("recv_cmd with invalid version and correct size response", "should return -1") { + fail_next_recv_reponse_size = GENERATE(sizeof(struct protocol_v1), sizeof(struct protocol_v2), sizeof(struct protocol)); + fail_next_recv_reponse_version = GENERATE(0, 4); + + int expected_size = -1; + int expected_version = fail_next_recv_reponse_version; + + ret = recv_cmd(&rsp, fd, block); + + REQUIRE(ret == expected_size); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Failed to receive render cmd with unknown protocol version " + std::to_string(expected_version))); + } + + SECTION("recv_cmd with correct size response (v1)", "should return") { + fail_next_recv_reponse_size = sizeof(struct protocol_v1); + fail_next_recv_reponse_version = 1; + + int expected_size = fail_next_recv_reponse_size; + int expected_version = fail_next_recv_reponse_version; + + ret = recv_cmd(&rsp, fd, block); + + REQUIRE(ret == expected_size); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Got incoming request with protocol version " + std::to_string(expected_version))); + } + + SECTION("recv_cmd with correct size response (v2)", "should return") { + fail_next_recv_reponse_size = sizeof(struct protocol_v1); + fail_next_recv_reponse_version = 2; + fail_next_next_recv_reponse_size = sizeof(struct protocol_v2) - fail_next_recv_reponse_size; + fail_next_next_recv_reponse_version = fail_next_recv_reponse_version; + + int expected_size = fail_next_recv_reponse_size + fail_next_next_recv_reponse_size; + int expected_version = fail_next_recv_reponse_version; + + ret = recv_cmd(&rsp, fd, block); + + REQUIRE(ret == expected_size); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Got incoming request with protocol version " + std::to_string(expected_version))); + } + + SECTION("recv_cmd with correct size response (v3)", "should return") { + fail_next_recv_reponse_size = sizeof(struct protocol_v1); + fail_next_recv_reponse_version = 3; + fail_next_next_recv_reponse_size = sizeof(struct protocol) - fail_next_recv_reponse_size; + fail_next_next_recv_reponse_version = fail_next_recv_reponse_version; + + int expected_size = fail_next_recv_reponse_size + fail_next_next_recv_reponse_size; + int expected_version = fail_next_recv_reponse_version; + + ret = recv_cmd(&rsp, fd, block); + + REQUIRE(ret == expected_size); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Got incoming request with protocol version " + std::to_string(expected_version))); + } + + SECTION("recv_cmd with incorrect size response (v2-v3)", "should return") { + fail_next_recv_reponse_size = sizeof(struct protocol_v1); + fail_next_recv_reponse_version = GENERATE(2, 3); + fail_next_next_recv_reponse_size = sizeof(struct protocol_v1); + fail_next_next_recv_reponse_version = fail_next_recv_reponse_version; + + int expected_size = fail_next_recv_reponse_size + fail_next_next_recv_reponse_size; + int expected_version = fail_next_recv_reponse_version; + + ret = recv_cmd(&rsp, fd, block); + + REQUIRE(ret == 0); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Socket read wrong number of bytes: " + std::to_string(expected_size))); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Got incoming request with protocol version " + std::to_string(expected_version))); + } + + SECTION("recv_cmd with incomplete second response (v2-v3)", "should return -1") { + fail_next_recv_reponse_size = sizeof(struct protocol_v1); + fail_next_recv_reponse_version = GENERATE(2, 3); + fail_next_next_recv_reponse_size = 0; + fail_next_next_recv_reponse_version = fail_next_recv_reponse_version; + + int expected_size = -1; + int expected_version = fail_next_recv_reponse_version; + + ret = recv_cmd(&rsp, fd, block); + + REQUIRE(ret == expected_size); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Got incoming request with protocol version " + std::to_string(expected_version))); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Socket prematurely closed: " + std::to_string(fd))); + } + + free(cmd); +} diff --git a/tests/unit_test_renderd_config.cpp b/tests/unit_test_renderd_config.cpp index 1a2ac742..ba0a0fe3 100644 --- a/tests/unit_test_renderd_config.cpp +++ b/tests/unit_test_renderd_config.cpp @@ -15,7 +15,8 @@ #endif extern int exit_status; -extern bool fail_next; +extern bool fail_next_asprintf; +extern bool fail_next_strndup; extern jmp_buf exit_jump; extern std::string err_log_lines; @@ -33,16 +34,16 @@ extern "C" { #undef strndup } -TEST_CASE("renderd_config function failure exit handling", "[renderd_config] [copy_string] [name_with_section]") +TEST_CASE("copy_string", "[copy_string]") { err_log_lines.clear(); exit_status = 0; - SECTION("copy_string handles strndup failure by exiting", "should return 7") { + SECTION("copy_string handles strndup failure by exiting", "should exit 7") { const char *src = "test"; const char *dest; - fail_next = true; + fail_next_strndup = true; if (setjmp(exit_jump) == 0) { copy_string(src, &dest, strlen(src)); @@ -51,13 +52,11 @@ TEST_CASE("renderd_config function failure exit handling", "[renderd_config] [co SUCCEED("Captured expected exit() call due to strndup failure"); } - fail_next = false; - REQUIRE(exit_status == 7); REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("copy_string: strndup error")); } - SECTION("copy_string with valid src and valid dest", "should succeed") { + SECTION("copy_string with valid src and valid dest", "should return") { const char *src = "test"; const char *dest = nullptr; @@ -65,9 +64,48 @@ TEST_CASE("renderd_config function failure exit handling", "[renderd_config] [co copy_string(src, &dest, strlen(src)); REQUIRE(strcmp(src, dest) == 0); } +} +TEST_CASE("name_with_section", "[name_with_section]") +{ + err_log_lines.clear(); + exit_status = 0; - SECTION("name_with_section handles asprintf failure by exiting") { + SECTION("name_with_section with valid name and invalid section", "should exit 7") { + const char *name = "socketname"; + const char *section = nullptr; + char *value = nullptr; + + if (setjmp(exit_jump) == 0) { + name_with_section(section, name); + FAIL("name_with_section should have called exit() but didn't"); + } else { + SUCCEED("Captured expected exit() call due to asprintf failure"); + } + + REQUIRE(exit_status == 7); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("name_with_section: invalid section (null)")); + } + + + SECTION("name_with_section with invalid name and valid section", "should exit 7") { + const char *name = nullptr; + const char *section = "renderd"; + char *value = nullptr; + + if (setjmp(exit_jump) == 0) { + name_with_section(section, name); + FAIL("name_with_section should have called exit() but didn't"); + } else { + SUCCEED("Captured expected exit() call due to asprintf failure"); + } + + REQUIRE(exit_status == 7); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("name_with_section: invalid name (null)")); + } + + + SECTION("name_with_section with valid name and valid section", "should return") { const char *name = "socketname"; const char *section = "renderd"; char *value = nullptr; @@ -77,11 +115,11 @@ TEST_CASE("renderd_config function failure exit handling", "[renderd_config] [co REQUIRE((std::string(section) + ":" + name) == value); } - SECTION("name_with_section handles asprintf failure by exiting", "should return 7") { + SECTION("name_with_section handles asprintf failure by exiting", "should exit 7") { const char *section = "renderd"; const char *name = "socketname"; - fail_next = true; + fail_next_asprintf = true; if (setjmp(exit_jump) == 0) { name_with_section(section, name); @@ -90,19 +128,140 @@ TEST_CASE("renderd_config function failure exit handling", "[renderd_config] [co SUCCEED("Captured expected exit() call due to asprintf failure"); } - fail_next = false; - REQUIRE(exit_status == 7); REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("name_with_section: asprintf error")); } } -TEST_CASE("renderd.conf file processing exit handling", "[renderd_config] [process_config_file] [process_renderd_sections] [process_mapnik_section] [process_map_sections]") +TEST_CASE("min_max_double_opt & min_max_int_opt", "[min_max_double_opt] [min_max_int_opt]") +{ + const char *opt_type_name = "value"; + double dmax = 1.15; + double dmin = 1.10; + + err_log_lines.clear(); + exit_status = 0; + + SECTION("min_max_double_opt success", "should not exit") { + const char *opt_arg = "1.125"; + + if (setjmp(exit_jump) == 0) { + min_max_double_opt(opt_arg, opt_type_name, dmin, dmax); + SUCCEED("min_max_double_opt did not call exit(), as expected"); + } else { + FAIL("min_max_double_opt captured unexpected exit() call"); + } + + REQUIRE(exit_status == 0); + REQUIRE(err_log_lines == ""); + } + + SECTION("min_max_double_opt exceeds max", "should exit 1") { + const char *opt_arg = "2"; + + if (setjmp(exit_jump) == 0) { + min_max_double_opt(opt_arg, opt_type_name, dmin, dmax); + FAIL("min_max_double_opt should have called exit() but didn't"); + } else { + SUCCEED("Captured expected exit() call due to asprintf failure"); + } + + REQUIRE(exit_status == 1); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Invalid " + std::string(opt_type_name) + ", must be <= " + std::to_string(dmax) + " (" + std::string(opt_arg) + " was provided)")); + } + + SECTION("min_max_double_opt less than min", "should exit 1") { + const char *opt_arg = "1"; + + if (setjmp(exit_jump) == 0) { + min_max_double_opt(opt_arg, opt_type_name, dmin, dmax); + FAIL("min_max_double_opt should have called exit() but didn't"); + } else { + SUCCEED("Captured expected exit() call due to asprintf failure"); + } + + REQUIRE(exit_status == 1); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Invalid " + std::string(opt_type_name) + ", must be >= " + std::to_string(dmin) + " (" + std::string(opt_arg) + " was provided)")); + } + + SECTION("min_max_double_opt exceeds max", "should exit 1") { + const char *opt_arg = "fail"; + + if (setjmp(exit_jump) == 0) { + min_max_double_opt(opt_arg, opt_type_name, dmin, dmax); + FAIL("min_max_double_opt should have called exit() but didn't"); + } else { + SUCCEED("Captured expected exit() call due to asprintf failure"); + } + + REQUIRE(exit_status == 1); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Invalid " + std::string(opt_type_name) + ", must be a double (" + std::string(opt_arg) + " was provided)")); + } + + + SECTION("min_max_int_opt success", "should not exit") { + const char *opt_arg = "1"; + + if (setjmp(exit_jump) == 0) { + min_max_int_opt(opt_arg, opt_type_name, dmin, dmax); + SUCCEED("min_max_int_opt did not call exit(), as expected"); + } else { + FAIL("min_max_int_opt captured unexpected exit() call"); + } + + REQUIRE(exit_status == 0); + REQUIRE(err_log_lines == ""); + } + + SECTION("min_max_int_opt exceeds max", "should exit 1") { + const char *opt_arg = "2"; + + if (setjmp(exit_jump) == 0) { + min_max_int_opt(opt_arg, opt_type_name, dmin, dmax); + FAIL("min_max_int_opt should have called exit() but didn't"); + } else { + SUCCEED("Captured expected exit() call due to asprintf failure"); + } + + REQUIRE(exit_status == 1); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Invalid " + std::string(opt_type_name) + ", must be <= " + std::to_string((int)dmax) + " (" + std::string(opt_arg) + " was provided)")); + } + + SECTION("min_max_int_opt less than min", "should exit 1") { + const char *opt_arg = "0"; + + if (setjmp(exit_jump) == 0) { + min_max_int_opt(opt_arg, opt_type_name, dmin, dmax); + FAIL("min_max_int_opt should have called exit() but didn't"); + } else { + SUCCEED("Captured expected exit() call due to asprintf failure"); + } + + REQUIRE(exit_status == 1); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Invalid " + std::string(opt_type_name) + ", must be >= " + std::to_string((int)dmin) + " (" + std::string(opt_arg) + " was provided)")); + } + + SECTION("min_max_int_opt exceeds max", "should exit 1") { + const char *opt_arg = "fail"; + + if (setjmp(exit_jump) == 0) { + min_max_int_opt(opt_arg, opt_type_name, dmin, dmax); + FAIL("min_max_int_opt should have called exit() but didn't"); + } else { + SUCCEED("Captured expected exit() call due to asprintf failure"); + } + + REQUIRE(exit_status == 1); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Invalid " + std::string(opt_type_name) + ", must be an integer (" + std::string(opt_arg) + " was provided)")); + } +} + +TEST_CASE("renderd.conf file processing", "[process_config_file] [process_renderd_sections] [process_mapnik_section] [process_map_sections]") { err_log_lines.clear(); exit_status = 0; - SECTION("valid renderd.conf file with invalid active renderd section", "should return 1") { + SECTION("valid renderd.conf file with invalid active renderd section", "should exit 1") { if (setjmp(exit_jump) == 0) { process_config_file(RENDERD_CONF, MAX_SLAVES, 0); FAIL("process_config_file should have called exit(), but did not"); @@ -114,7 +273,7 @@ TEST_CASE("renderd.conf file processing exit handling", "[renderd_config] [proce REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Active renderd section (" + std::to_string(MAX_SLAVES) + ") must be between 0 and " + std::to_string(MAX_SLAVES - 1) + ".")); } - SECTION("valid renderd.conf file with nonexistent active renderd section", "should return 1") { + SECTION("valid renderd.conf file with nonexistent active renderd section", "should exit 1") { if (setjmp(exit_jump) == 0) { process_config_file(RENDERD_CONF, (MAX_SLAVES - 1), 0); FAIL("process_config_file should have called exit(), but did not"); @@ -126,7 +285,7 @@ TEST_CASE("renderd.conf file processing exit handling", "[renderd_config] [proce REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Active renderd section (" + std::to_string(MAX_SLAVES - 1) + ") does not exist.")); } - SECTION("nonexistent renderd.conf file with valid active renderd section", "should return 1") { + SECTION("nonexistent renderd.conf file with valid active renderd section", "should exit 1") { if (setjmp(exit_jump) == 0) { process_config_file("doesnotexist", 0, 0); FAIL("process_config_file should have called exit(), but did not"); @@ -138,7 +297,7 @@ TEST_CASE("renderd.conf file processing exit handling", "[renderd_config] [proce REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Failed to load config file (process_config_file): 'doesnotexist'")); } - SECTION("nonexistent renderd.conf file with valid active renderd section (process_renderd_sections)", "should return 1") { + SECTION("nonexistent renderd.conf file with valid active renderd section (process_renderd_sections)", "should exit 1") { if (setjmp(exit_jump) == 0) { process_renderd_sections(NULL, "doesnotexist", config_slaves); FAIL("process_renderd_sections should have called exit(), but did not"); @@ -150,7 +309,7 @@ TEST_CASE("renderd.conf file processing exit handling", "[renderd_config] [proce REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Failed to load config file (process_renderd_sections): 'doesnotexist'")); } - SECTION("nonexistent renderd.conf file with valid active renderd section (process_mapnik_section)", "should return 1") { + SECTION("nonexistent renderd.conf file with valid active renderd section (process_mapnik_section)", "should exit 1") { if (setjmp(exit_jump) == 0) { process_mapnik_section(NULL, "doesnotexist", config_slaves); FAIL("process_mapnik_section should have called exit(), but did not"); @@ -162,7 +321,7 @@ TEST_CASE("renderd.conf file processing exit handling", "[renderd_config] [proce REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Failed to load config file (process_mapnik_section): 'doesnotexist'")); } - SECTION("nonexistent renderd.conf file with valid active renderd section (process_map_sections)", "should return 1") { + SECTION("nonexistent renderd.conf file with valid active renderd section (process_map_sections)", "should exit 1") { if (setjmp(exit_jump) == 0) { process_map_sections(NULL, "doesnotexist", maps, "", 0); FAIL("process_map_sections should have called exit(), but did not"); @@ -174,7 +333,7 @@ TEST_CASE("renderd.conf file processing exit handling", "[renderd_config] [proce REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Failed to load config file (process_map_sections): 'doesnotexist'")); } - SECTION("valid renderd.conf file with valid active renderd section (process_config_file)", "should return 0") { + SECTION("valid renderd.conf file with valid active renderd section (process_config_file)", "should not exit") { if (setjmp(exit_jump) == 0) { process_config_file(RENDERD_CONF, 0, 0); SUCCEED("process_config_file did not call exit(), as expected"); @@ -185,7 +344,7 @@ TEST_CASE("renderd.conf file processing exit handling", "[renderd_config] [proce REQUIRE(exit_status == 0); } - SECTION("valid renderd.conf file with valid active renderd section (process_renderd_sections)", "should return 0") { + SECTION("valid renderd.conf file with valid active renderd section (process_renderd_sections)", "should not exit") { if (setjmp(exit_jump) == 0) { process_renderd_sections(NULL, RENDERD_CONF, config_slaves); SUCCEED("process_renderd_sections did not call exit(), as expected"); @@ -196,7 +355,7 @@ TEST_CASE("renderd.conf file processing exit handling", "[renderd_config] [proce REQUIRE(exit_status == 0); } - SECTION("valid renderd.conf file with valid active renderd section (process_mapnik_section)", "should return 0") { + SECTION("valid renderd.conf file with valid active renderd section (process_mapnik_section)", "should not exit") { if (setjmp(exit_jump) == 0) { process_mapnik_section(NULL, RENDERD_CONF, config_slaves); SUCCEED("process_mapnik_section did not call exit(), as expected"); @@ -207,7 +366,7 @@ TEST_CASE("renderd.conf file processing exit handling", "[renderd_config] [proce REQUIRE(exit_status == 0); } - SECTION("valid renderd.conf file with valid active renderd section (process_map_sections)", "should return 0") { + SECTION("valid renderd.conf file with valid active renderd section (process_map_sections)", "should not exit") { if (setjmp(exit_jump) == 0) { process_map_sections(NULL, RENDERD_CONF, maps, "", 0); SUCCEED("process_map_sections did not call exit(), as expected"); @@ -228,7 +387,7 @@ TEST_CASE("renderd.conf file processing exit handling", "[renderd_config] [proce close(fd); std::ofstream renderd_conf_file(renderd_conf_path); - SECTION("renderd.conf with too many map sections", "should return 7") { + SECTION("renderd.conf with too many map sections", "should exit 7") { renderd_conf_file << "[mapnik]\n[renderd]\n"; for (int i = 0; i <= XMLCONFIGS_MAX; i++) { @@ -249,7 +408,7 @@ TEST_CASE("renderd.conf file processing exit handling", "[renderd_config] [proce REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Can't handle more than " + std::to_string(XMLCONFIGS_MAX) + " map config sections")); } - SECTION("renderd.conf without map sections", "should return 1") { + SECTION("renderd.conf without map sections", "should exit 1") { renderd_conf_file << "[mapnik]\n[renderd]\n"; renderd_conf_file.close(); @@ -265,7 +424,7 @@ TEST_CASE("renderd.conf file processing exit handling", "[renderd_config] [proce REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("No map config sections were found in file: " + renderd_conf_path)); } - SECTION("renderd.conf without mapnik section", "should return 1") { + SECTION("renderd.conf without mapnik section", "should exit 1") { renderd_conf_file << "[map]\n[renderd]\n"; renderd_conf_file.close(); @@ -281,7 +440,7 @@ TEST_CASE("renderd.conf file processing exit handling", "[renderd_config] [proce REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("No mapnik config section was found in file: " + renderd_conf_path)); } - SECTION("renderd.conf with invalid renderd sections", "should return 7") { + SECTION("renderd.conf with invalid renderd sections", "should exit 7") { std::string renderd_conf_renderd_section_name = "renderdinvalid"; renderd_conf_file << "[mapnik]\n[map]\n[" + renderd_conf_renderd_section_name + "]\n"; @@ -299,7 +458,7 @@ TEST_CASE("renderd.conf file processing exit handling", "[renderd_config] [proce REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Invalid renderd section name: " + renderd_conf_renderd_section_name)); } - SECTION("renderd.conf with too many renderd sections", "should return 7") { + SECTION("renderd.conf with too many renderd sections", "should exit 7") { renderd_conf_file << "[mapnik]\n[map]\n"; for (int i = 0; i <= MAX_SLAVES; i++) { @@ -320,7 +479,7 @@ TEST_CASE("renderd.conf file processing exit handling", "[renderd_config] [proce REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Can't handle more than " + std::to_string(MAX_SLAVES) + " renderd config sections")); } - SECTION("renderd.conf without renderd sections", "should return 1") { + SECTION("renderd.conf without renderd sections", "should exit 1") { renderd_conf_file << "[map]\n[mapnik]\n"; renderd_conf_file.close(); @@ -336,7 +495,7 @@ TEST_CASE("renderd.conf file processing exit handling", "[renderd_config] [proce REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("No renderd config sections were found in file: " + renderd_conf_path)); } - SECTION("renderd.conf map section scale too small", "should return 7") { + SECTION("renderd.conf map section scale too small", "should exit 7") { renderd_conf_file << "[mapnik]\n[renderd]\n"; renderd_conf_file << "[map]\nscale=0.0\n"; renderd_conf_file.close(); @@ -353,7 +512,7 @@ TEST_CASE("renderd.conf file processing exit handling", "[renderd_config] [proce REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Specified scale factor (0.000000) is too small, must be greater than or equal to 0.100000.")); } - SECTION("renderd.conf map section scale too large", "should return 7") { + SECTION("renderd.conf map section scale too large", "should exit 7") { renderd_conf_file << "[mapnik]\n[renderd]\n"; renderd_conf_file << "[map]\nscale=8.1\n"; renderd_conf_file.close(); @@ -370,7 +529,7 @@ TEST_CASE("renderd.conf file processing exit handling", "[renderd_config] [proce REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Specified scale factor (8.100000) is too large, must be less than or equal to 8.000000.")); } - SECTION("renderd.conf map section maxzoom too small", "should return 7") { + SECTION("renderd.conf map section maxzoom too small", "should exit 7") { renderd_conf_file << "[mapnik]\n[renderd]\n"; renderd_conf_file << "[map]\nmaxzoom=-1\n"; renderd_conf_file.close(); @@ -387,7 +546,7 @@ TEST_CASE("renderd.conf file processing exit handling", "[renderd_config] [proce REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Specified max zoom (-1) is too small, must be greater than or equal to 0.")); } - SECTION("renderd.conf map section maxzoom too large", "should return 7") { + SECTION("renderd.conf map section maxzoom too large", "should exit 7") { renderd_conf_file << "[mapnik]\n[renderd]\n"; renderd_conf_file << "[map]\nmaxzoom=" << MAX_ZOOM + 1 << "\n"; renderd_conf_file.close(); @@ -404,7 +563,7 @@ TEST_CASE("renderd.conf file processing exit handling", "[renderd_config] [proce REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Specified max zoom (" + std::to_string(MAX_ZOOM + 1) + ") is too large, must be less than or equal to " + std::to_string(MAX_ZOOM) + ".")); } - SECTION("renderd.conf map section minzoom too small", "should return 7") { + SECTION("renderd.conf map section minzoom too small", "should exit 7") { renderd_conf_file << "[mapnik]\n[renderd]\n"; renderd_conf_file << "[map]\nminzoom=-1\n"; renderd_conf_file.close(); @@ -421,7 +580,7 @@ TEST_CASE("renderd.conf file processing exit handling", "[renderd_config] [proce REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Specified min zoom (-1) is too small, must be greater than or equal to 0.")); } - SECTION("renderd.conf map section minzoom too large", "should return 7") { + SECTION("renderd.conf map section minzoom too large", "should exit 7") { renderd_conf_file << "[mapnik]\n[renderd]\n"; renderd_conf_file << "[map]\nminzoom=" << MAX_ZOOM + 1 << "\n"; renderd_conf_file.close(); @@ -438,7 +597,7 @@ TEST_CASE("renderd.conf file processing exit handling", "[renderd_config] [proce REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Specified min zoom (" + std::to_string(MAX_ZOOM + 1) + ") is larger than max zoom (" + std::to_string(MAX_ZOOM) + ").")); } - SECTION("renderd.conf map section type has too few parts", "should return 7") { + SECTION("renderd.conf map section type has too few parts", "should exit 7") { std::string renderd_conf_map_type = "a"; renderd_conf_file << "[mapnik]\n[renderd]\n"; @@ -457,7 +616,7 @@ TEST_CASE("renderd.conf file processing exit handling", "[renderd_config] [proce REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Specified type (" + renderd_conf_map_type + ") has too few parts, there must be at least 2, e.g., 'png image/png'.")); } - SECTION("renderd.conf map section type has too many parts", "should return 7") { + SECTION("renderd.conf map section type has too many parts", "should exit 7") { std::string renderd_conf_map_type = "a b c d"; renderd_conf_file << "[mapnik]\n[renderd]\n"; @@ -476,7 +635,7 @@ TEST_CASE("renderd.conf file processing exit handling", "[renderd_config] [proce REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Specified type (" + renderd_conf_map_type + ") has too many parts, there must be no more than 3, e.g., 'png image/png png256'.")); } - SECTION("renderd.conf map section type has two parts", "should return 0") { + SECTION("renderd.conf map section type has two parts", "should not exit") { renderd_conf_file << "[mapnik]\n[map]\ntype=png image/png\n"; renderd_conf_file << "[renderd]\n"; renderd_conf_file.close(); @@ -489,13 +648,13 @@ TEST_CASE("renderd.conf file processing exit handling", "[renderd_config] [proce } REQUIRE(exit_status == 0); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Read map:type: 'png image/png'")); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Read map:type:file_extension: 'png'")); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Read map:type:mime_type: 'image/png'")); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Read map:type: 'png image/png'")); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Read map:type:file_extension: 'png'")); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Read map:type:mime_type: 'image/png'")); REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Read map:type:output_format: 'png256'")); } - SECTION("renderd.conf renderd section socketname is too long", "should return 7") { + SECTION("renderd.conf renderd section socketname is too long", "should exit 7") { int renderd_socketname_maxlen = sizeof(((struct sockaddr_un *)0)->sun_path); std::string renderd_socketname = "/" + std::string(renderd_socketname_maxlen, 'A'); @@ -515,7 +674,7 @@ TEST_CASE("renderd.conf file processing exit handling", "[renderd_config] [proce REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Specified socketname (" + renderd_socketname + ") exceeds maximum allowed length of " + std::to_string(renderd_socketname_maxlen) + ".")); } - SECTION("renderd.conf duplicate renderd section names", "should return 7") { + SECTION("renderd.conf duplicate renderd section names", "should exit 7") { renderd_conf_file << "[mapnik]\n[map]\n"; renderd_conf_file << "[renderd0]\n[renderd]\n"; renderd_conf_file.close(); @@ -532,7 +691,7 @@ TEST_CASE("renderd.conf file processing exit handling", "[renderd_config] [proce REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Duplicate renderd config section names for section 0: renderd0 & renderd")); } - SECTION("renderd.conf renderd section num_threads is -1", "should return 0") { + SECTION("renderd.conf renderd section num_threads is -1", "should not exit") { renderd_conf_file << "[mapnik]\n[map]\n"; renderd_conf_file << "[renderd0]\nnum_threads=-1\n"; renderd_conf_file.close(); @@ -545,11 +704,11 @@ TEST_CASE("renderd.conf file processing exit handling", "[renderd_config] [proce } REQUIRE(exit_status == 0); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("renderd(0): num_threads = '" + std::to_string(sysconf(_SC_NPROCESSORS_ONLN)) + "'")); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("renderd: num_threads = '" + std::to_string(sysconf(_SC_NPROCESSORS_ONLN)) + "'")); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("renderd(0): num_threads = '" + std::to_string(sysconf(_SC_NPROCESSORS_ONLN)) + "'")); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("renderd: num_threads = '" + std::to_string(sysconf(_SC_NPROCESSORS_ONLN)) + "'")); } - SECTION("renderd.conf slave renderd sections' num_threads sum equals num_slave_threads", "should return 0") { + SECTION("renderd.conf slave renderd sections' num_threads sum equals num_slave_threads", "should not exit") { renderd_conf_file << "[mapnik]\n[map]\n"; renderd_conf_file << "[renderd0]\nnum_threads=-1\n[renderd1]\nnum_threads=2\n[renderd2]\nnum_threads=2\n"; renderd_conf_file.close(); @@ -562,12 +721,12 @@ TEST_CASE("renderd.conf file processing exit handling", "[renderd_config] [proce } REQUIRE(exit_status == 0); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("renderd(1): num_threads = '2'")); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("renderd(2): num_threads = '2'")); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("renderd: num_slave_threads = '4'")); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("renderd(1): num_threads = '2'")); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("renderd(2): num_threads = '2'")); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("renderd: num_slave_threads = '4'")); } - SECTION("renderd.conf renderd section not using unix socketname", "should return 0") { + SECTION("renderd.conf renderd section not using unix socketname", "should not exit") { renderd_conf_file << "[mapnik]\n[map]\n"; renderd_conf_file << "[renderd0]\niphostname=hostname\nipport=9999\n"; renderd_conf_file.close(); @@ -580,126 +739,122 @@ TEST_CASE("renderd.conf file processing exit handling", "[renderd_config] [proce } REQUIRE(exit_status == 0); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("renderd(0): ip socket = 'hostname:9999'")); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("renderd: ip socket = 'hostname:9999'")); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("renderd(0): ip socket = 'hostname:9999'")); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("renderd: ip socket = 'hostname:9999'")); } std::remove(renderd_conf_path.c_str()); } -TEST_CASE("renderd_config minimum and maximum function exit handling", "[renderd_config] [min_max_double_opt] [min_max_int_opt]") +TEST_CASE("process_config_bool, process_config_double, process_config_int & process_config_string", "[process_config_bool] [process_config_double] [process_config_int] [process_config_string]") { - const char *opt_type_name = "value"; - double dmax = 1.15; - double dmin = 1.10; + dictionary *ini = iniparser_load(RENDERD_CONF); + std::string section = "section"; + std::string name = "name"; err_log_lines.clear(); - exit_status = 0; - SECTION("min_max_double_opt success", "should return 0") { - const char *opt_arg = "1.125"; + SECTION("process_config_bool", "should not exit") { + bool notfound = true; if (setjmp(exit_jump) == 0) { - min_max_double_opt(opt_arg, opt_type_name, dmin, dmax); - SUCCEED("min_max_double_opt did not call exit(), as expected"); + process_config_bool(ini, section.c_str(), name.c_str(), &maps[0].num_threads, notfound); + SUCCEED("process_config_bool did not call exit(), as expected"); } else { - FAIL("min_max_double_opt captured unexpected exit() call"); + FAIL("process_config_bool captured unexpected exit() call"); } REQUIRE(exit_status == 0); - REQUIRE(err_log_lines == ""); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Read " + section + ":" + name + ": '" + (notfound ? "true" : "false") + "'")); } - SECTION("min_max_double_opt exceeds max", "should return 1") { - const char *opt_arg = "2"; + SECTION("process_config_double", "should not exit") { + double notfound = 123.456; if (setjmp(exit_jump) == 0) { - min_max_double_opt(opt_arg, opt_type_name, dmin, dmax); - FAIL("min_max_double_opt should have called exit() but didn't"); + process_config_double(ini, section.c_str(), name.c_str(), &maps[0].scale_factor, notfound); + SUCCEED("process_config_double did not call exit(), as expected"); } else { - SUCCEED("Captured expected exit() call due to asprintf failure"); + FAIL("process_config_double captured unexpected exit() call"); } - REQUIRE(exit_status == 1); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Invalid " + std::string(opt_type_name) + ", must be <= " + std::to_string(dmax) + " (" + std::string(opt_arg) + " was provided)")); - } - SECTION("min_max_double_opt less than min", "should return 1") { - const char *opt_arg = "1"; - - if (setjmp(exit_jump) == 0) { - min_max_double_opt(opt_arg, opt_type_name, dmin, dmax); - FAIL("min_max_double_opt should have called exit() but didn't"); - } else { - SUCCEED("Captured expected exit() call due to asprintf failure"); - } - REQUIRE(exit_status == 1); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Invalid " + std::string(opt_type_name) + ", must be >= " + std::to_string(dmin) + " (" + std::string(opt_arg) + " was provided)")); + REQUIRE(exit_status == 0); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Read " + section + ":" + name + ": '" + std::to_string(notfound) + "'")); } - SECTION("min_max_double_opt exceeds max", "should return 1") { - const char *opt_arg = "fail"; + SECTION("process_config_int", "should not exit") { + int notfound = 123456; if (setjmp(exit_jump) == 0) { - min_max_double_opt(opt_arg, opt_type_name, dmin, dmax); - FAIL("min_max_double_opt should have called exit() but didn't"); + process_config_int(ini, section.c_str(), name.c_str(), &maps[0].num_threads, notfound); + SUCCEED("process_config_int did not call exit(), as expected"); } else { - SUCCEED("Captured expected exit() call due to asprintf failure"); + FAIL("process_config_int captured unexpected exit() call"); } - REQUIRE(exit_status == 1); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Invalid " + std::string(opt_type_name) + ", must be a double (" + std::string(opt_arg) + " was provided)")); - } + REQUIRE(exit_status == 0); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Read " + section + ":" + name + ": '" + std::to_string(notfound) + "'")); + } - SECTION("min_max_int_opt success", "should return 0") { - const char *opt_arg = "1"; + SECTION("process_config_string", "should not exit") { + std::string notfound = "notfound"; if (setjmp(exit_jump) == 0) { - min_max_int_opt(opt_arg, opt_type_name, dmin, dmax); - SUCCEED("min_max_int_opt did not call exit(), as expected"); + process_config_string(ini, section.c_str(), name.c_str(), &maps[0].xmlname, notfound.c_str(), notfound.length()); + SUCCEED("process_config_string did not call exit(), as expected"); } else { - FAIL("min_max_int_opt captured unexpected exit() call"); + FAIL("process_config_string captured unexpected exit() call"); } REQUIRE(exit_status == 0); - REQUIRE(err_log_lines == ""); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Read " + section + ":" + name + ": '" + notfound + "'")); } - SECTION("min_max_int_opt exceeds max", "should return 1") { - const char *opt_arg = "2"; + iniparser_freedict(ini); +} - if (setjmp(exit_jump) == 0) { - min_max_int_opt(opt_arg, opt_type_name, dmin, dmax); - FAIL("min_max_int_opt should have called exit() but didn't"); - } else { - SUCCEED("Captured expected exit() call due to asprintf failure"); - } - REQUIRE(exit_status == 1); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Invalid " + std::string(opt_type_name) + ", must be <= " + std::to_string((int)dmax) + " (" + std::string(opt_arg) + " was provided)")); - } +TEST_CASE("free_map_section, free_map_sections, free_renderd_section & free_renderd_sections", "[free_map_section] [free_map_sections] [free_renderd_section] [free_renderd_sections]") +{ + int active_renderd_section_num = 0; + int map_section_num = 0; - SECTION("min_max_int_opt less than min", "should return 1") { - const char *opt_arg = "0"; + process_config_file(RENDERD_CONF, active_renderd_section_num, 0); + REQUIRE(config == &config_slaves[active_renderd_section_num]); + REQUIRE(config->name == std::string("renderd")); + REQUIRE(config_slaves[active_renderd_section_num].name == std::string("renderd")); + REQUIRE(maps[map_section_num].xmlname == std::string("example-map")); - if (setjmp(exit_jump) == 0) { - min_max_int_opt(opt_arg, opt_type_name, dmin, dmax); - FAIL("min_max_int_opt should have called exit() but didn't"); - } else { - SUCCEED("Captured expected exit() call due to asprintf failure"); - } - REQUIRE(exit_status == 1); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Invalid " + std::string(opt_type_name) + ", must be >= " + std::to_string((int)dmin) + " (" + std::string(opt_arg) + " was provided)")); + err_log_lines.clear(); + + SECTION("free_map_section", "should not exit") { + free_map_section(&maps[map_section_num]); + REQUIRE(maps[map_section_num].xmlname == nullptr); + free_map_section(&maps[map_section_num]); + REQUIRE(maps[map_section_num].xmlname == nullptr); } - SECTION("min_max_int_opt exceeds max", "should return 1") { - const char *opt_arg = "fail"; + SECTION("free_map_sections", "should not exit") { + free_map_sections(maps); + REQUIRE(maps[map_section_num].xmlname == nullptr); + free_map_sections(maps); + REQUIRE(maps[map_section_num].xmlname == nullptr); + } - if (setjmp(exit_jump) == 0) { - min_max_int_opt(opt_arg, opt_type_name, dmin, dmax); - FAIL("min_max_int_opt should have called exit() but didn't"); - } else { - SUCCEED("Captured expected exit() call due to asprintf failure"); - } - REQUIRE(exit_status == 1); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Invalid " + std::string(opt_type_name) + ", must be an integer (" + std::string(opt_arg) + " was provided)")); + SECTION("free_renderd_section", "should not exit") { + free_renderd_section(&config_slaves[active_renderd_section_num]); + REQUIRE(config->name == nullptr); + REQUIRE(config_slaves[active_renderd_section_num].name == nullptr); + free_renderd_section(&config_slaves[active_renderd_section_num]); + REQUIRE(config->name == nullptr); + REQUIRE(config_slaves[active_renderd_section_num].name == nullptr); + } + + SECTION("free_renderd_sections", "should not exit") { + free_renderd_sections(config_slaves); + REQUIRE(config->name == nullptr); + REQUIRE(config_slaves[active_renderd_section_num].name == nullptr); + free_renderd_sections(config_slaves); + REQUIRE(config->name == nullptr); + REQUIRE(config_slaves[active_renderd_section_num].name == nullptr); } } From 94ba49b07f3465d019d3ca9944a4dd12cd943f5a Mon Sep 17 00:00:00 2001 From: David Hummel <6109326+hummeltech@users.noreply.github.com> Date: Sat, 21 Mar 2026 17:55:31 -0700 Subject: [PATCH 07/10] Adding unit tests --- src/CMakeLists.txt | 19 ++++++++++++++++ tests/CMakeLists.txt | 5 +++++ tests/catch_test_common.cpp | 13 ++++++++++- tests/catch_test_common.hpp | 1 + tests/unit_test_sys_utils.cpp | 42 +++++++++++++++++++++++++++++++++++ 5 files changed, 79 insertions(+), 1 deletion(-) create mode 100644 tests/unit_test_sys_utils.cpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 03640376..541edff4 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -327,4 +327,23 @@ target_compile_definitions(unit_test_renderd_config PRIVATE target_include_directories(unit_test_renderd_config PRIVATE ${PROJECT_SOURCE_DIR}/src ${PROJECT_SOURCE_DIR}/tests) target_link_libraries(unit_test_renderd_config ${unit_test_renderd_config_LIBS}) + +#----------------------------------------------------------------------------- +# +# unit_test_sys_utils +# +#----------------------------------------------------------------------------- + +set(unit_test_sys_utils_SRCS + $ + ${PROJECT_SOURCE_DIR}/tests/unit_test_sys_utils.cpp +) +set(unit_test_sys_utils_LIBS + ${GLIB_LIBRARIES} +) +add_executable(unit_test_sys_utils ${unit_test_sys_utils_SRCS}) +set_target_properties(unit_test_sys_utils PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/tests) +target_include_directories(unit_test_sys_utils PRIVATE ${PROJECT_SOURCE_DIR}/src ${PROJECT_SOURCE_DIR}/tests) +target_link_libraries(unit_test_sys_utils ${unit_test_sys_utils_LIBS}) + endif() diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 6e8b80be..79f1cdc3 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -245,6 +245,11 @@ add_test( COMMAND unit_test_renderd_config ) +add_test( + NAME unit_test_sys_utils + COMMAND unit_test_sys_utils +) + foreach(STORAGE_BACKEND_INDEX RANGE ${STORAGE_BACKENDS_LENGTH}) # Get STORAGE_BACKEND from STORAGE_BACKENDS list list(GET STORAGE_BACKENDS ${STORAGE_BACKEND_INDEX} STORAGE_BACKEND) diff --git a/tests/catch_test_common.cpp b/tests/catch_test_common.cpp index a7152344..57864699 100644 --- a/tests/catch_test_common.cpp +++ b/tests/catch_test_common.cpp @@ -44,6 +44,7 @@ bool fail_next_asprintf = false; bool fail_next_connect = false; bool fail_next_getaddrinfo = false; bool fail_next_getaddrinfo_empty_res = false; +bool fail_next_getloadavg = false; bool fail_next_malloc = false; bool fail_next_mkdir = false; bool fail_next_open = false; @@ -156,7 +157,6 @@ void start_capture(bool debug) setenv("G_MESSAGES_DEBUG", "all", 1); #if GLIB_MAJOR_VERSION == 2 && GLIB_MINOR_VERSION >= 79 // https://gitlab.gnome.org/GNOME/glib/-/merge_requests/3710 - std::cout << "Resetting G_MESSAGES_DEBUG env var in runtime no longer has an effect.\n"; const gchar *domains[] = {"all", NULL}; g_log_writer_default_set_debug_domains(domains); #endif @@ -170,6 +170,7 @@ std::tuple end_capture(bool print) { setenv("G_MESSAGES_DEBUG", "", 1); #if GLIB_MAJOR_VERSION == 2 && GLIB_MINOR_VERSION >= 79 + // https://gitlab.gnome.org/GNOME/glib/-/merge_requests/3710 g_log_writer_default_set_debug_domains(NULL); #endif foreground = 0; @@ -343,6 +344,16 @@ extern "C" { return getaddrinfo(node, service, hints, res); } + int mocked_getloadavg(double loadavg[], int nelem) + { + if (fail_next_getloadavg) { + fail_next_getloadavg = false; + return -1; + } + + return getloadavg(loadavg, nelem); + } + void *mocked_malloc(size_t size) { if (fail_next_malloc) { diff --git a/tests/catch_test_common.hpp b/tests/catch_test_common.hpp index c8311c11..26344db7 100644 --- a/tests/catch_test_common.hpp +++ b/tests/catch_test_common.hpp @@ -55,6 +55,7 @@ extern "C" { int mocked_asprintf(char **strp, const char *fmt, ...); int mocked_connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen); int mocked_getaddrinfo(const char *node, const char *service, const struct addrinfo *hints, struct addrinfo **res); + int mocked_getloadavg(double loadavg[], int nelem); void *mocked_malloc(size_t size); int mocked_mkdir(const char *path, mode_t mode); int mocked_open(const char *pathname, int flags, ...); diff --git a/tests/unit_test_sys_utils.cpp b/tests/unit_test_sys_utils.cpp new file mode 100644 index 00000000..33f58766 --- /dev/null +++ b/tests/unit_test_sys_utils.cpp @@ -0,0 +1,42 @@ +#include +#include +#include +#include + +#include "catch/catch.hpp" +#include "catch_test_common.hpp" +#include "sys_utils.h" + +extern bool fail_next_getloadavg; +extern std::string err_log_lines; + +#define g_logger mocked_g_logger +#define getloadavg mocked_getloadavg + +#include "sys_utils.c" + +#undef g_logger +#undef getloadavg + +TEST_CASE("get_load_avg function", "[get_load_avg]") +{ + err_log_lines.clear(); + + SECTION("get_load_avg", "should return positive") { + double loadavg = get_load_avg(); + + REQUIRE(loadavg > 0); + } + + SECTION("get_load_avg with unobtainable load average", "should return 1000") { + fail_next_getloadavg = true; + + double loadavg = get_load_avg(); + +#ifdef HAVE_GETLOADAVG + REQUIRE(loadavg == 1000); +#else + REQUIRE(loadavg > 0); +#endif + } +} From 543a57597e2a3355c7aef967416417c89e6fa880 Mon Sep 17 00:00:00 2001 From: David Hummel <6109326+hummeltech@users.noreply.github.com> Date: Sun, 22 Mar 2026 21:16:10 -0700 Subject: [PATCH 08/10] Adding unit tests --- src/CMakeLists.txt | 23 +++++- src/cache_expire.c | 49 ++++++------ src/metatile.cpp | 12 ++- src/render_submit_queue.c | 8 +- tests/CMakeLists.txt | 5 ++ tests/unit_test_render_submit_queue.cpp | 100 ++++++++++++++++++++++++ tests/unit_test_sys_utils.cpp | 2 + 7 files changed, 159 insertions(+), 40 deletions(-) create mode 100644 tests/unit_test_render_submit_queue.cpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 541edff4..fbdb251b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -294,17 +294,32 @@ set(unit_test_protocol_helper_SRCS ) set(unit_test_protocol_helper_LIBS ${GLIB_LIBRARIES} - ${INIPARSER_LIBRARIES} ) add_executable(unit_test_protocol_helper ${unit_test_protocol_helper_SRCS}) set_target_properties(unit_test_protocol_helper PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/tests) -target_compile_definitions(unit_test_protocol_helper PRIVATE - RENDERD_CONF="${PROJECT_SOURCE_DIR}/etc/renderd/renderd.conf.examples" -) target_include_directories(unit_test_protocol_helper PRIVATE ${PROJECT_SOURCE_DIR}/src ${PROJECT_SOURCE_DIR}/tests) target_link_libraries(unit_test_protocol_helper ${unit_test_protocol_helper_LIBS}) +#----------------------------------------------------------------------------- +# +# unit_test_render_submit_queue +# +#----------------------------------------------------------------------------- + +set(unit_test_render_submit_queue_SRCS + $ + ${PROJECT_SOURCE_DIR}/tests/unit_test_render_submit_queue.cpp +) +set(unit_test_render_submit_queue_LIBS + ${GLIB_LIBRARIES} +) +add_executable(unit_test_render_submit_queue ${unit_test_render_submit_queue_SRCS}) +set_target_properties(unit_test_render_submit_queue PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/tests) +target_include_directories(unit_test_render_submit_queue PRIVATE ${PROJECT_SOURCE_DIR}/src ${PROJECT_SOURCE_DIR}/tests) +target_link_libraries(unit_test_render_submit_queue ${unit_test_render_submit_queue_LIBS}) + + #----------------------------------------------------------------------------- # # unit_test_renderd_config diff --git a/src/cache_expire.c b/src/cache_expire.c index ff128882..0d172f57 100644 --- a/src/cache_expire.c +++ b/src/cache_expire.c @@ -32,9 +32,9 @@ * URL. * RFC for HTCP can be found at http://www.htcp.org/ */ -static void cache_expire_url(int sock, char * url) +static void cache_expire_url(int sock, char *url) { - char * buf; + char *buf; if (sock < 0) { return; @@ -44,7 +44,7 @@ static void cache_expire_url(int sock, char * url) int url_len; url_len = strlen(url); - buf = (char *) malloc(12 + 22 + url_len); + buf = (char *)malloc(12 + 22 + url_len); if (!buf) { return; @@ -52,58 +52,58 @@ static void cache_expire_url(int sock, char * url) idx = 0; - //16 bit: Overall length of the datagram packet, including this header + // 16 bit: Overall length of the datagram packet, including this header *((uint16_t *)(&buf[idx])) = htons(12 + 22 + url_len); idx += 2; - //HTCP version. Currently at 0.0 - buf[idx++] = 0; //Major version - buf[idx++] = 0; //Minor version + // HTCP version. Currently at 0.0 + buf[idx++] = 0; // Major version + buf[idx++] = 0; // Minor version - //Length of HTCP data, including this field + // Length of HTCP data, including this field *((uint16_t *)(&buf[idx])) = htons(8 + 22 + url_len); idx += 2; - //HTCP opcode CLR=4 + // HTCP opcode CLR=4 buf[idx++] = 4; - //Reserved + // Reserved buf[idx++] = 0; - //32 bit transaction id; + // 32 bit transaction id; *((uint32_t *)(&buf[idx])) = htonl(255); idx += 4; buf[idx++] = 0; - buf[idx++] = 0; //HTCP reason + buf[idx++] = 0; // HTCP reason - //Length of the Method string + // Length of the Method string *((uint16_t *)(&buf[idx])) = htons(4); idx += 2; - ///Method string + /// Method string memcpy(&buf[idx], "HEAD", 4); idx += 4; - //Length of the url string + // Length of the url string *((uint16_t *)(&buf[idx])) = htons(url_len); idx += 2; - //Url string + // Url string memcpy(&buf[idx], url, url_len); idx += url_len; - //Length of version string + // Length of version string *((uint16_t *)(&buf[idx])) = htons(8); idx += 2; - //version string + // version string memcpy(&buf[idx], "HTTP/1.1", 8); idx += 8; - //Length of request headers. Currently 0 as we don't have any headers to send + // Length of request headers. Currently 0 as we don't have any headers to send *((uint16_t *)(&buf[idx])) = htons(0); - if (send(sock, (void *) buf, (12 + 22 + url_len), 0) < (12 + 22 + url_len)) { + if (send(sock, (void *)buf, (12 + 22 + url_len), 0) < (12 + 22 + url_len)) { g_logger(G_LOG_LEVEL_ERROR, "Failed to send HTCP purge for %s", url); }; @@ -117,13 +117,13 @@ void cache_expire(int sock, const char *host, const char *uri, int x, int y, int return; } - char * url = (char *)malloc(1024); + char *url = (char *)malloc(1024); snprintf(url, 1024, "http://%s%s%i/%i/%i.png", host, uri, z, x, y); cache_expire_url(sock, url); free(url); } -int init_cache_expire(const char * htcphost) +int init_cache_expire(const char *htcphost) { struct addrinfo hints; struct addrinfo *result, *rp; @@ -132,7 +132,7 @@ int init_cache_expire(const char * htcphost) /* Obtain address(es) matching host/port */ memset(&hints, 0, sizeof(struct addrinfo)); - hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */ + hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */ hints.ai_socktype = SOCK_DGRAM; /* Datagram socket */ hints.ai_flags = 0; hints.ai_protocol = 0; /* Any protocol */ @@ -157,7 +157,7 @@ int init_cache_expire(const char * htcphost) } if (connect(sfd, rp->ai_addr, rp->ai_addrlen) != -1) { - break; /* Success */ + break; /* Success */ } close(sfd); @@ -171,5 +171,4 @@ int init_cache_expire(const char * htcphost) freeaddrinfo(result); /* No longer needed */ return sfd; - } diff --git a/src/metatile.cpp b/src/metatile.cpp index d8a8acc7..6a92142e 100644 --- a/src/metatile.cpp +++ b/src/metatile.cpp @@ -28,8 +28,7 @@ #include "render_config.h" #include "store.h" -metaTile::metaTile(const std::string &xmlconfig, const std::string &options, int x, int y, int z): - x_(x), y_(y), z_(z), xmlconfig_(xmlconfig), options_(options) +metaTile::metaTile(const std::string &xmlconfig, const std::string &options, int x, int y, int z) : x_(x), y_(y), z_(z), xmlconfig_(xmlconfig), options_(options) { clear(); } @@ -59,13 +58,13 @@ int metaTile::xyz_to_meta_offset(int x, int y, int z) return (x & mask) * METATILE + (y & mask); } -void metaTile::save(struct storage_backend * store) +void metaTile::save(struct storage_backend *store) { int ox, oy, limit; ssize_t offset; struct meta_layout m; struct entry offsets[METATILE * METATILE]; - char * metatilebuffer; + char *metatilebuffer; char *tmp; memset(&m, 0, sizeof(m)); @@ -86,12 +85,12 @@ void metaTile::save(struct storage_backend * store) for (oy = 0; oy < limit; oy++) { int mt = xyz_to_meta_offset(x_ + ox, y_ + oy, z_); offsets[mt].offset = offset; - offsets[mt].size = tile[ox][oy].size(); + offsets[mt].size = tile[ox][oy].size(); offset += offsets[mt].size; } } - metatilebuffer = (char *) malloc(offset); + metatilebuffer = (char *)malloc(offset); if (metatilebuffer == 0) { g_logger(G_LOG_LEVEL_WARNING, "Failed to write metatile. Out of memory"); @@ -118,7 +117,6 @@ void metaTile::save(struct storage_backend * store) free(metatilebuffer); } - void metaTile::expire_tiles(int sock, const char *host, const char *uri) { if (sock < 0) { diff --git a/src/render_submit_queue.c b/src/render_submit_queue.c index 28315dc8..e6cb6c1f 100644 --- a/src/render_submit_queue.c +++ b/src/render_submit_queue.c @@ -174,7 +174,7 @@ static struct protocol *fetch(void) pthread_cond_signal(&qCondNotFull); pthread_mutex_unlock(&qLock); - struct protocol *cmd = malloc(sizeof(struct protocol)); + struct protocol *cmd = (struct protocol *)malloc(sizeof(struct protocol)); cmd->ver = 2; cmd->cmd = cmdRenderBulk; @@ -192,7 +192,7 @@ static struct protocol *fetch(void) void enqueue(const char *xmlname, int x, int y, int z) { // Add this path in the local render queue - struct qItem *e = malloc(sizeof(struct qItem)); + struct qItem *e = (struct qItem *)malloc(sizeof(struct qItem)); e->mapname = strdup(xmlname); e->x = x; @@ -265,7 +265,7 @@ int make_connection(const char *spath) if (!hostname_len) { hostname = strdup(RENDERD_HOST); } else { - hostname = malloc(hostname_len + sizeof('\0')); + hostname = (char *)malloc(hostname_len + sizeof('\0')); assert(hostname != NULL); strncpy(hostname, spath, hostname_len); } @@ -408,7 +408,7 @@ void spawn_workers(int num, const char *spath, int max_load) qMaxLen = no_workers; g_logger(G_LOG_LEVEL_MESSAGE, "Starting %d rendering threads", no_workers); - workers = calloc(sizeof(pthread_t), no_workers); + workers = (pthread_t *)calloc(sizeof(pthread_t), no_workers); if (!workers) { g_logger(G_LOG_LEVEL_CRITICAL, "Error allocating worker memory: %s", strerror(errno)); diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 79f1cdc3..3982eab0 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -240,6 +240,11 @@ add_test( COMMAND unit_test_protocol_helper ) +add_test( + NAME unit_test_render_submit_queue + COMMAND unit_test_render_submit_queue +) + add_test( NAME unit_test_renderd_config COMMAND unit_test_renderd_config diff --git a/tests/unit_test_render_submit_queue.cpp b/tests/unit_test_render_submit_queue.cpp new file mode 100644 index 00000000..d8c141c5 --- /dev/null +++ b/tests/unit_test_render_submit_queue.cpp @@ -0,0 +1,100 @@ +#include +#include +#include +#include +#include +#include + +#include "catch/catch.hpp" +#include "catch_test_common.hpp" +#include "render_submit_queue.h" +#include "sys_utils.h" + +extern int exit_status; +extern bool fail_next_getloadavg; +extern bool fail_next_socket; +extern jmp_buf exit_jump; +extern std::string err_log_lines; + +extern "C" { +#define exit mocked_exit +#define g_logger mocked_g_logger +#define getloadavg mocked_getloadavg +#define socket mocked_socket + +#include "protocol_helper.c" +#include "render_submit_queue.c" +#include "sys_utils.c" + +#undef exit +#undef g_logger +#undef getloadavg +#undef socket +} + +TEST_CASE("check_load function", "[check_load]") +{ + err_log_lines.clear(); + + maxLoad = 999; + auto start = std::chrono::high_resolution_clock::now(); + + SECTION("check_load with max load of 999", "should return") { + check_load(); + } + + SECTION("check_load with max load of 999 and unobtainable load average (which returns 1000)", "should return after sleeping 5 seconds") { + fail_next_getloadavg = true; + check_load(); + REQUIRE(std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - start).count() >= 5); + } +} + +TEST_CASE("process function", "[process]") +{ + int fd, ret; + int pipefd[2]; + pipe(pipefd); + struct protocol *cmd = (struct protocol *)malloc(sizeof(struct protocol)); + + err_log_lines.clear(); + + fd = pipefd[0]; + + SECTION("process", "should return positive") { + ret = process(cmd, fd); + + REQUIRE(ret == 0); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Sending request")); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("send error: Success")); + } +} + +TEST_CASE("make_connection function", "[make_connection]") +{ + int ret; + std::string socket_path = std::string(P_tmpdir) + "/renderd.sock"; + + err_log_lines.clear(); + exit_status = 0; + + SECTION("make_connection", "should return positive") { + ret = make_connection(socket_path.c_str()); + + // REQUIRE(ret > 0); + } + + SECTION("make_connection handles socket failure by exiting", "should exit 2") { + fail_next_socket = true; + + if (setjmp(exit_jump) == 0) { + make_connection(socket_path.c_str()); + FAIL("make_connection should have called exit() but didn't"); + } else { + SUCCEED("Captured expected exit() call due to socket failure"); + } + + REQUIRE(exit_status == 2); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("failed to create unix socket")); + } +} diff --git a/tests/unit_test_sys_utils.cpp b/tests/unit_test_sys_utils.cpp index 33f58766..b92cc354 100644 --- a/tests/unit_test_sys_utils.cpp +++ b/tests/unit_test_sys_utils.cpp @@ -10,6 +10,7 @@ extern bool fail_next_getloadavg; extern std::string err_log_lines; +extern "C" { #define g_logger mocked_g_logger #define getloadavg mocked_getloadavg @@ -17,6 +18,7 @@ extern std::string err_log_lines; #undef g_logger #undef getloadavg +} TEST_CASE("get_load_avg function", "[get_load_avg]") { From 5e74b7c0d2b73166626b2ae595a9e9861a988b24 Mon Sep 17 00:00:00 2001 From: David Hummel <6109326+hummeltech@users.noreply.github.com> Date: Sun, 22 Mar 2026 21:16:30 -0700 Subject: [PATCH 09/10] Organizing --- src/CMakeLists.txt | 136 +-- tests/CMakeLists.txt | 34 +- tests/unit_test_cache_expire.cpp | 239 ++--- tests/unit_test_metatile.cpp | 218 ++-- tests/unit_test_parameterize_style.cpp | 123 +-- tests/unit_test_protocol_helper.cpp | 265 ++--- tests/unit_test_render_submit_queue.cpp | 126 +-- tests/unit_test_renderd_config.cpp | 1217 +++++++++++------------ tests/unit_test_sys_utils.cpp | 44 +- tests/unit_tests.cpp | 148 +++ 10 files changed, 1175 insertions(+), 1375 deletions(-) create mode 100644 tests/unit_tests.cpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index fbdb251b..c115dcbf 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -223,142 +223,26 @@ target_link_libraries(gen_tile_test ${gen_tile_test_LIBS}) #----------------------------------------------------------------------------- # -# unit_test_cache_expire +# unit_tests # #----------------------------------------------------------------------------- -set(unit_test_cache_expire_SRCS +set(unit_tests_SRCS $ - ${PROJECT_SOURCE_DIR}/tests/unit_test_cache_expire.cpp + ${PROJECT_SOURCE_DIR}/tests/unit_tests.cpp ) -set(unit_test_cache_expire_LIBS - ${GLIB_LIBRARIES} -) -add_executable(unit_test_cache_expire ${unit_test_cache_expire_SRCS}) -set_target_properties(unit_test_cache_expire PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/tests) -target_include_directories(unit_test_cache_expire PRIVATE ${PROJECT_SOURCE_DIR}/src ${PROJECT_SOURCE_DIR}/tests) -target_link_libraries(unit_test_cache_expire ${unit_test_cache_expire_LIBS}) - - -#----------------------------------------------------------------------------- -# -# unit_test_metatile -# -#----------------------------------------------------------------------------- - -set(unit_test_metatile_SRCS - $ - ${PROJECT_SOURCE_DIR}/tests/unit_test_metatile.cpp -) -set(unit_test_metatile_LIBS - ${GLIB_LIBRARIES} -) -add_executable(unit_test_metatile ${unit_test_metatile_SRCS}) -set_target_properties(unit_test_metatile PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/tests) -target_include_directories(unit_test_metatile PRIVATE ${PROJECT_SOURCE_DIR}/src ${PROJECT_SOURCE_DIR}/tests) -target_link_libraries(unit_test_metatile ${unit_test_metatile_LIBS}) - - -#----------------------------------------------------------------------------- -# -# unit_test_parameterize_style -# -#----------------------------------------------------------------------------- - -set(unit_test_parameterize_style_SRCS - $ - ${PROJECT_SOURCE_DIR}/tests/unit_test_parameterize_style.cpp -) -set(unit_test_parameterize_style_LIBS +set(unit_tests_LIBS ${GLIB_LIBRARIES} + ${INIPARSER_LIBRARIES} ${LIBMAPNIK_LIBRARIES} ) -add_executable(unit_test_parameterize_style ${unit_test_parameterize_style_SRCS}) -set_target_properties(unit_test_parameterize_style PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/tests) -target_compile_definitions(unit_test_parameterize_style PRIVATE +add_executable(unit_tests ${unit_tests_SRCS}) +set_target_properties(unit_tests PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/tests) +target_compile_definitions(unit_tests PRIVATE MAPNIK_XML="${PROJECT_SOURCE_DIR}/utils/example-map/mapnik.xml" -) -target_include_directories(unit_test_parameterize_style PRIVATE ${PROJECT_SOURCE_DIR}/src ${PROJECT_SOURCE_DIR}/tests) -target_link_libraries(unit_test_parameterize_style ${unit_test_parameterize_style_LIBS}) - - -#----------------------------------------------------------------------------- -# -# unit_test_protocol_helper -# -#----------------------------------------------------------------------------- - -set(unit_test_protocol_helper_SRCS - $ - ${PROJECT_SOURCE_DIR}/tests/unit_test_protocol_helper.cpp -) -set(unit_test_protocol_helper_LIBS - ${GLIB_LIBRARIES} -) -add_executable(unit_test_protocol_helper ${unit_test_protocol_helper_SRCS}) -set_target_properties(unit_test_protocol_helper PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/tests) -target_include_directories(unit_test_protocol_helper PRIVATE ${PROJECT_SOURCE_DIR}/src ${PROJECT_SOURCE_DIR}/tests) -target_link_libraries(unit_test_protocol_helper ${unit_test_protocol_helper_LIBS}) - - -#----------------------------------------------------------------------------- -# -# unit_test_render_submit_queue -# -#----------------------------------------------------------------------------- - -set(unit_test_render_submit_queue_SRCS - $ - ${PROJECT_SOURCE_DIR}/tests/unit_test_render_submit_queue.cpp -) -set(unit_test_render_submit_queue_LIBS - ${GLIB_LIBRARIES} -) -add_executable(unit_test_render_submit_queue ${unit_test_render_submit_queue_SRCS}) -set_target_properties(unit_test_render_submit_queue PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/tests) -target_include_directories(unit_test_render_submit_queue PRIVATE ${PROJECT_SOURCE_DIR}/src ${PROJECT_SOURCE_DIR}/tests) -target_link_libraries(unit_test_render_submit_queue ${unit_test_render_submit_queue_LIBS}) - - -#----------------------------------------------------------------------------- -# -# unit_test_renderd_config -# -#----------------------------------------------------------------------------- - -set(unit_test_renderd_config_SRCS - $ - ${PROJECT_SOURCE_DIR}/tests/unit_test_renderd_config.cpp -) -set(unit_test_renderd_config_LIBS - ${GLIB_LIBRARIES} - ${INIPARSER_LIBRARIES} -) -add_executable(unit_test_renderd_config ${unit_test_renderd_config_SRCS}) -set_target_properties(unit_test_renderd_config PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/tests) -target_compile_definitions(unit_test_renderd_config PRIVATE RENDERD_CONF="${PROJECT_SOURCE_DIR}/etc/renderd/renderd.conf.examples" ) -target_include_directories(unit_test_renderd_config PRIVATE ${PROJECT_SOURCE_DIR}/src ${PROJECT_SOURCE_DIR}/tests) -target_link_libraries(unit_test_renderd_config ${unit_test_renderd_config_LIBS}) - - -#----------------------------------------------------------------------------- -# -# unit_test_sys_utils -# -#----------------------------------------------------------------------------- - -set(unit_test_sys_utils_SRCS - $ - ${PROJECT_SOURCE_DIR}/tests/unit_test_sys_utils.cpp -) -set(unit_test_sys_utils_LIBS - ${GLIB_LIBRARIES} -) -add_executable(unit_test_sys_utils ${unit_test_sys_utils_SRCS}) -set_target_properties(unit_test_sys_utils PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/tests) -target_include_directories(unit_test_sys_utils PRIVATE ${PROJECT_SOURCE_DIR}/src ${PROJECT_SOURCE_DIR}/tests) -target_link_libraries(unit_test_sys_utils ${unit_test_sys_utils_LIBS}) +target_include_directories(unit_tests PRIVATE ${PROJECT_SOURCE_DIR}/src ${PROJECT_SOURCE_DIR}/tests) +target_link_libraries(unit_tests ${unit_tests_LIBS}) endif() diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 3982eab0..bdb0971d 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -221,38 +221,8 @@ add_test( ) add_test( - NAME unit_test_cache_expire - COMMAND unit_test_cache_expire -) - -add_test( - NAME unit_test_metatile - COMMAND unit_test_metatile -) - -add_test( - NAME unit_test_parameterize_style - COMMAND unit_test_parameterize_style -) - -add_test( - NAME unit_test_protocol_helper - COMMAND unit_test_protocol_helper -) - -add_test( - NAME unit_test_render_submit_queue - COMMAND unit_test_render_submit_queue -) - -add_test( - NAME unit_test_renderd_config - COMMAND unit_test_renderd_config -) - -add_test( - NAME unit_test_sys_utils - COMMAND unit_test_sys_utils + NAME unit_tests + COMMAND unit_tests ) foreach(STORAGE_BACKEND_INDEX RANGE ${STORAGE_BACKENDS_LENGTH}) diff --git a/tests/unit_test_cache_expire.cpp b/tests/unit_test_cache_expire.cpp index bed2a3fb..f4a667ed 100644 --- a/tests/unit_test_cache_expire.cpp +++ b/tests/unit_test_cache_expire.cpp @@ -1,157 +1,132 @@ -#include -#include -#include -#include -#include -#include - #include "catch/catch.hpp" #include "catch_test_common.hpp" -extern bool fail_next_connect; -extern bool fail_next_getaddrinfo; -extern bool fail_next_getaddrinfo_empty_res; -extern bool fail_next_malloc; -extern bool fail_next_socket; -extern std::string err_log_lines; - -#define g_logger mocked_g_logger -#define connect mocked_connect -#define getaddrinfo mocked_getaddrinfo -#define malloc mocked_malloc -#define socket mocked_socket - -#include "cache_expire.c" - -#undef g_logger -#undef connect -#undef getaddrinfo -#undef malloc -#undef socket - -int x = 0; -int y = 0; -int z = 0; -std::string host("host"); -std::string uri("/uri/"); - -TEST_CASE("cache_expire_url function failure handling", "[cache_expire_url]") +TEST_CASE("cache_expire.c", "[cache_expire]") { - std::string url("http://" + host + uri + std::to_string(z) + "/" + std::to_string(x) + "/" + std::to_string(y) + ".png"); - - err_log_lines.clear(); + int x = 0; + int y = 0; + int z = 0; + std::string host("host"); + std::string uri("/uri/"); - SECTION("cache_expire_url", "should return") { - int sock = 0; + SECTION("cache_expire_url function") + { + std::string url("http://" + host + uri + std::to_string(z) + "/" + std::to_string(x) + "/" + std::to_string(y) + ".png"); - cache_expire_url(sock, (char *)url.c_str()); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Failed to send HTCP purge for " + url)); - } + err_log_lines.clear(); - SECTION("cache_expire_url handles malloc failure", "should return") { - int sock = 0; + SECTION("cache_expire_url", "should return") { + int sock = 0; - fail_next_malloc = true; + cache_expire_url(sock, (char *)url.c_str()); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Failed to send HTCP purge for " + url)); + } - cache_expire_url(sock, (char *)url.c_str()); - REQUIRE(err_log_lines == ""); - } + SECTION("cache_expire_url handles malloc failure", "should return") { + int sock = 0; - SECTION("cache_expire_url handles negative sock", "should return") { - int sock = -1; - - cache_expire_url(sock, (char *)url.c_str()); - REQUIRE(err_log_lines == ""); - } -} + fail_next_malloc = true; -TEST_CASE("cache_expire function failure handling", "[cache_expire]") -{ - err_log_lines.clear(); + cache_expire_url(sock, (char *)url.c_str()); + REQUIRE(err_log_lines == ""); + } - SECTION("cache_expire", "should return") { - int sock = 0; + SECTION("cache_expire_url handles negative sock", "should return") { + int sock = -1; - cache_expire(sock, (char *)host.c_str(), (char *)uri.c_str(), x, y, z); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Failed to send HTCP purge for http://" + host + uri + std::to_string(z) + "/" + std::to_string(x) + "/" + std::to_string(y) + ".png")); + cache_expire_url(sock, (char *)url.c_str()); + REQUIRE(err_log_lines == ""); + } } - SECTION("cache_expire handles malloc failure", "should return") { - int sock = 0; + SECTION("cache_expire function") + { + err_log_lines.clear(); - cache_expire(sock, (char *)host.c_str(), (char *)uri.c_str(), x, y, z); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Failed to send HTCP purge for http://" + host + uri + std::to_string(z) + "/" + std::to_string(x) + "/" + std::to_string(y) + ".png")); - } + SECTION("cache_expire", "should return") { + int sock = 0; - SECTION("cache_expire handles negative sock", "should return") { - int sock = -1; + cache_expire(sock, (char *)host.c_str(), (char *)uri.c_str(), x, y, z); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Failed to send HTCP purge for http://" + host + uri + std::to_string(z) + "/" + std::to_string(x) + "/" + std::to_string(y) + ".png")); + } - cache_expire(sock, (char *)host.c_str(), (char *)uri.c_str(), x, y, z); - REQUIRE(err_log_lines == ""); - } -} + SECTION("cache_expire handles malloc failure", "should return") { + int sock = 0; -TEST_CASE("init_cache_expire function failure handling", "[init_cache_expire]") -{ - err_log_lines.clear(); + cache_expire(sock, (char *)host.c_str(), (char *)uri.c_str(), x, y, z); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Failed to send HTCP purge for http://" + host + uri + std::to_string(z) + "/" + std::to_string(x) + "/" + std::to_string(y) + ".png")); + } - SECTION("init_cache_expire", "should return") { - std::string htcphost("localhost"); - init_cache_expire((char *)htcphost.c_str()); - REQUIRE(err_log_lines == ""); - } + SECTION("cache_expire handles negative sock", "should return") { + int sock = -1; - SECTION("init_cache_expire with nonexistent host", "should return") { - std::string htcphost("nonexistenthost"); - init_cache_expire((char *)htcphost.c_str()); - REQUIRE_THAT(err_log_lines, - Catch::Matchers::Contains("Failed to lookup HTCP cache host: Address family for hostname not supported") - || - Catch::Matchers::Contains("Failed to lookup HTCP cache host: Name or service not known") - || - Catch::Matchers::Contains("Failed to lookup HTCP cache host: Temporary failure in name resolution") - || - Catch::Matchers::Contains("Failed to lookup HTCP cache host: nodename nor servname provided, or not known") - ); + cache_expire(sock, (char *)host.c_str(), (char *)uri.c_str(), x, y, z); + REQUIRE(err_log_lines == ""); + } } - SECTION("init_cache_expire with failed socket", "should return") { - std::string htcphost("localhost"); - - fail_next_socket = true; - - init_cache_expire((char *)htcphost.c_str()); - REQUIRE(err_log_lines == ""); - } - - SECTION("init_cache_expire with failed getaddrinfo", "should return") { - std::string htcphost("localhost"); - - fail_next_getaddrinfo = true; - - init_cache_expire((char *)htcphost.c_str()); - REQUIRE_THAT(err_log_lines, - Catch::Matchers::Contains("Failed to lookup HTCP cache host: Bad value for ai_flags") - || - Catch::Matchers::Contains("Failed to lookup HTCP cache host: Unknown error") - ); - } - - SECTION("init_cache_expire with empty getaddrinfo response", "should return") { - std::string htcphost("localhost"); - - fail_next_getaddrinfo_empty_res = true; - - init_cache_expire((char *)htcphost.c_str()); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Failed to create HTCP cache socket")); - } - - SECTION("init_cache_expire with failed connect", "should return") { - std::string htcphost("localhost"); - - fail_next_connect = true; - - init_cache_expire((char *)htcphost.c_str()); - REQUIRE(err_log_lines == ""); + SECTION("init_cache_expire functiong") + { + err_log_lines.clear(); + + SECTION("init_cache_expire", "should return") { + std::string htcphost("localhost"); + init_cache_expire((char *)htcphost.c_str()); + REQUIRE(err_log_lines == ""); + } + + SECTION("init_cache_expire with nonexistent host", "should return") { + std::string htcphost("nonexistenthost"); + init_cache_expire((char *)htcphost.c_str()); + REQUIRE_THAT(err_log_lines, + Catch::Matchers::Contains("Failed to lookup HTCP cache host: Address family for hostname not supported") + || + Catch::Matchers::Contains("Failed to lookup HTCP cache host: Name or service not known") + || + Catch::Matchers::Contains("Failed to lookup HTCP cache host: Temporary failure in name resolution") + || + Catch::Matchers::Contains("Failed to lookup HTCP cache host: nodename nor servname provided, or not known") + ); + } + + SECTION("init_cache_expire with failed socket", "should return") { + std::string htcphost("localhost"); + + fail_next_socket = true; + + init_cache_expire((char *)htcphost.c_str()); + REQUIRE(err_log_lines == ""); + } + + SECTION("init_cache_expire with failed getaddrinfo", "should return") { + std::string htcphost("localhost"); + + fail_next_getaddrinfo = true; + + init_cache_expire((char *)htcphost.c_str()); + REQUIRE_THAT(err_log_lines, + Catch::Matchers::Contains("Failed to lookup HTCP cache host: Bad value for ai_flags") + || + Catch::Matchers::Contains("Failed to lookup HTCP cache host: Unknown error") + ); + } + + SECTION("init_cache_expire with empty getaddrinfo response", "should return") { + std::string htcphost("localhost"); + + fail_next_getaddrinfo_empty_res = true; + + init_cache_expire((char *)htcphost.c_str()); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Failed to create HTCP cache socket")); + } + + SECTION("init_cache_expire with failed connect", "should return") { + std::string htcphost("localhost"); + + fail_next_connect = true; + + init_cache_expire((char *)htcphost.c_str()); + REQUIRE(err_log_lines == ""); + } } } diff --git a/tests/unit_test_metatile.cpp b/tests/unit_test_metatile.cpp index 5a2784cf..207f8dd5 100644 --- a/tests/unit_test_metatile.cpp +++ b/tests/unit_test_metatile.cpp @@ -1,173 +1,135 @@ -#include -#include -#include -#include -#include -#include - #include "catch/catch.hpp" #include "catch_test_common.hpp" -#include "metatile.h" - -extern bool fail_next_malloc; -extern bool fail_next_mkdir; -extern bool fail_next_open; -extern bool fail_next_write; -extern std::string err_log_lines; - -#define g_logger mocked_g_logger -#define malloc mocked_malloc -#define mkdir mocked_mkdir -#define open mocked_open -#define write mocked_write - -extern "C" { -#include "cache_expire.c" -#include "store_file.c" -#include "store_file_utils.c" - - struct storage_backend * init_storage_backend(const char * options) - { - struct storage_backend * store = init_storage_file(options); - return store; - } -} - -#include "metatile.cpp" - -#undef g_logger -#undef malloc -#undef mkdir -#undef open -#undef write -int x = 1024; -int y = 1024; -int z = 10; -std::string host("host"); -std::string metatile_path; -std::string tile_data_string = "DEADBEAF"; -std::string tile_dir; -std::string uri("/uri/"); -std::string xmlconfig = XMLCONFIG_DEFAULT; -struct storage_backend *store; - -metaTile tiles(xmlconfig.c_str(), "", x, y, z); - - -TEST_CASE("metaTile::save", "[metaTile::save] [metaTile::set]") +TEST_CASE("metatile.cpp", "[metatile]") { - tile_dir = create_tile_dir("mod_tile.unit_test_metatile"); - store = init_storage_backend(tile_dir.c_str()); + int x = 1024; + int y = 1024; + int z = 10; + std::string host("host"); + std::string metatile_path; + std::string tile_data_string = "DEADBEAF"; + std::string tile_dir; + std::string uri("/uri/"); + std::string xmlconfig = XMLCONFIG_DEFAULT; + struct storage_backend *store; + + metaTile tiles(xmlconfig.c_str(), "", x, y, z); + + SECTION("metaTile::save function") + { + tile_dir = create_tile_dir("mod_tile.unit_test_metatile"); + store = init_storage_backend(tile_dir.c_str()); - metatile_path = std::string((char *)store->storage_ctx) + "/" + xmlconfig + "/" + std::to_string(z) + "/0/0/68/0/0.meta"; + metatile_path = std::string((char *)store->storage_ctx) + "/" + xmlconfig + "/" + std::to_string(z) + "/0/0/68/0/0.meta"; - err_log_lines.clear(); + err_log_lines.clear(); - for (int xx = 0; xx < METATILE; xx++) { - for (int yy = 0; yy < METATILE; yy++) { - std::string tile_set_data(tile_data_string + " " + std::to_string(xx) + " " + std::to_string(yy)); - tiles.set(xx, yy, tile_set_data); + for (int xx = 0; xx < METATILE; xx++) { + for (int yy = 0; yy < METATILE; yy++) { + std::string tile_set_data(tile_data_string + " " + std::to_string(xx) + " " + std::to_string(yy)); + tiles.set(xx, yy, tile_set_data); + } } - } - SECTION("metaTile::save handles malloc failure", "should return") { - fail_next_malloc = true; + SECTION("metaTile::save handles malloc failure", "should return") { + fail_next_malloc = true; - tiles.save(store); + tiles.save(store); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Failed to write metatile. Out of memory")); - } + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Failed to write metatile. Out of memory")); + } - SECTION("metaTile::save handles mkdir failure", "should return") { - fail_next_mkdir = true; + SECTION("metaTile::save handles mkdir failure", "should return") { + fail_next_mkdir = true; - tiles.save(store); + tiles.save(store); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Error creating directory " + metatile_path)); - } + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Error creating directory " + metatile_path)); + } - SECTION("metaTile::save handles open failure", "should return") { - fail_next_open = true; + SECTION("metaTile::save handles open failure", "should return") { + fail_next_open = true; - tiles.save(store); + tiles.save(store); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Error creating file " + metatile_path)); - } + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Error creating file " + metatile_path)); + } - SECTION("metaTile::save handles write failure", "should return") { - fail_next_write = true; + SECTION("metaTile::save handles write failure", "should return") { + fail_next_write = true; - tiles.save(store); + tiles.save(store); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Error writing file " + metatile_path)); - } + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Error writing file " + metatile_path)); + } - store->metatile_delete(store, xmlconfig.c_str(), 1024, 1024, 10); - store->close_storage(store); - delete_tile_dir(tile_dir); - tiles.clear(); -} + store->metatile_delete(store, xmlconfig.c_str(), 1024, 1024, 10); + store->close_storage(store); + delete_tile_dir(tile_dir); + tiles.clear(); + } -TEST_CASE("metaTile::set", "[metaTile::clear] [metaTile::get] [metaTile::set]") -{ - err_log_lines.clear(); + SECTION("metaTile::set function") + { + err_log_lines.clear(); - SECTION("metaTile::set", "then metaTile::get, then metaTile::clear, then metaTile::get") { - for (int xx = 0; xx < METATILE; xx++) { - for (int yy = 0; yy < METATILE; yy++) { - std::string tile_set_data(tile_data_string + " " + std::to_string(xx) + " " + std::to_string(yy)); - tiles.set(xx, yy, tile_set_data); + SECTION("metaTile::set", "then metaTile::get, then metaTile::clear, then metaTile::get") { + for (int xx = 0; xx < METATILE; xx++) { + for (int yy = 0; yy < METATILE; yy++) { + std::string tile_set_data(tile_data_string + " " + std::to_string(xx) + " " + std::to_string(yy)); + tiles.set(xx, yy, tile_set_data); - std::string tile_get_data = tiles.get(xx, yy); - CHECK(tile_get_data == tile_set_data); + std::string tile_get_data = tiles.get(xx, yy); + CHECK(tile_get_data == tile_set_data); + } } - } - tiles.clear(); + tiles.clear(); - for (int xx = 0; xx < METATILE; xx++) { - for (int yy = 0; yy < METATILE; yy++) { - std::string tile_get_data = tiles.get(xx, yy); - CHECK(tile_get_data == ""); + for (int xx = 0; xx < METATILE; xx++) { + for (int yy = 0; yy < METATILE; yy++) { + std::string tile_get_data = tiles.get(xx, yy); + CHECK(tile_get_data == ""); + } } } } -} -TEST_CASE("metaTile::expire_tiles", "[metaTile::expire_tiles]") -{ - err_log_lines.clear(); + SECTION("metaTile::expire_tiles function") + { + err_log_lines.clear(); - SECTION("metaTile::expire_tile", "should return") { - int sock = 0; + SECTION("metaTile::expire_tile", "should return") { + int sock = 0; - tiles.expire_tiles(sock, "host", "/uri/"); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Purging metatile via HTCP cache expiry")); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Failed to send HTCP purge for http://" + host + uri + std::to_string(z) + "/" + std::to_string(x) + "/" + std::to_string(y) + ".png")); - } + tiles.expire_tiles(sock, "host", "/uri/"); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Purging metatile via HTCP cache expiry")); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Failed to send HTCP purge for http://" + host + uri + std::to_string(z) + "/" + std::to_string(x) + "/" + std::to_string(y) + ".png")); + } - SECTION("metaTile::expire_tiles handles negative sock", "should return") { - int sock = -1; + SECTION("metaTile::expire_tiles handles negative sock", "should return") { + int sock = -1; - tiles.expire_tiles(sock, "host", "/uri/"); - REQUIRE(err_log_lines == ""); + tiles.expire_tiles(sock, "host", "/uri/"); + REQUIRE(err_log_lines == ""); + } } -} -TEST_CASE("metaTile::xyz_to_meta_offset", "[metaTile::xyz_to_meta_offset]") -{ - err_log_lines.clear(); + SECTION("metaTile::xyz_to_meta_offset function") + { + err_log_lines.clear(); - SECTION("metaTile::xyz_to_meta_offset", "should return") { - int i = 0; + SECTION("metaTile::xyz_to_meta_offset", "should return") { + int i = 0; - for (int xx = 0; xx < METATILE; xx++) { - for (int yy = 0; yy < METATILE; yy++) { - CHECK(tiles.xyz_to_meta_offset(xx, yy, z) == i++); + for (int xx = 0; xx < METATILE; xx++) { + for (int yy = 0; yy < METATILE; yy++) { + CHECK(tiles.xyz_to_meta_offset(xx, yy, z) == i++); + } } } } diff --git a/tests/unit_test_parameterize_style.cpp b/tests/unit_test_parameterize_style.cpp index f0627fb1..9a5516e1 100644 --- a/tests/unit_test_parameterize_style.cpp +++ b/tests/unit_test_parameterize_style.cpp @@ -1,100 +1,67 @@ -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#if MAPNIK_MAJOR_VERSION < 4 -#include -#endif - #include "catch/catch.hpp" #include "catch_test_common.hpp" -#include "config.h" -#include "parameterize_style.hpp" - -#ifndef MAPNIK_XML -#define MAPNIK_XML "./utils/example-map/mapnik.xml" -#endif - -#ifndef MAPNIK_PLUGINS_DIR -#define MAPNIK_PLUGINS_DIR "/usr/local/lib64/mapnik/input" -#endif - -extern bool fail_next_strtok; -extern std::string err_log_lines; - -#define g_logger mocked_g_logger -#define strtok mocked_strtok -#include "parameterize_style.cpp" - -#undef g_logger -#undef strtok - -TEST_CASE("parameterize_map_language function failure exit handling", "[parameterize_map_language]") +TEST_CASE("parameterize_style.cpp", "[parameterize_style]") { - const char * parameter = "en,de,_"; - mapnik::datasource_cache::instance().register_datasources(MAPNIK_PLUGINS_DIR); - mapnik::Map map(256, 256); - mapnik::load_map(map, MAPNIK_XML); - mapnik::layer layer = map.get_layer(0); - map.remove_all(); - mapnik::parameters parameters = layer.datasource()->params(); - parameters["table"] = ",name"; - layer.set_datasource(mapnik::datasource_cache::instance().create(parameters)); - map.add_layer(layer); + SECTION("parameterize_map_language function") + { + const char * parameter = "en,de,_"; + mapnik::datasource_cache::instance().register_datasources(MAPNIK_PLUGINS_DIR); + mapnik::Map map(256, 256); + mapnik::load_map(map, MAPNIK_XML); + mapnik::layer layer = map.get_layer(0); + map.remove_all(); + mapnik::parameters parameters = layer.datasource()->params(); + parameters["table"] = ",name"; + layer.set_datasource(mapnik::datasource_cache::instance().create(parameters)); + map.add_layer(layer); - err_log_lines.clear(); + err_log_lines.clear(); - SECTION("parameterize_map_language handles strtok failure", "should return") { - fail_next_strtok = true; + SECTION("parameterize_map_language handles strtok failure", "should return") { + fail_next_strtok = true; - parameterize_map_language(map, (char *)parameter); + parameterize_map_language(map, (char *)parameter); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Internationalizing map to language parameter: en,de,_")); - } + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Internationalizing map to language parameter: en,de,_")); + } - SECTION("parameterize_map_language modifies 'table' parameter", "should return") { - layer = map.get_layer(0); - REQUIRE(layer.datasource()->params().get("table") == std::string(",name")); + SECTION("parameterize_map_language modifies 'table' parameter", "should return") { + layer = map.get_layer(0); + REQUIRE(layer.datasource()->params().get("table") == std::string(",name")); - parameterize_map_language(map, (char *)parameter); + parameterize_map_language(map, (char *)parameter); - layer = map.get_layer(0); - REQUIRE(layer.datasource()->params().get("table") != std::string(",name")); + layer = map.get_layer(0); + REQUIRE(layer.datasource()->params().get("table") != std::string(",name")); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Internationalizing map to language parameter: en,de,_")); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Internationalizing map to language parameter: en,de,_")); + } } -} -TEST_CASE("init_parameterization_function function", "[init_parameterization_function]") -{ - err_log_lines.clear(); + SECTION("init_parameterization_function function") + { + err_log_lines.clear(); - SECTION("init_parameterization_function with empty function_name", "should return NULL") { - parameterize_function_ptr response = init_parameterization_function(""); + SECTION("init_parameterization_function with empty function_name", "should return NULL") { + parameterize_function_ptr response = init_parameterization_function(""); - REQUIRE(response == NULL); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Parameterize_style not specified (or empty string specified)")); - } + REQUIRE(response == NULL); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Parameterize_style not specified (or empty string specified)")); + } - SECTION("init_parameterization_function with non-'language' function_name", "should return NULL") { - parameterize_function_ptr response = init_parameterization_function("doesnotexist"); + SECTION("init_parameterization_function with non-'language' function_name", "should return NULL") { + parameterize_function_ptr response = init_parameterization_function("doesnotexist"); - REQUIRE(response == NULL); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("unknown parameterization function for 'doesnotexist'")); - } + REQUIRE(response == NULL); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("unknown parameterization function for 'doesnotexist'")); + } - SECTION("init_parameterization_function with 'language' function_name", "should return parameterize_map_language") { - parameterize_function_ptr response = init_parameterization_function("language"); + SECTION("init_parameterization_function with 'language' function_name", "should return parameterize_map_language") { + parameterize_function_ptr response = init_parameterization_function("language"); - REQUIRE(response == parameterize_map_language); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Loading parameterization function for 'language'")); + REQUIRE(response == parameterize_map_language); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Loading parameterization function for 'language'")); + } } } diff --git a/tests/unit_test_protocol_helper.cpp b/tests/unit_test_protocol_helper.cpp index 77ca2222..7cccba5a 100644 --- a/tests/unit_test_protocol_helper.cpp +++ b/tests/unit_test_protocol_helper.cpp @@ -1,69 +1,12 @@ -#include -#include -#include -#include -#include -#include -#include - #include "catch/catch.hpp" #include "catch_test_common.hpp" -#include "protocol.h" -#include "protocol_helper.h" - -extern std::string err_log_lines; - -extern "C" { - bool fail_next_recv = false; - int fail_next_recv_reponse_size = -1; - int fail_next_recv_reponse_version = -1; - int fail_next_next_recv_reponse_size = -1; - int fail_next_next_recv_reponse_version = -1; - - ssize_t mocked_recv(int fd, void *buf, size_t n, int flags) - { - if (fail_next_recv) { - fail_next_recv = false; - return -1; - } - - if (fail_next_recv_reponse_size != -1) { - int reponse_size = fail_next_recv_reponse_size; - fail_next_recv_reponse_size = (fail_next_next_recv_reponse_size != -1) ? fail_next_next_recv_reponse_size : -1; - fail_next_next_recv_reponse_size = -1; - - if (fail_next_recv_reponse_version != -1) { - struct protocol *cmd = (struct protocol *)malloc(sizeof(struct protocol)); - cmd->ver = fail_next_recv_reponse_version; - fail_next_recv_reponse_version = (fail_next_next_recv_reponse_version != -1) ? fail_next_next_recv_reponse_version : -1; - fail_next_next_recv_reponse_version = -1; - - memcpy(buf, cmd, reponse_size); - - free(cmd); - } - - return reponse_size; - } - - return recv(fd, buf, n, flags); - } -} - -#define g_logger mocked_g_logger -#define recv mocked_recv - -#include "protocol_helper.c" -#undef g_logger -#undef recv - -int x = 1024; -int y = 1024; -int z = 10; - -TEST_CASE("protocol_helper", "Test protocol_helper.c") +TEST_CASE("protocol_helper.c", "[protocol_helper]") { + int x = 1024; + int y = 1024; + int z = 10; + int block = 1, fd, ret; int pipefd[2]; pipe(pipefd); @@ -77,139 +20,147 @@ TEST_CASE("protocol_helper", "Test protocol_helper.c") cmd->y = y; cmd->z = z; - fd = pipefd[1]; - SECTION("send_cmd with invalid version", "should return -1") { - cmd->ver = GENERATE(0, 4); + SECTION("send_cmd function") + { + fd = pipefd[1]; - ret = send_cmd(cmd, fd); + SECTION("send_cmd with invalid version", "should return -1") { + cmd->ver = GENERATE(0, 4); - REQUIRE(ret == -1); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Failed to send render cmd with unknown protocol version " + std::to_string(cmd->ver))); - } + ret = send_cmd(cmd, fd); - SECTION("send_cmd with invalid fd", "should return -1") { - cmd->ver = GENERATE(1, 2, 3); + REQUIRE(ret == -1); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Failed to send render cmd with unknown protocol version " + std::to_string(cmd->ver))); + } + + SECTION("send_cmd with invalid fd", "should return -1") { + cmd->ver = GENERATE(1, 2, 3); - ret = send_cmd(cmd, fd); + ret = send_cmd(cmd, fd); - REQUIRE(ret == -1); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Failed to send render cmd on fd " + std::to_string(fd))); + REQUIRE(ret == -1); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Failed to send render cmd on fd " + std::to_string(fd))); + } } - fd = pipefd[0]; - SECTION("recv_cmd with invalid version", "should return -1") { - fail_next_recv_reponse_size = sizeof(struct protocol); - fail_next_recv_reponse_version = GENERATE(0, 4); - std::string expected_message = "Failed to receive render cmd with unknown protocol version " + std::to_string(fail_next_recv_reponse_version); + SECTION("recv_cmd function") + { + fd = pipefd[0]; - ret = recv_cmd(&rsp, fd, block); + SECTION("recv_cmd with invalid version", "should return -1") { + fail_next_recv_reponse_size = sizeof(struct protocol); + fail_next_recv_reponse_version = GENERATE(0, 4); + std::string expected_message = "Failed to receive render cmd with unknown protocol version " + std::to_string(fail_next_recv_reponse_version); - REQUIRE(ret == -1); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains(expected_message)); - } + ret = recv_cmd(&rsp, fd, block); - SECTION("recv_cmd with invalid fd", "should return -1") { - ret = recv_cmd(&rsp, fd, block); + REQUIRE(ret == -1); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains(expected_message)); + } - REQUIRE(ret == -1); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Failed to read cmd on fd " + std::to_string(fd))); - } + SECTION("recv_cmd with invalid fd", "should return -1") { + ret = recv_cmd(&rsp, fd, block); - SECTION("recv_cmd with incomplete response", "should return 0") { - fail_next_recv_reponse_size = 1; + REQUIRE(ret == -1); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Failed to read cmd on fd " + std::to_string(fd))); + } - ret = recv_cmd(&rsp, fd, block); + SECTION("recv_cmd with incomplete response", "should return 0") { + fail_next_recv_reponse_size = 1; - REQUIRE(ret == 0); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Read incomplete cmd on fd " + std::to_string(fd))); - } + ret = recv_cmd(&rsp, fd, block); - SECTION("recv_cmd with invalid version and correct size response", "should return -1") { - fail_next_recv_reponse_size = GENERATE(sizeof(struct protocol_v1), sizeof(struct protocol_v2), sizeof(struct protocol)); - fail_next_recv_reponse_version = GENERATE(0, 4); + REQUIRE(ret == 0); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Read incomplete cmd on fd " + std::to_string(fd))); + } - int expected_size = -1; - int expected_version = fail_next_recv_reponse_version; + SECTION("recv_cmd with invalid version and correct size response", "should return -1") { + fail_next_recv_reponse_size = GENERATE(sizeof(struct protocol_v1), sizeof(struct protocol_v2), sizeof(struct protocol)); + fail_next_recv_reponse_version = GENERATE(0, 4); - ret = recv_cmd(&rsp, fd, block); + int expected_size = -1; + int expected_version = fail_next_recv_reponse_version; - REQUIRE(ret == expected_size); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Failed to receive render cmd with unknown protocol version " + std::to_string(expected_version))); - } + ret = recv_cmd(&rsp, fd, block); - SECTION("recv_cmd with correct size response (v1)", "should return") { - fail_next_recv_reponse_size = sizeof(struct protocol_v1); - fail_next_recv_reponse_version = 1; + REQUIRE(ret == expected_size); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Failed to receive render cmd with unknown protocol version " + std::to_string(expected_version))); + } - int expected_size = fail_next_recv_reponse_size; - int expected_version = fail_next_recv_reponse_version; + SECTION("recv_cmd with correct size response (v1)", "should return") { + fail_next_recv_reponse_size = sizeof(struct protocol_v1); + fail_next_recv_reponse_version = 1; - ret = recv_cmd(&rsp, fd, block); + int expected_size = fail_next_recv_reponse_size; + int expected_version = fail_next_recv_reponse_version; - REQUIRE(ret == expected_size); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Got incoming request with protocol version " + std::to_string(expected_version))); - } + ret = recv_cmd(&rsp, fd, block); - SECTION("recv_cmd with correct size response (v2)", "should return") { - fail_next_recv_reponse_size = sizeof(struct protocol_v1); - fail_next_recv_reponse_version = 2; - fail_next_next_recv_reponse_size = sizeof(struct protocol_v2) - fail_next_recv_reponse_size; - fail_next_next_recv_reponse_version = fail_next_recv_reponse_version; + REQUIRE(ret == expected_size); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Got incoming request with protocol version " + std::to_string(expected_version))); + } - int expected_size = fail_next_recv_reponse_size + fail_next_next_recv_reponse_size; - int expected_version = fail_next_recv_reponse_version; + SECTION("recv_cmd with correct size response (v2)", "should return") { + fail_next_recv_reponse_size = sizeof(struct protocol_v1); + fail_next_recv_reponse_version = 2; + fail_next_next_recv_reponse_size = sizeof(struct protocol_v2) - fail_next_recv_reponse_size; + fail_next_next_recv_reponse_version = fail_next_recv_reponse_version; - ret = recv_cmd(&rsp, fd, block); + int expected_size = fail_next_recv_reponse_size + fail_next_next_recv_reponse_size; + int expected_version = fail_next_recv_reponse_version; - REQUIRE(ret == expected_size); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Got incoming request with protocol version " + std::to_string(expected_version))); - } + ret = recv_cmd(&rsp, fd, block); - SECTION("recv_cmd with correct size response (v3)", "should return") { - fail_next_recv_reponse_size = sizeof(struct protocol_v1); - fail_next_recv_reponse_version = 3; - fail_next_next_recv_reponse_size = sizeof(struct protocol) - fail_next_recv_reponse_size; - fail_next_next_recv_reponse_version = fail_next_recv_reponse_version; + REQUIRE(ret == expected_size); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Got incoming request with protocol version " + std::to_string(expected_version))); + } - int expected_size = fail_next_recv_reponse_size + fail_next_next_recv_reponse_size; - int expected_version = fail_next_recv_reponse_version; + SECTION("recv_cmd with correct size response (v3)", "should return") { + fail_next_recv_reponse_size = sizeof(struct protocol_v1); + fail_next_recv_reponse_version = 3; + fail_next_next_recv_reponse_size = sizeof(struct protocol) - fail_next_recv_reponse_size; + fail_next_next_recv_reponse_version = fail_next_recv_reponse_version; - ret = recv_cmd(&rsp, fd, block); + int expected_size = fail_next_recv_reponse_size + fail_next_next_recv_reponse_size; + int expected_version = fail_next_recv_reponse_version; - REQUIRE(ret == expected_size); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Got incoming request with protocol version " + std::to_string(expected_version))); - } + ret = recv_cmd(&rsp, fd, block); - SECTION("recv_cmd with incorrect size response (v2-v3)", "should return") { - fail_next_recv_reponse_size = sizeof(struct protocol_v1); - fail_next_recv_reponse_version = GENERATE(2, 3); - fail_next_next_recv_reponse_size = sizeof(struct protocol_v1); - fail_next_next_recv_reponse_version = fail_next_recv_reponse_version; + REQUIRE(ret == expected_size); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Got incoming request with protocol version " + std::to_string(expected_version))); + } - int expected_size = fail_next_recv_reponse_size + fail_next_next_recv_reponse_size; - int expected_version = fail_next_recv_reponse_version; + SECTION("recv_cmd with incorrect size response (v2-v3)", "should return") { + fail_next_recv_reponse_size = sizeof(struct protocol_v1); + fail_next_recv_reponse_version = GENERATE(2, 3); + fail_next_next_recv_reponse_size = sizeof(struct protocol_v1); + fail_next_next_recv_reponse_version = fail_next_recv_reponse_version; - ret = recv_cmd(&rsp, fd, block); + int expected_size = fail_next_recv_reponse_size + fail_next_next_recv_reponse_size; + int expected_version = fail_next_recv_reponse_version; - REQUIRE(ret == 0); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Socket read wrong number of bytes: " + std::to_string(expected_size))); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Got incoming request with protocol version " + std::to_string(expected_version))); - } + ret = recv_cmd(&rsp, fd, block); - SECTION("recv_cmd with incomplete second response (v2-v3)", "should return -1") { - fail_next_recv_reponse_size = sizeof(struct protocol_v1); - fail_next_recv_reponse_version = GENERATE(2, 3); - fail_next_next_recv_reponse_size = 0; - fail_next_next_recv_reponse_version = fail_next_recv_reponse_version; + REQUIRE(ret == 0); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Socket read wrong number of bytes: " + std::to_string(expected_size))); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Got incoming request with protocol version " + std::to_string(expected_version))); + } - int expected_size = -1; - int expected_version = fail_next_recv_reponse_version; + SECTION("recv_cmd with incomplete second response (v2-v3)", "should return -1") { + fail_next_recv_reponse_size = sizeof(struct protocol_v1); + fail_next_recv_reponse_version = GENERATE(2, 3); + fail_next_next_recv_reponse_size = 0; + fail_next_next_recv_reponse_version = fail_next_recv_reponse_version; - ret = recv_cmd(&rsp, fd, block); + int expected_size = -1; + int expected_version = fail_next_recv_reponse_version; - REQUIRE(ret == expected_size); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Got incoming request with protocol version " + std::to_string(expected_version))); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Socket prematurely closed: " + std::to_string(fd))); + ret = recv_cmd(&rsp, fd, block); + + REQUIRE(ret == expected_size); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Got incoming request with protocol version " + std::to_string(expected_version))); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Socket prematurely closed: " + std::to_string(fd))); + } } free(cmd); diff --git a/tests/unit_test_render_submit_queue.cpp b/tests/unit_test_render_submit_queue.cpp index d8c141c5..7d1e0615 100644 --- a/tests/unit_test_render_submit_queue.cpp +++ b/tests/unit_test_render_submit_queue.cpp @@ -1,100 +1,72 @@ -#include -#include -#include -#include -#include -#include - #include "catch/catch.hpp" #include "catch_test_common.hpp" -#include "render_submit_queue.h" -#include "sys_utils.h" - -extern int exit_status; -extern bool fail_next_getloadavg; -extern bool fail_next_socket; -extern jmp_buf exit_jump; -extern std::string err_log_lines; - -extern "C" { -#define exit mocked_exit -#define g_logger mocked_g_logger -#define getloadavg mocked_getloadavg -#define socket mocked_socket - -#include "protocol_helper.c" -#include "render_submit_queue.c" -#include "sys_utils.c" - -#undef exit -#undef g_logger -#undef getloadavg -#undef socket -} -TEST_CASE("check_load function", "[check_load]") +TEST_CASE("render_submit_queue.c", "[render_submit_queue]") { - err_log_lines.clear(); + SECTION("check_load function") + { + err_log_lines.clear(); - maxLoad = 999; - auto start = std::chrono::high_resolution_clock::now(); + maxLoad = 999; + auto start = std::chrono::high_resolution_clock::now(); - SECTION("check_load with max load of 999", "should return") { - check_load(); - } + SECTION("check_load with max load of 999", "should return") { + check_load(); + } - SECTION("check_load with max load of 999 and unobtainable load average (which returns 1000)", "should return after sleeping 5 seconds") { - fail_next_getloadavg = true; - check_load(); - REQUIRE(std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - start).count() >= 5); + SECTION("check_load with max load of 999 and unobtainable load average (which returns 1000)", "should return after sleeping 5 seconds") { + fail_next_getloadavg = true; + check_load(); + REQUIRE(std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - start).count() >= 5); + } } -} -TEST_CASE("process function", "[process]") -{ - int fd, ret; - int pipefd[2]; - pipe(pipefd); - struct protocol *cmd = (struct protocol *)malloc(sizeof(struct protocol)); + SECTION("process function") + { + int fd, ret; + int pipefd[2]; + pipe(pipefd); + struct protocol *cmd = (struct protocol *)malloc(sizeof(struct protocol)); - err_log_lines.clear(); + err_log_lines.clear(); - fd = pipefd[0]; + fd = pipefd[0]; - SECTION("process", "should return positive") { - ret = process(cmd, fd); + SECTION("process", "should return positive") { + ret = process(cmd, fd); - REQUIRE(ret == 0); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Sending request")); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("send error: Success")); + REQUIRE(ret == 0); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Sending request")); + // REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("send error: Success")); + } } -} -TEST_CASE("make_connection function", "[make_connection]") -{ - int ret; - std::string socket_path = std::string(P_tmpdir) + "/renderd.sock"; + SECTION("make_connection function") + { + int ret; + std::string socket_path = std::string(P_tmpdir) + "/renderd.sock"; - err_log_lines.clear(); - exit_status = 0; + err_log_lines.clear(); + exit_status = 0; - SECTION("make_connection", "should return positive") { - ret = make_connection(socket_path.c_str()); + SECTION("make_connection", "should return positive") { + ret = make_connection(socket_path.c_str()); - // REQUIRE(ret > 0); - } + // REQUIRE(ret > 0); + } - SECTION("make_connection handles socket failure by exiting", "should exit 2") { - fail_next_socket = true; + SECTION("make_connection handles socket failure by exiting", "should exit 2") { + fail_next_socket = true; - if (setjmp(exit_jump) == 0) { - make_connection(socket_path.c_str()); - FAIL("make_connection should have called exit() but didn't"); - } else { - SUCCEED("Captured expected exit() call due to socket failure"); - } + if (setjmp(exit_jump) == 0) { + make_connection(socket_path.c_str()); + FAIL("make_connection should have called exit() but didn't"); + } else { + SUCCEED("Captured expected exit() call due to socket failure"); + } - REQUIRE(exit_status == 2); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("failed to create unix socket")); + REQUIRE(exit_status == 2); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("failed to create unix socket")); + } } } diff --git a/tests/unit_test_renderd_config.cpp b/tests/unit_test_renderd_config.cpp index ba0a0fe3..0bc89789 100644 --- a/tests/unit_test_renderd_config.cpp +++ b/tests/unit_test_renderd_config.cpp @@ -1,25 +1,9 @@ -#include -#include -#include -#include -#include -#include -#include - -#include "catch/catch.hpp" -#include "catch_test_common.hpp" #include "renderd_config.h" #ifndef RENDERD_CONF #define RENDERD_CONF "./etc/renderd/renderd.conf.examples" #endif -extern int exit_status; -extern bool fail_next_asprintf; -extern bool fail_next_strndup; -extern jmp_buf exit_jump; -extern std::string err_log_lines; - extern "C" { #define asprintf mocked_asprintf #define exit mocked_exit @@ -34,827 +18,830 @@ extern "C" { #undef strndup } -TEST_CASE("copy_string", "[copy_string]") +TEST_CASE("renderd_config.c", "[renderd_config]") { - err_log_lines.clear(); - exit_status = 0; + SECTION("copy_string function") + { + err_log_lines.clear(); + exit_status = 0; - SECTION("copy_string handles strndup failure by exiting", "should exit 7") { - const char *src = "test"; - const char *dest; + SECTION("copy_string handles strndup failure by exiting", "should exit 7") { + const char *src = "test"; + const char *dest; - fail_next_strndup = true; + fail_next_strndup = true; - if (setjmp(exit_jump) == 0) { - copy_string(src, &dest, strlen(src)); - FAIL("copy_string should have called exit() but didn't"); - } else { - SUCCEED("Captured expected exit() call due to strndup failure"); - } + if (setjmp(exit_jump) == 0) { + copy_string(src, &dest, strlen(src)); + FAIL("copy_string should have called exit() but didn't"); + } else { + SUCCEED("Captured expected exit() call due to strndup failure"); + } - REQUIRE(exit_status == 7); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("copy_string: strndup error")); - } + REQUIRE(exit_status == 7); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("copy_string: strndup error")); + } - SECTION("copy_string with valid src and valid dest", "should return") { - const char *src = "test"; - const char *dest = nullptr; + SECTION("copy_string with valid src and valid dest", "should return") { + const char *src = "test"; + const char *dest = nullptr; - REQUIRE(nullptr == dest); - copy_string(src, &dest, strlen(src)); - REQUIRE(strcmp(src, dest) == 0); + REQUIRE(nullptr == dest); + copy_string(src, &dest, strlen(src)); + REQUIRE(strcmp(src, dest) == 0); + } } -} -TEST_CASE("name_with_section", "[name_with_section]") -{ - err_log_lines.clear(); - exit_status = 0; + SECTION("name_with_section function") + { + err_log_lines.clear(); + exit_status = 0; - SECTION("name_with_section with valid name and invalid section", "should exit 7") { - const char *name = "socketname"; - const char *section = nullptr; - char *value = nullptr; + SECTION("name_with_section with valid name and invalid section", "should exit 7") { + const char *name = "socketname"; + const char *section = nullptr; + char *value = nullptr; - if (setjmp(exit_jump) == 0) { - name_with_section(section, name); - FAIL("name_with_section should have called exit() but didn't"); - } else { - SUCCEED("Captured expected exit() call due to asprintf failure"); + if (setjmp(exit_jump) == 0) { + name_with_section(section, name); + FAIL("name_with_section should have called exit() but didn't"); + } else { + SUCCEED("Captured expected exit() call due to asprintf failure"); + } + + REQUIRE(exit_status == 7); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("name_with_section: invalid section (null)")); } - REQUIRE(exit_status == 7); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("name_with_section: invalid section (null)")); - } + SECTION("name_with_section with invalid name and valid section", "should exit 7") { + const char *name = nullptr; + const char *section = "renderd"; + char *value = nullptr; - SECTION("name_with_section with invalid name and valid section", "should exit 7") { - const char *name = nullptr; - const char *section = "renderd"; - char *value = nullptr; + if (setjmp(exit_jump) == 0) { + name_with_section(section, name); + FAIL("name_with_section should have called exit() but didn't"); + } else { + SUCCEED("Captured expected exit() call due to asprintf failure"); + } - if (setjmp(exit_jump) == 0) { - name_with_section(section, name); - FAIL("name_with_section should have called exit() but didn't"); - } else { - SUCCEED("Captured expected exit() call due to asprintf failure"); + REQUIRE(exit_status == 7); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("name_with_section: invalid name (null)")); } - REQUIRE(exit_status == 7); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("name_with_section: invalid name (null)")); - } + SECTION("name_with_section with valid name and valid section", "should return") { + const char *name = "socketname"; + const char *section = "renderd"; + char *value = nullptr; - SECTION("name_with_section with valid name and valid section", "should return") { - const char *name = "socketname"; - const char *section = "renderd"; - char *value = nullptr; + REQUIRE(nullptr == value); + value = name_with_section(section, name); + REQUIRE((std::string(section) + ":" + name) == value); + } - REQUIRE(nullptr == value); - value = name_with_section(section, name); - REQUIRE((std::string(section) + ":" + name) == value); - } + SECTION("name_with_section handles asprintf failure by exiting", "should exit 7") { + const char *section = "renderd"; + const char *name = "socketname"; - SECTION("name_with_section handles asprintf failure by exiting", "should exit 7") { - const char *section = "renderd"; - const char *name = "socketname"; + fail_next_asprintf = true; - fail_next_asprintf = true; + if (setjmp(exit_jump) == 0) { + name_with_section(section, name); + FAIL("name_with_section should have called exit() but didn't"); + } else { + SUCCEED("Captured expected exit() call due to asprintf failure"); + } - if (setjmp(exit_jump) == 0) { - name_with_section(section, name); - FAIL("name_with_section should have called exit() but didn't"); - } else { - SUCCEED("Captured expected exit() call due to asprintf failure"); + REQUIRE(exit_status == 7); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("name_with_section: asprintf error")); } - - REQUIRE(exit_status == 7); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("name_with_section: asprintf error")); } -} -TEST_CASE("min_max_double_opt & min_max_int_opt", "[min_max_double_opt] [min_max_int_opt]") -{ - const char *opt_type_name = "value"; - double dmax = 1.15; - double dmin = 1.10; + SECTION("min_max_double_opt & min_max_int_opt functions") + { + const char *opt_type_name = "value"; + double dmax = 1.15; + double dmin = 1.10; + + err_log_lines.clear(); + exit_status = 0; - err_log_lines.clear(); - exit_status = 0; + SECTION("min_max_double_opt success", "should not exit") { + const char *opt_arg = "1.125"; - SECTION("min_max_double_opt success", "should not exit") { - const char *opt_arg = "1.125"; + if (setjmp(exit_jump) == 0) { + min_max_double_opt(opt_arg, opt_type_name, dmin, dmax); + SUCCEED("min_max_double_opt did not call exit(), as expected"); + } else { + FAIL("min_max_double_opt captured unexpected exit() call"); + } - if (setjmp(exit_jump) == 0) { - min_max_double_opt(opt_arg, opt_type_name, dmin, dmax); - SUCCEED("min_max_double_opt did not call exit(), as expected"); - } else { - FAIL("min_max_double_opt captured unexpected exit() call"); + REQUIRE(exit_status == 0); + REQUIRE(err_log_lines == ""); } - REQUIRE(exit_status == 0); - REQUIRE(err_log_lines == ""); - } + SECTION("min_max_double_opt exceeds max", "should exit 1") { + const char *opt_arg = "2"; - SECTION("min_max_double_opt exceeds max", "should exit 1") { - const char *opt_arg = "2"; + if (setjmp(exit_jump) == 0) { + min_max_double_opt(opt_arg, opt_type_name, dmin, dmax); + FAIL("min_max_double_opt should have called exit() but didn't"); + } else { + SUCCEED("Captured expected exit() call due to asprintf failure"); + } - if (setjmp(exit_jump) == 0) { - min_max_double_opt(opt_arg, opt_type_name, dmin, dmax); - FAIL("min_max_double_opt should have called exit() but didn't"); - } else { - SUCCEED("Captured expected exit() call due to asprintf failure"); + REQUIRE(exit_status == 1); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Invalid " + std::string(opt_type_name) + ", must be <= " + std::to_string(dmax) + " (" + std::string(opt_arg) + " was provided)")); } - REQUIRE(exit_status == 1); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Invalid " + std::string(opt_type_name) + ", must be <= " + std::to_string(dmax) + " (" + std::string(opt_arg) + " was provided)")); - } + SECTION("min_max_double_opt less than min", "should exit 1") { + const char *opt_arg = "1"; - SECTION("min_max_double_opt less than min", "should exit 1") { - const char *opt_arg = "1"; + if (setjmp(exit_jump) == 0) { + min_max_double_opt(opt_arg, opt_type_name, dmin, dmax); + FAIL("min_max_double_opt should have called exit() but didn't"); + } else { + SUCCEED("Captured expected exit() call due to asprintf failure"); + } - if (setjmp(exit_jump) == 0) { - min_max_double_opt(opt_arg, opt_type_name, dmin, dmax); - FAIL("min_max_double_opt should have called exit() but didn't"); - } else { - SUCCEED("Captured expected exit() call due to asprintf failure"); + REQUIRE(exit_status == 1); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Invalid " + std::string(opt_type_name) + ", must be >= " + std::to_string(dmin) + " (" + std::string(opt_arg) + " was provided)")); } - REQUIRE(exit_status == 1); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Invalid " + std::string(opt_type_name) + ", must be >= " + std::to_string(dmin) + " (" + std::string(opt_arg) + " was provided)")); - } + SECTION("min_max_double_opt exceeds max", "should exit 1") { + const char *opt_arg = "fail"; - SECTION("min_max_double_opt exceeds max", "should exit 1") { - const char *opt_arg = "fail"; + if (setjmp(exit_jump) == 0) { + min_max_double_opt(opt_arg, opt_type_name, dmin, dmax); + FAIL("min_max_double_opt should have called exit() but didn't"); + } else { + SUCCEED("Captured expected exit() call due to asprintf failure"); + } - if (setjmp(exit_jump) == 0) { - min_max_double_opt(opt_arg, opt_type_name, dmin, dmax); - FAIL("min_max_double_opt should have called exit() but didn't"); - } else { - SUCCEED("Captured expected exit() call due to asprintf failure"); + REQUIRE(exit_status == 1); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Invalid " + std::string(opt_type_name) + ", must be a double (" + std::string(opt_arg) + " was provided)")); } - REQUIRE(exit_status == 1); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Invalid " + std::string(opt_type_name) + ", must be a double (" + std::string(opt_arg) + " was provided)")); - } + SECTION("min_max_int_opt success", "should not exit") { + const char *opt_arg = "1"; - SECTION("min_max_int_opt success", "should not exit") { - const char *opt_arg = "1"; + if (setjmp(exit_jump) == 0) { + min_max_int_opt(opt_arg, opt_type_name, dmin, dmax); + SUCCEED("min_max_int_opt did not call exit(), as expected"); + } else { + FAIL("min_max_int_opt captured unexpected exit() call"); + } - if (setjmp(exit_jump) == 0) { - min_max_int_opt(opt_arg, opt_type_name, dmin, dmax); - SUCCEED("min_max_int_opt did not call exit(), as expected"); - } else { - FAIL("min_max_int_opt captured unexpected exit() call"); + REQUIRE(exit_status == 0); + REQUIRE(err_log_lines == ""); } - REQUIRE(exit_status == 0); - REQUIRE(err_log_lines == ""); - } + SECTION("min_max_int_opt exceeds max", "should exit 1") { + const char *opt_arg = "2"; - SECTION("min_max_int_opt exceeds max", "should exit 1") { - const char *opt_arg = "2"; + if (setjmp(exit_jump) == 0) { + min_max_int_opt(opt_arg, opt_type_name, dmin, dmax); + FAIL("min_max_int_opt should have called exit() but didn't"); + } else { + SUCCEED("Captured expected exit() call due to asprintf failure"); + } - if (setjmp(exit_jump) == 0) { - min_max_int_opt(opt_arg, opt_type_name, dmin, dmax); - FAIL("min_max_int_opt should have called exit() but didn't"); - } else { - SUCCEED("Captured expected exit() call due to asprintf failure"); + REQUIRE(exit_status == 1); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Invalid " + std::string(opt_type_name) + ", must be <= " + std::to_string((int)dmax) + " (" + std::string(opt_arg) + " was provided)")); } - REQUIRE(exit_status == 1); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Invalid " + std::string(opt_type_name) + ", must be <= " + std::to_string((int)dmax) + " (" + std::string(opt_arg) + " was provided)")); - } + SECTION("min_max_int_opt less than min", "should exit 1") { + const char *opt_arg = "0"; - SECTION("min_max_int_opt less than min", "should exit 1") { - const char *opt_arg = "0"; + if (setjmp(exit_jump) == 0) { + min_max_int_opt(opt_arg, opt_type_name, dmin, dmax); + FAIL("min_max_int_opt should have called exit() but didn't"); + } else { + SUCCEED("Captured expected exit() call due to asprintf failure"); + } - if (setjmp(exit_jump) == 0) { - min_max_int_opt(opt_arg, opt_type_name, dmin, dmax); - FAIL("min_max_int_opt should have called exit() but didn't"); - } else { - SUCCEED("Captured expected exit() call due to asprintf failure"); + REQUIRE(exit_status == 1); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Invalid " + std::string(opt_type_name) + ", must be >= " + std::to_string((int)dmin) + " (" + std::string(opt_arg) + " was provided)")); } - REQUIRE(exit_status == 1); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Invalid " + std::string(opt_type_name) + ", must be >= " + std::to_string((int)dmin) + " (" + std::string(opt_arg) + " was provided)")); - } + SECTION("min_max_int_opt exceeds max", "should exit 1") { + const char *opt_arg = "fail"; - SECTION("min_max_int_opt exceeds max", "should exit 1") { - const char *opt_arg = "fail"; + if (setjmp(exit_jump) == 0) { + min_max_int_opt(opt_arg, opt_type_name, dmin, dmax); + FAIL("min_max_int_opt should have called exit() but didn't"); + } else { + SUCCEED("Captured expected exit() call due to asprintf failure"); + } - if (setjmp(exit_jump) == 0) { - min_max_int_opt(opt_arg, opt_type_name, dmin, dmax); - FAIL("min_max_int_opt should have called exit() but didn't"); - } else { - SUCCEED("Captured expected exit() call due to asprintf failure"); + REQUIRE(exit_status == 1); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Invalid " + std::string(opt_type_name) + ", must be an integer (" + std::string(opt_arg) + " was provided)")); } - - REQUIRE(exit_status == 1); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Invalid " + std::string(opt_type_name) + ", must be an integer (" + std::string(opt_arg) + " was provided)")); } -} -TEST_CASE("renderd.conf file processing", "[process_config_file] [process_renderd_sections] [process_mapnik_section] [process_map_sections]") -{ - err_log_lines.clear(); - exit_status = 0; + SECTION("renderd.conf file processing functions") + { + err_log_lines.clear(); + exit_status = 0; + + SECTION("valid renderd.conf file with invalid active renderd section", "should exit 1") { + if (setjmp(exit_jump) == 0) { + process_config_file(RENDERD_CONF, MAX_SLAVES, 0); + FAIL("process_config_file should have called exit(), but did not"); + } else { + SUCCEED("process_config_file captured expected exit() call"); + } - SECTION("valid renderd.conf file with invalid active renderd section", "should exit 1") { - if (setjmp(exit_jump) == 0) { - process_config_file(RENDERD_CONF, MAX_SLAVES, 0); - FAIL("process_config_file should have called exit(), but did not"); - } else { - SUCCEED("process_config_file captured expected exit() call"); + REQUIRE(exit_status == 1); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Active renderd section (" + std::to_string(MAX_SLAVES) + ") must be between 0 and " + std::to_string(MAX_SLAVES - 1) + ".")); } - REQUIRE(exit_status == 1); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Active renderd section (" + std::to_string(MAX_SLAVES) + ") must be between 0 and " + std::to_string(MAX_SLAVES - 1) + ".")); - } + SECTION("valid renderd.conf file with nonexistent active renderd section", "should exit 1") { + if (setjmp(exit_jump) == 0) { + process_config_file(RENDERD_CONF, (MAX_SLAVES - 1), 0); + FAIL("process_config_file should have called exit(), but did not"); + } else { + SUCCEED("process_config_file captured expected exit() call"); + } - SECTION("valid renderd.conf file with nonexistent active renderd section", "should exit 1") { - if (setjmp(exit_jump) == 0) { - process_config_file(RENDERD_CONF, (MAX_SLAVES - 1), 0); - FAIL("process_config_file should have called exit(), but did not"); - } else { - SUCCEED("process_config_file captured expected exit() call"); + REQUIRE(exit_status == 1); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Active renderd section (" + std::to_string(MAX_SLAVES - 1) + ") does not exist.")); } - REQUIRE(exit_status == 1); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Active renderd section (" + std::to_string(MAX_SLAVES - 1) + ") does not exist.")); - } + SECTION("nonexistent renderd.conf file with valid active renderd section", "should exit 1") { + if (setjmp(exit_jump) == 0) { + process_config_file("doesnotexist", 0, 0); + FAIL("process_config_file should have called exit(), but did not"); + } else { + SUCCEED("process_config_file captured expected exit() call"); + } - SECTION("nonexistent renderd.conf file with valid active renderd section", "should exit 1") { - if (setjmp(exit_jump) == 0) { - process_config_file("doesnotexist", 0, 0); - FAIL("process_config_file should have called exit(), but did not"); - } else { - SUCCEED("process_config_file captured expected exit() call"); + REQUIRE(exit_status == 1); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Failed to load config file (process_config_file): 'doesnotexist'")); } - REQUIRE(exit_status == 1); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Failed to load config file (process_config_file): 'doesnotexist'")); - } + SECTION("nonexistent renderd.conf file with valid active renderd section (process_renderd_sections)", "should exit 1") { + if (setjmp(exit_jump) == 0) { + process_renderd_sections(NULL, "doesnotexist", config_slaves); + FAIL("process_renderd_sections should have called exit(), but did not"); + } else { + SUCCEED("process_renderd_sections captured expected exit() call"); + } - SECTION("nonexistent renderd.conf file with valid active renderd section (process_renderd_sections)", "should exit 1") { - if (setjmp(exit_jump) == 0) { - process_renderd_sections(NULL, "doesnotexist", config_slaves); - FAIL("process_renderd_sections should have called exit(), but did not"); - } else { - SUCCEED("process_renderd_sections captured expected exit() call"); + REQUIRE(exit_status == 1); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Failed to load config file (process_renderd_sections): 'doesnotexist'")); } - REQUIRE(exit_status == 1); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Failed to load config file (process_renderd_sections): 'doesnotexist'")); - } + SECTION("nonexistent renderd.conf file with valid active renderd section (process_mapnik_section)", "should exit 1") { + if (setjmp(exit_jump) == 0) { + process_mapnik_section(NULL, "doesnotexist", config_slaves); + FAIL("process_mapnik_section should have called exit(), but did not"); + } else { + SUCCEED("process_mapnik_section captured expected exit() call"); + } - SECTION("nonexistent renderd.conf file with valid active renderd section (process_mapnik_section)", "should exit 1") { - if (setjmp(exit_jump) == 0) { - process_mapnik_section(NULL, "doesnotexist", config_slaves); - FAIL("process_mapnik_section should have called exit(), but did not"); - } else { - SUCCEED("process_mapnik_section captured expected exit() call"); + REQUIRE(exit_status == 1); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Failed to load config file (process_mapnik_section): 'doesnotexist'")); } - REQUIRE(exit_status == 1); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Failed to load config file (process_mapnik_section): 'doesnotexist'")); - } + SECTION("nonexistent renderd.conf file with valid active renderd section (process_map_sections)", "should exit 1") { + if (setjmp(exit_jump) == 0) { + process_map_sections(NULL, "doesnotexist", maps, "", 0); + FAIL("process_map_sections should have called exit(), but did not"); + } else { + SUCCEED("process_map_sections captured expected exit() call"); + } - SECTION("nonexistent renderd.conf file with valid active renderd section (process_map_sections)", "should exit 1") { - if (setjmp(exit_jump) == 0) { - process_map_sections(NULL, "doesnotexist", maps, "", 0); - FAIL("process_map_sections should have called exit(), but did not"); - } else { - SUCCEED("process_map_sections captured expected exit() call"); + REQUIRE(exit_status == 1); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Failed to load config file (process_map_sections): 'doesnotexist'")); } - REQUIRE(exit_status == 1); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Failed to load config file (process_map_sections): 'doesnotexist'")); - } + SECTION("valid renderd.conf file with valid active renderd section (process_config_file)", "should not exit") { + if (setjmp(exit_jump) == 0) { + process_config_file(RENDERD_CONF, 0, 0); + SUCCEED("process_config_file did not call exit(), as expected"); + } else { + FAIL("process_config_file captured unexpected exit() call"); + } - SECTION("valid renderd.conf file with valid active renderd section (process_config_file)", "should not exit") { - if (setjmp(exit_jump) == 0) { - process_config_file(RENDERD_CONF, 0, 0); - SUCCEED("process_config_file did not call exit(), as expected"); - } else { - FAIL("process_config_file captured unexpected exit() call"); + REQUIRE(exit_status == 0); } - REQUIRE(exit_status == 0); - } + SECTION("valid renderd.conf file with valid active renderd section (process_renderd_sections)", "should not exit") { + if (setjmp(exit_jump) == 0) { + process_renderd_sections(NULL, RENDERD_CONF, config_slaves); + SUCCEED("process_renderd_sections did not call exit(), as expected"); + } else { + FAIL("process_renderd_sections captured unexpected exit() call"); + } - SECTION("valid renderd.conf file with valid active renderd section (process_renderd_sections)", "should not exit") { - if (setjmp(exit_jump) == 0) { - process_renderd_sections(NULL, RENDERD_CONF, config_slaves); - SUCCEED("process_renderd_sections did not call exit(), as expected"); - } else { - FAIL("process_renderd_sections captured unexpected exit() call"); + REQUIRE(exit_status == 0); } - REQUIRE(exit_status == 0); - } + SECTION("valid renderd.conf file with valid active renderd section (process_mapnik_section)", "should not exit") { + if (setjmp(exit_jump) == 0) { + process_mapnik_section(NULL, RENDERD_CONF, config_slaves); + SUCCEED("process_mapnik_section did not call exit(), as expected"); + } else { + FAIL("process_mapnik_section captured unexpected exit() call"); + } - SECTION("valid renderd.conf file with valid active renderd section (process_mapnik_section)", "should not exit") { - if (setjmp(exit_jump) == 0) { - process_mapnik_section(NULL, RENDERD_CONF, config_slaves); - SUCCEED("process_mapnik_section did not call exit(), as expected"); - } else { - FAIL("process_mapnik_section captured unexpected exit() call"); + REQUIRE(exit_status == 0); } - REQUIRE(exit_status == 0); - } + SECTION("valid renderd.conf file with valid active renderd section (process_map_sections)", "should not exit") { + if (setjmp(exit_jump) == 0) { + process_map_sections(NULL, RENDERD_CONF, maps, "", 0); + SUCCEED("process_map_sections did not call exit(), as expected"); + } else { + FAIL("process_map_sections captured unexpected exit() call"); + } - SECTION("valid renderd.conf file with valid active renderd section (process_map_sections)", "should not exit") { - if (setjmp(exit_jump) == 0) { - process_map_sections(NULL, RENDERD_CONF, maps, "", 0); - SUCCEED("process_map_sections did not call exit(), as expected"); - } else { - FAIL("process_map_sections captured unexpected exit() call"); + REQUIRE(exit_status == 0); } - REQUIRE(exit_status == 0); - } + std::string renderd_conf_path = std::string(P_tmpdir) + "/renderd.conf_XXXXXX"; + int fd = mkstemp(&renderd_conf_path[0]); - std::string renderd_conf_path = std::string(P_tmpdir) + "/renderd.conf_XXXXXX"; - int fd = mkstemp(&renderd_conf_path[0]); + if (fd == -1) { + FAIL("mkstemp failed"); + } - if (fd == -1) { - FAIL("mkstemp failed"); - } + close(fd); + std::ofstream renderd_conf_file(renderd_conf_path); - close(fd); - std::ofstream renderd_conf_file(renderd_conf_path); + SECTION("renderd.conf with too many map sections", "should exit 7") { + renderd_conf_file << "[mapnik]\n[renderd]\n"; - SECTION("renderd.conf with too many map sections", "should exit 7") { - renderd_conf_file << "[mapnik]\n[renderd]\n"; + for (int i = 0; i <= XMLCONFIGS_MAX; i++) { + renderd_conf_file << "[map" + std::to_string(i) + "]\n"; + } - for (int i = 0; i <= XMLCONFIGS_MAX; i++) { - renderd_conf_file << "[map" + std::to_string(i) + "]\n"; - } + renderd_conf_file.close(); + + if (setjmp(exit_jump) == 0) { + process_config_file(renderd_conf_path.c_str(), 0, 0); + FAIL("process_config_file should have called exit(), but did not"); + } else { + SUCCEED("process_config_file captured expected exit() call"); + } - renderd_conf_file.close(); + REQUIRE(exit_status == 7); - if (setjmp(exit_jump) == 0) { - process_config_file(renderd_conf_path.c_str(), 0, 0); - FAIL("process_config_file should have called exit(), but did not"); - } else { - SUCCEED("process_config_file captured expected exit() call"); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Can't handle more than " + std::to_string(XMLCONFIGS_MAX) + " map config sections")); } - REQUIRE(exit_status == 7); + SECTION("renderd.conf without map sections", "should exit 1") { + renderd_conf_file << "[mapnik]\n[renderd]\n"; + renderd_conf_file.close(); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Can't handle more than " + std::to_string(XMLCONFIGS_MAX) + " map config sections")); - } + if (setjmp(exit_jump) == 0) { + process_config_file(renderd_conf_path.c_str(), 0, 0); + FAIL("process_config_file should have called exit(), but did not"); + } else { + SUCCEED("process_config_file captured expected exit() call"); + } - SECTION("renderd.conf without map sections", "should exit 1") { - renderd_conf_file << "[mapnik]\n[renderd]\n"; - renderd_conf_file.close(); + REQUIRE(exit_status == 1); - if (setjmp(exit_jump) == 0) { - process_config_file(renderd_conf_path.c_str(), 0, 0); - FAIL("process_config_file should have called exit(), but did not"); - } else { - SUCCEED("process_config_file captured expected exit() call"); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("No map config sections were found in file: " + renderd_conf_path)); } - REQUIRE(exit_status == 1); + SECTION("renderd.conf without mapnik section", "should exit 1") { + renderd_conf_file << "[map]\n[renderd]\n"; + renderd_conf_file.close(); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("No map config sections were found in file: " + renderd_conf_path)); - } + if (setjmp(exit_jump) == 0) { + process_config_file(renderd_conf_path.c_str(), 0, 0); + FAIL("process_config_file should have called exit(), but did not"); + } else { + SUCCEED("process_config_file captured expected exit() call"); + } - SECTION("renderd.conf without mapnik section", "should exit 1") { - renderd_conf_file << "[map]\n[renderd]\n"; - renderd_conf_file.close(); + REQUIRE(exit_status == 1); - if (setjmp(exit_jump) == 0) { - process_config_file(renderd_conf_path.c_str(), 0, 0); - FAIL("process_config_file should have called exit(), but did not"); - } else { - SUCCEED("process_config_file captured expected exit() call"); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("No mapnik config section was found in file: " + renderd_conf_path)); } - REQUIRE(exit_status == 1); + SECTION("renderd.conf with invalid renderd sections", "should exit 7") { + std::string renderd_conf_renderd_section_name = "renderdinvalid"; - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("No mapnik config section was found in file: " + renderd_conf_path)); - } + renderd_conf_file << "[mapnik]\n[map]\n[" + renderd_conf_renderd_section_name + "]\n"; + renderd_conf_file.close(); - SECTION("renderd.conf with invalid renderd sections", "should exit 7") { - std::string renderd_conf_renderd_section_name = "renderdinvalid"; + if (setjmp(exit_jump) == 0) { + process_config_file(renderd_conf_path.c_str(), 0, 0); + FAIL("process_config_file should have called exit(), but did not"); + } else { + SUCCEED("process_config_file captured expected exit() call"); + } - renderd_conf_file << "[mapnik]\n[map]\n[" + renderd_conf_renderd_section_name + "]\n"; - renderd_conf_file.close(); + REQUIRE(exit_status == 7); - if (setjmp(exit_jump) == 0) { - process_config_file(renderd_conf_path.c_str(), 0, 0); - FAIL("process_config_file should have called exit(), but did not"); - } else { - SUCCEED("process_config_file captured expected exit() call"); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Invalid renderd section name: " + renderd_conf_renderd_section_name)); } - REQUIRE(exit_status == 7); + SECTION("renderd.conf with too many renderd sections", "should exit 7") { + renderd_conf_file << "[mapnik]\n[map]\n"; - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Invalid renderd section name: " + renderd_conf_renderd_section_name)); - } + for (int i = 0; i <= MAX_SLAVES; i++) { + renderd_conf_file << "[renderd" + std::to_string(i) + "]\n"; + } - SECTION("renderd.conf with too many renderd sections", "should exit 7") { - renderd_conf_file << "[mapnik]\n[map]\n"; + renderd_conf_file.close(); - for (int i = 0; i <= MAX_SLAVES; i++) { - renderd_conf_file << "[renderd" + std::to_string(i) + "]\n"; - } + if (setjmp(exit_jump) == 0) { + process_config_file(renderd_conf_path.c_str(), 0, 0); + FAIL("process_config_file should have called exit(), but did not"); + } else { + SUCCEED("process_config_file captured expected exit() call"); + } - renderd_conf_file.close(); + REQUIRE(exit_status == 7); - if (setjmp(exit_jump) == 0) { - process_config_file(renderd_conf_path.c_str(), 0, 0); - FAIL("process_config_file should have called exit(), but did not"); - } else { - SUCCEED("process_config_file captured expected exit() call"); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Can't handle more than " + std::to_string(MAX_SLAVES) + " renderd config sections")); } - REQUIRE(exit_status == 7); + SECTION("renderd.conf without renderd sections", "should exit 1") { + renderd_conf_file << "[map]\n[mapnik]\n"; + renderd_conf_file.close(); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Can't handle more than " + std::to_string(MAX_SLAVES) + " renderd config sections")); - } + if (setjmp(exit_jump) == 0) { + process_config_file(renderd_conf_path.c_str(), 0, 0); + FAIL("process_config_file should have called exit(), but did not"); + } else { + SUCCEED("process_config_file captured expected exit() call"); + } - SECTION("renderd.conf without renderd sections", "should exit 1") { - renderd_conf_file << "[map]\n[mapnik]\n"; - renderd_conf_file.close(); + REQUIRE(exit_status == 1); - if (setjmp(exit_jump) == 0) { - process_config_file(renderd_conf_path.c_str(), 0, 0); - FAIL("process_config_file should have called exit(), but did not"); - } else { - SUCCEED("process_config_file captured expected exit() call"); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("No renderd config sections were found in file: " + renderd_conf_path)); } - REQUIRE(exit_status == 1); + SECTION("renderd.conf map section scale too small", "should exit 7") { + renderd_conf_file << "[mapnik]\n[renderd]\n"; + renderd_conf_file << "[map]\nscale=0.0\n"; + renderd_conf_file.close(); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("No renderd config sections were found in file: " + renderd_conf_path)); - } + if (setjmp(exit_jump) == 0) { + process_config_file(renderd_conf_path.c_str(), 0, 0); + FAIL("process_config_file should have called exit(), but did not"); + } else { + SUCCEED("process_config_file captured expected exit() call"); + } - SECTION("renderd.conf map section scale too small", "should exit 7") { - renderd_conf_file << "[mapnik]\n[renderd]\n"; - renderd_conf_file << "[map]\nscale=0.0\n"; - renderd_conf_file.close(); + REQUIRE(exit_status == 7); - if (setjmp(exit_jump) == 0) { - process_config_file(renderd_conf_path.c_str(), 0, 0); - FAIL("process_config_file should have called exit(), but did not"); - } else { - SUCCEED("process_config_file captured expected exit() call"); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Specified scale factor (0.000000) is too small, must be greater than or equal to 0.100000.")); } - REQUIRE(exit_status == 7); + SECTION("renderd.conf map section scale too large", "should exit 7") { + renderd_conf_file << "[mapnik]\n[renderd]\n"; + renderd_conf_file << "[map]\nscale=8.1\n"; + renderd_conf_file.close(); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Specified scale factor (0.000000) is too small, must be greater than or equal to 0.100000.")); - } + if (setjmp(exit_jump) == 0) { + process_config_file(renderd_conf_path.c_str(), 0, 0); + FAIL("process_config_file should have called exit(), but did not"); + } else { + SUCCEED("process_config_file captured expected exit() call"); + } - SECTION("renderd.conf map section scale too large", "should exit 7") { - renderd_conf_file << "[mapnik]\n[renderd]\n"; - renderd_conf_file << "[map]\nscale=8.1\n"; - renderd_conf_file.close(); + REQUIRE(exit_status == 7); - if (setjmp(exit_jump) == 0) { - process_config_file(renderd_conf_path.c_str(), 0, 0); - FAIL("process_config_file should have called exit(), but did not"); - } else { - SUCCEED("process_config_file captured expected exit() call"); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Specified scale factor (8.100000) is too large, must be less than or equal to 8.000000.")); } - REQUIRE(exit_status == 7); + SECTION("renderd.conf map section maxzoom too small", "should exit 7") { + renderd_conf_file << "[mapnik]\n[renderd]\n"; + renderd_conf_file << "[map]\nmaxzoom=-1\n"; + renderd_conf_file.close(); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Specified scale factor (8.100000) is too large, must be less than or equal to 8.000000.")); - } + if (setjmp(exit_jump) == 0) { + process_config_file(renderd_conf_path.c_str(), 0, 0); + FAIL("process_config_file should have called exit(), but did not"); + } else { + SUCCEED("process_config_file captured expected exit() call"); + } - SECTION("renderd.conf map section maxzoom too small", "should exit 7") { - renderd_conf_file << "[mapnik]\n[renderd]\n"; - renderd_conf_file << "[map]\nmaxzoom=-1\n"; - renderd_conf_file.close(); + REQUIRE(exit_status == 7); - if (setjmp(exit_jump) == 0) { - process_config_file(renderd_conf_path.c_str(), 0, 0); - FAIL("process_config_file should have called exit(), but did not"); - } else { - SUCCEED("process_config_file captured expected exit() call"); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Specified max zoom (-1) is too small, must be greater than or equal to 0.")); } - REQUIRE(exit_status == 7); + SECTION("renderd.conf map section maxzoom too large", "should exit 7") { + renderd_conf_file << "[mapnik]\n[renderd]\n"; + renderd_conf_file << "[map]\nmaxzoom=" << MAX_ZOOM + 1 << "\n"; + renderd_conf_file.close(); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Specified max zoom (-1) is too small, must be greater than or equal to 0.")); - } + if (setjmp(exit_jump) == 0) { + process_config_file(renderd_conf_path.c_str(), 0, 0); + FAIL("process_config_file should have called exit(), but did not"); + } else { + SUCCEED("process_config_file captured expected exit() call"); + } - SECTION("renderd.conf map section maxzoom too large", "should exit 7") { - renderd_conf_file << "[mapnik]\n[renderd]\n"; - renderd_conf_file << "[map]\nmaxzoom=" << MAX_ZOOM + 1 << "\n"; - renderd_conf_file.close(); + REQUIRE(exit_status == 7); - if (setjmp(exit_jump) == 0) { - process_config_file(renderd_conf_path.c_str(), 0, 0); - FAIL("process_config_file should have called exit(), but did not"); - } else { - SUCCEED("process_config_file captured expected exit() call"); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Specified max zoom (" + std::to_string(MAX_ZOOM + 1) + ") is too large, must be less than or equal to " + std::to_string(MAX_ZOOM) + ".")); } - REQUIRE(exit_status == 7); + SECTION("renderd.conf map section minzoom too small", "should exit 7") { + renderd_conf_file << "[mapnik]\n[renderd]\n"; + renderd_conf_file << "[map]\nminzoom=-1\n"; + renderd_conf_file.close(); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Specified max zoom (" + std::to_string(MAX_ZOOM + 1) + ") is too large, must be less than or equal to " + std::to_string(MAX_ZOOM) + ".")); - } + if (setjmp(exit_jump) == 0) { + process_config_file(renderd_conf_path.c_str(), 0, 0); + FAIL("process_config_file should have called exit(), but did not"); + } else { + SUCCEED("process_config_file captured expected exit() call"); + } - SECTION("renderd.conf map section minzoom too small", "should exit 7") { - renderd_conf_file << "[mapnik]\n[renderd]\n"; - renderd_conf_file << "[map]\nminzoom=-1\n"; - renderd_conf_file.close(); + REQUIRE(exit_status == 7); - if (setjmp(exit_jump) == 0) { - process_config_file(renderd_conf_path.c_str(), 0, 0); - FAIL("process_config_file should have called exit(), but did not"); - } else { - SUCCEED("process_config_file captured expected exit() call"); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Specified min zoom (-1) is too small, must be greater than or equal to 0.")); } - REQUIRE(exit_status == 7); + SECTION("renderd.conf map section minzoom too large", "should exit 7") { + renderd_conf_file << "[mapnik]\n[renderd]\n"; + renderd_conf_file << "[map]\nminzoom=" << MAX_ZOOM + 1 << "\n"; + renderd_conf_file.close(); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Specified min zoom (-1) is too small, must be greater than or equal to 0.")); - } + if (setjmp(exit_jump) == 0) { + process_config_file(renderd_conf_path.c_str(), 0, 0); + FAIL("process_config_file should have called exit(), but did not"); + } else { + SUCCEED("process_config_file captured expected exit() call"); + } - SECTION("renderd.conf map section minzoom too large", "should exit 7") { - renderd_conf_file << "[mapnik]\n[renderd]\n"; - renderd_conf_file << "[map]\nminzoom=" << MAX_ZOOM + 1 << "\n"; - renderd_conf_file.close(); + REQUIRE(exit_status == 7); - if (setjmp(exit_jump) == 0) { - process_config_file(renderd_conf_path.c_str(), 0, 0); - FAIL("process_config_file should have called exit(), but did not"); - } else { - SUCCEED("process_config_file captured expected exit() call"); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Specified min zoom (" + std::to_string(MAX_ZOOM + 1) + ") is larger than max zoom (" + std::to_string(MAX_ZOOM) + ").")); } - REQUIRE(exit_status == 7); + SECTION("renderd.conf map section type has too few parts", "should exit 7") { + std::string renderd_conf_map_type = "a"; - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Specified min zoom (" + std::to_string(MAX_ZOOM + 1) + ") is larger than max zoom (" + std::to_string(MAX_ZOOM) + ").")); - } + renderd_conf_file << "[mapnik]\n[renderd]\n"; + renderd_conf_file << "[map]\ntype=" + renderd_conf_map_type + "\n"; + renderd_conf_file.close(); - SECTION("renderd.conf map section type has too few parts", "should exit 7") { - std::string renderd_conf_map_type = "a"; + if (setjmp(exit_jump) == 0) { + process_config_file(renderd_conf_path.c_str(), 0, 0); + FAIL("process_config_file should have called exit(), but did not"); + } else { + SUCCEED("process_config_file captured expected exit() call"); + } - renderd_conf_file << "[mapnik]\n[renderd]\n"; - renderd_conf_file << "[map]\ntype=" + renderd_conf_map_type + "\n"; - renderd_conf_file.close(); + REQUIRE(exit_status == 7); - if (setjmp(exit_jump) == 0) { - process_config_file(renderd_conf_path.c_str(), 0, 0); - FAIL("process_config_file should have called exit(), but did not"); - } else { - SUCCEED("process_config_file captured expected exit() call"); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Specified type (" + renderd_conf_map_type + ") has too few parts, there must be at least 2, e.g., 'png image/png'.")); } - REQUIRE(exit_status == 7); + SECTION("renderd.conf map section type has too many parts", "should exit 7") { + std::string renderd_conf_map_type = "a b c d"; - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Specified type (" + renderd_conf_map_type + ") has too few parts, there must be at least 2, e.g., 'png image/png'.")); - } + renderd_conf_file << "[mapnik]\n[renderd]\n"; + renderd_conf_file << "[map]\ntype=" + renderd_conf_map_type + "\n"; + renderd_conf_file.close(); - SECTION("renderd.conf map section type has too many parts", "should exit 7") { - std::string renderd_conf_map_type = "a b c d"; + if (setjmp(exit_jump) == 0) { + process_config_file(renderd_conf_path.c_str(), 0, 0); + FAIL("process_config_file should have called exit(), but did not"); + } else { + SUCCEED("process_config_file captured expected exit() call"); + } - renderd_conf_file << "[mapnik]\n[renderd]\n"; - renderd_conf_file << "[map]\ntype=" + renderd_conf_map_type + "\n"; - renderd_conf_file.close(); + REQUIRE(exit_status == 7); - if (setjmp(exit_jump) == 0) { - process_config_file(renderd_conf_path.c_str(), 0, 0); - FAIL("process_config_file should have called exit(), but did not"); - } else { - SUCCEED("process_config_file captured expected exit() call"); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Specified type (" + renderd_conf_map_type + ") has too many parts, there must be no more than 3, e.g., 'png image/png png256'.")); } - REQUIRE(exit_status == 7); + SECTION("renderd.conf map section type has two parts", "should not exit") { + renderd_conf_file << "[mapnik]\n[map]\ntype=png image/png\n"; + renderd_conf_file << "[renderd]\n"; + renderd_conf_file.close(); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Specified type (" + renderd_conf_map_type + ") has too many parts, there must be no more than 3, e.g., 'png image/png png256'.")); - } - - SECTION("renderd.conf map section type has two parts", "should not exit") { - renderd_conf_file << "[mapnik]\n[map]\ntype=png image/png\n"; - renderd_conf_file << "[renderd]\n"; - renderd_conf_file.close(); + if (setjmp(exit_jump) == 0) { + process_config_file(renderd_conf_path.c_str(), 0, 0); + SUCCEED("process_config_file did not call exit(), as expected"); + } else { + FAIL("process_config_file captured unexpected exit() call"); + } - if (setjmp(exit_jump) == 0) { - process_config_file(renderd_conf_path.c_str(), 0, 0); - SUCCEED("process_config_file did not call exit(), as expected"); - } else { - FAIL("process_config_file captured unexpected exit() call"); + REQUIRE(exit_status == 0); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Read map:type: 'png image/png'")); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Read map:type:file_extension: 'png'")); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Read map:type:mime_type: 'image/png'")); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Read map:type:output_format: 'png256'")); } - REQUIRE(exit_status == 0); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Read map:type: 'png image/png'")); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Read map:type:file_extension: 'png'")); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Read map:type:mime_type: 'image/png'")); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Read map:type:output_format: 'png256'")); - } + SECTION("renderd.conf renderd section socketname is too long", "should exit 7") { + int renderd_socketname_maxlen = sizeof(((struct sockaddr_un *)0)->sun_path); + std::string renderd_socketname = "/" + std::string(renderd_socketname_maxlen, 'A'); - SECTION("renderd.conf renderd section socketname is too long", "should exit 7") { - int renderd_socketname_maxlen = sizeof(((struct sockaddr_un *)0)->sun_path); - std::string renderd_socketname = "/" + std::string(renderd_socketname_maxlen, 'A'); + renderd_conf_file << "[mapnik]\n[map]\n"; + renderd_conf_file << "[renderd]\nsocketname=" << renderd_socketname << "\n"; + renderd_conf_file.close(); - renderd_conf_file << "[mapnik]\n[map]\n"; - renderd_conf_file << "[renderd]\nsocketname=" << renderd_socketname << "\n"; - renderd_conf_file.close(); + if (setjmp(exit_jump) == 0) { + process_config_file(renderd_conf_path.c_str(), 0, 0); + FAIL("process_config_file should have called exit(), but did not"); + } else { + SUCCEED("process_config_file captured expected exit() call"); + } - if (setjmp(exit_jump) == 0) { - process_config_file(renderd_conf_path.c_str(), 0, 0); - FAIL("process_config_file should have called exit(), but did not"); - } else { - SUCCEED("process_config_file captured expected exit() call"); + REQUIRE(exit_status == 7); + + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Specified socketname (" + renderd_socketname + ") exceeds maximum allowed length of " + std::to_string(renderd_socketname_maxlen) + ".")); } - REQUIRE(exit_status == 7); + SECTION("renderd.conf duplicate renderd section names", "should exit 7") { + renderd_conf_file << "[mapnik]\n[map]\n"; + renderd_conf_file << "[renderd0]\n[renderd]\n"; + renderd_conf_file.close(); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Specified socketname (" + renderd_socketname + ") exceeds maximum allowed length of " + std::to_string(renderd_socketname_maxlen) + ".")); - } + if (setjmp(exit_jump) == 0) { + process_config_file(renderd_conf_path.c_str(), 0, 0); + FAIL("process_config_file should have called exit(), but did not"); + } else { + SUCCEED("process_config_file captured expected exit() call"); + } - SECTION("renderd.conf duplicate renderd section names", "should exit 7") { - renderd_conf_file << "[mapnik]\n[map]\n"; - renderd_conf_file << "[renderd0]\n[renderd]\n"; - renderd_conf_file.close(); + REQUIRE(exit_status == 7); - if (setjmp(exit_jump) == 0) { - process_config_file(renderd_conf_path.c_str(), 0, 0); - FAIL("process_config_file should have called exit(), but did not"); - } else { - SUCCEED("process_config_file captured expected exit() call"); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Duplicate renderd config section names for section 0: renderd0 & renderd")); } - REQUIRE(exit_status == 7); + SECTION("renderd.conf renderd section num_threads is -1", "should not exit") { + renderd_conf_file << "[mapnik]\n[map]\n"; + renderd_conf_file << "[renderd0]\nnum_threads=-1\n"; + renderd_conf_file.close(); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Duplicate renderd config section names for section 0: renderd0 & renderd")); - } - - SECTION("renderd.conf renderd section num_threads is -1", "should not exit") { - renderd_conf_file << "[mapnik]\n[map]\n"; - renderd_conf_file << "[renderd0]\nnum_threads=-1\n"; - renderd_conf_file.close(); + if (setjmp(exit_jump) == 0) { + process_config_file(renderd_conf_path.c_str(), 0, 0); + SUCCEED("process_config_file did not call exit(), as expected"); + } else { + FAIL("process_config_file captured unexpected exit() call"); + } - if (setjmp(exit_jump) == 0) { - process_config_file(renderd_conf_path.c_str(), 0, 0); - SUCCEED("process_config_file did not call exit(), as expected"); - } else { - FAIL("process_config_file captured unexpected exit() call"); + REQUIRE(exit_status == 0); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("renderd(0): num_threads = '" + std::to_string(sysconf(_SC_NPROCESSORS_ONLN)) + "'")); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("renderd: num_threads = '" + std::to_string(sysconf(_SC_NPROCESSORS_ONLN)) + "'")); } - REQUIRE(exit_status == 0); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("renderd(0): num_threads = '" + std::to_string(sysconf(_SC_NPROCESSORS_ONLN)) + "'")); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("renderd: num_threads = '" + std::to_string(sysconf(_SC_NPROCESSORS_ONLN)) + "'")); - } + SECTION("renderd.conf slave renderd sections' num_threads sum equals num_slave_threads", "should not exit") { + renderd_conf_file << "[mapnik]\n[map]\n"; + renderd_conf_file << "[renderd0]\nnum_threads=-1\n[renderd1]\nnum_threads=2\n[renderd2]\nnum_threads=2\n"; + renderd_conf_file.close(); - SECTION("renderd.conf slave renderd sections' num_threads sum equals num_slave_threads", "should not exit") { - renderd_conf_file << "[mapnik]\n[map]\n"; - renderd_conf_file << "[renderd0]\nnum_threads=-1\n[renderd1]\nnum_threads=2\n[renderd2]\nnum_threads=2\n"; - renderd_conf_file.close(); + if (setjmp(exit_jump) == 0) { + process_config_file(renderd_conf_path.c_str(), 0, 0); + SUCCEED("process_config_file did not call exit(), as expected"); + } else { + FAIL("process_config_file captured unexpected exit() call"); + } - if (setjmp(exit_jump) == 0) { - process_config_file(renderd_conf_path.c_str(), 0, 0); - SUCCEED("process_config_file did not call exit(), as expected"); - } else { - FAIL("process_config_file captured unexpected exit() call"); + REQUIRE(exit_status == 0); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("renderd(1): num_threads = '2'")); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("renderd(2): num_threads = '2'")); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("renderd: num_slave_threads = '4'")); } - REQUIRE(exit_status == 0); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("renderd(1): num_threads = '2'")); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("renderd(2): num_threads = '2'")); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("renderd: num_slave_threads = '4'")); - } + SECTION("renderd.conf renderd section not using unix socketname", "should not exit") { + renderd_conf_file << "[mapnik]\n[map]\n"; + renderd_conf_file << "[renderd0]\niphostname=hostname\nipport=9999\n"; + renderd_conf_file.close(); - SECTION("renderd.conf renderd section not using unix socketname", "should not exit") { - renderd_conf_file << "[mapnik]\n[map]\n"; - renderd_conf_file << "[renderd0]\niphostname=hostname\nipport=9999\n"; - renderd_conf_file.close(); + if (setjmp(exit_jump) == 0) { + process_config_file(renderd_conf_path.c_str(), 0, 0); + SUCCEED("process_config_file did not call exit(), as expected"); + } else { + FAIL("process_config_file captured unexpected exit() call"); + } - if (setjmp(exit_jump) == 0) { - process_config_file(renderd_conf_path.c_str(), 0, 0); - SUCCEED("process_config_file did not call exit(), as expected"); - } else { - FAIL("process_config_file captured unexpected exit() call"); + REQUIRE(exit_status == 0); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("renderd(0): ip socket = 'hostname:9999'")); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("renderd: ip socket = 'hostname:9999'")); } - REQUIRE(exit_status == 0); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("renderd(0): ip socket = 'hostname:9999'")); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("renderd: ip socket = 'hostname:9999'")); + std::remove(renderd_conf_path.c_str()); } - std::remove(renderd_conf_path.c_str()); -} + SECTION("process_config_bool, process_config_double, process_config_int & process_config_string functions") + { + dictionary *ini = iniparser_load(RENDERD_CONF); + std::string section = "section"; + std::string name = "name"; -TEST_CASE("process_config_bool, process_config_double, process_config_int & process_config_string", "[process_config_bool] [process_config_double] [process_config_int] [process_config_string]") -{ - dictionary *ini = iniparser_load(RENDERD_CONF); - std::string section = "section"; - std::string name = "name"; + err_log_lines.clear(); - err_log_lines.clear(); + SECTION("process_config_bool", "should not exit") { + bool notfound = true; - SECTION("process_config_bool", "should not exit") { - bool notfound = true; + if (setjmp(exit_jump) == 0) { + process_config_bool(ini, section.c_str(), name.c_str(), &maps[0].num_threads, notfound); + SUCCEED("process_config_bool did not call exit(), as expected"); + } else { + FAIL("process_config_bool captured unexpected exit() call"); + } - if (setjmp(exit_jump) == 0) { - process_config_bool(ini, section.c_str(), name.c_str(), &maps[0].num_threads, notfound); - SUCCEED("process_config_bool did not call exit(), as expected"); - } else { - FAIL("process_config_bool captured unexpected exit() call"); + REQUIRE(exit_status == 0); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Read " + section + ":" + name + ": '" + (notfound ? "true" : "false") + "'")); } - REQUIRE(exit_status == 0); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Read " + section + ":" + name + ": '" + (notfound ? "true" : "false") + "'")); - } + SECTION("process_config_double", "should not exit") { + double notfound = 123.456; - SECTION("process_config_double", "should not exit") { - double notfound = 123.456; + if (setjmp(exit_jump) == 0) { + process_config_double(ini, section.c_str(), name.c_str(), &maps[0].scale_factor, notfound); + SUCCEED("process_config_double did not call exit(), as expected"); + } else { + FAIL("process_config_double captured unexpected exit() call"); + } - if (setjmp(exit_jump) == 0) { - process_config_double(ini, section.c_str(), name.c_str(), &maps[0].scale_factor, notfound); - SUCCEED("process_config_double did not call exit(), as expected"); - } else { - FAIL("process_config_double captured unexpected exit() call"); + REQUIRE(exit_status == 0); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Read " + section + ":" + name + ": '" + std::to_string(notfound) + "'")); } - REQUIRE(exit_status == 0); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Read " + section + ":" + name + ": '" + std::to_string(notfound) + "'")); - } + SECTION("process_config_int", "should not exit") { + int notfound = 123456; - SECTION("process_config_int", "should not exit") { - int notfound = 123456; + if (setjmp(exit_jump) == 0) { + process_config_int(ini, section.c_str(), name.c_str(), &maps[0].num_threads, notfound); + SUCCEED("process_config_int did not call exit(), as expected"); + } else { + FAIL("process_config_int captured unexpected exit() call"); + } - if (setjmp(exit_jump) == 0) { - process_config_int(ini, section.c_str(), name.c_str(), &maps[0].num_threads, notfound); - SUCCEED("process_config_int did not call exit(), as expected"); - } else { - FAIL("process_config_int captured unexpected exit() call"); + REQUIRE(exit_status == 0); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Read " + section + ":" + name + ": '" + std::to_string(notfound) + "'")); } - REQUIRE(exit_status == 0); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Read " + section + ":" + name + ": '" + std::to_string(notfound) + "'")); - } + SECTION("process_config_string", "should not exit") { + std::string notfound = "notfound"; - SECTION("process_config_string", "should not exit") { - std::string notfound = "notfound"; + if (setjmp(exit_jump) == 0) { + process_config_string(ini, section.c_str(), name.c_str(), &maps[0].xmlname, notfound.c_str(), notfound.length()); + SUCCEED("process_config_string did not call exit(), as expected"); + } else { + FAIL("process_config_string captured unexpected exit() call"); + } - if (setjmp(exit_jump) == 0) { - process_config_string(ini, section.c_str(), name.c_str(), &maps[0].xmlname, notfound.c_str(), notfound.length()); - SUCCEED("process_config_string did not call exit(), as expected"); - } else { - FAIL("process_config_string captured unexpected exit() call"); + REQUIRE(exit_status == 0); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Read " + section + ":" + name + ": '" + notfound + "'")); } - REQUIRE(exit_status == 0); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Read " + section + ":" + name + ": '" + notfound + "'")); + iniparser_freedict(ini); } - iniparser_freedict(ini); -} + SECTION("free_map_section, free_map_sections, free_renderd_section & free_renderd_sections functions") + { + int active_renderd_section_num = 0; + int map_section_num = 0; -TEST_CASE("free_map_section, free_map_sections, free_renderd_section & free_renderd_sections", "[free_map_section] [free_map_sections] [free_renderd_section] [free_renderd_sections]") -{ - int active_renderd_section_num = 0; - int map_section_num = 0; - - process_config_file(RENDERD_CONF, active_renderd_section_num, 0); - REQUIRE(config == &config_slaves[active_renderd_section_num]); - REQUIRE(config->name == std::string("renderd")); - REQUIRE(config_slaves[active_renderd_section_num].name == std::string("renderd")); - REQUIRE(maps[map_section_num].xmlname == std::string("example-map")); - - err_log_lines.clear(); - - SECTION("free_map_section", "should not exit") { - free_map_section(&maps[map_section_num]); - REQUIRE(maps[map_section_num].xmlname == nullptr); - free_map_section(&maps[map_section_num]); - REQUIRE(maps[map_section_num].xmlname == nullptr); - } + process_config_file(RENDERD_CONF, active_renderd_section_num, 0); + REQUIRE(config == &config_slaves[active_renderd_section_num]); + REQUIRE(config->name == std::string("renderd")); + REQUIRE(config_slaves[active_renderd_section_num].name == std::string("renderd")); + REQUIRE(maps[map_section_num].xmlname == std::string("example-map")); - SECTION("free_map_sections", "should not exit") { - free_map_sections(maps); - REQUIRE(maps[map_section_num].xmlname == nullptr); - free_map_sections(maps); - REQUIRE(maps[map_section_num].xmlname == nullptr); - } + err_log_lines.clear(); - SECTION("free_renderd_section", "should not exit") { - free_renderd_section(&config_slaves[active_renderd_section_num]); - REQUIRE(config->name == nullptr); - REQUIRE(config_slaves[active_renderd_section_num].name == nullptr); - free_renderd_section(&config_slaves[active_renderd_section_num]); - REQUIRE(config->name == nullptr); - REQUIRE(config_slaves[active_renderd_section_num].name == nullptr); - } + SECTION("free_map_section", "should not exit") { + free_map_section(&maps[map_section_num]); + REQUIRE(maps[map_section_num].xmlname == nullptr); + free_map_section(&maps[map_section_num]); + REQUIRE(maps[map_section_num].xmlname == nullptr); + } - SECTION("free_renderd_sections", "should not exit") { - free_renderd_sections(config_slaves); - REQUIRE(config->name == nullptr); - REQUIRE(config_slaves[active_renderd_section_num].name == nullptr); - free_renderd_sections(config_slaves); - REQUIRE(config->name == nullptr); - REQUIRE(config_slaves[active_renderd_section_num].name == nullptr); + SECTION("free_map_sections", "should not exit") { + free_map_sections(maps); + REQUIRE(maps[map_section_num].xmlname == nullptr); + free_map_sections(maps); + REQUIRE(maps[map_section_num].xmlname == nullptr); + } + + SECTION("free_renderd_section", "should not exit") { + free_renderd_section(&config_slaves[active_renderd_section_num]); + REQUIRE(config->name == nullptr); + REQUIRE(config_slaves[active_renderd_section_num].name == nullptr); + free_renderd_section(&config_slaves[active_renderd_section_num]); + REQUIRE(config->name == nullptr); + REQUIRE(config_slaves[active_renderd_section_num].name == nullptr); + } + + SECTION("free_renderd_sections", "should not exit") { + free_renderd_sections(config_slaves); + REQUIRE(config->name == nullptr); + REQUIRE(config_slaves[active_renderd_section_num].name == nullptr); + free_renderd_sections(config_slaves); + REQUIRE(config->name == nullptr); + REQUIRE(config_slaves[active_renderd_section_num].name == nullptr); + } } } diff --git a/tests/unit_test_sys_utils.cpp b/tests/unit_test_sys_utils.cpp index b92cc354..4cff179b 100644 --- a/tests/unit_test_sys_utils.cpp +++ b/tests/unit_test_sys_utils.cpp @@ -1,44 +1,28 @@ -#include -#include -#include -#include - #include "catch/catch.hpp" #include "catch_test_common.hpp" -#include "sys_utils.h" - -extern bool fail_next_getloadavg; -extern std::string err_log_lines; - -extern "C" { -#define g_logger mocked_g_logger -#define getloadavg mocked_getloadavg - -#include "sys_utils.c" -#undef g_logger -#undef getloadavg -} - -TEST_CASE("get_load_avg function", "[get_load_avg]") +TEST_CASE("sys_utils.c", "[sys_utils]") { - err_log_lines.clear(); + SECTION("get_load_avg function") + { + err_log_lines.clear(); - SECTION("get_load_avg", "should return positive") { - double loadavg = get_load_avg(); + SECTION("get_load_avg", "should return positive") { + double loadavg = get_load_avg(); - REQUIRE(loadavg > 0); - } + REQUIRE(loadavg > 0); + } - SECTION("get_load_avg with unobtainable load average", "should return 1000") { - fail_next_getloadavg = true; + SECTION("get_load_avg with unobtainable load average", "should return 1000") { + fail_next_getloadavg = true; - double loadavg = get_load_avg(); + double loadavg = get_load_avg(); #ifdef HAVE_GETLOADAVG - REQUIRE(loadavg == 1000); + REQUIRE(loadavg == 1000); #else - REQUIRE(loadavg > 0); + REQUIRE(loadavg > 0); #endif + } } } diff --git a/tests/unit_tests.cpp b/tests/unit_tests.cpp new file mode 100644 index 00000000..862042f1 --- /dev/null +++ b/tests/unit_tests.cpp @@ -0,0 +1,148 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#if MAPNIK_MAJOR_VERSION < 4 +#include +#endif + +#include "catch/catch.hpp" +#include "catch_test_common.hpp" + +#include "config.h" + +#include "metatile.h" +#include "parameterize_style.hpp" +#include "protocol.h" +#include "protocol_helper.h" +#include "render_submit_queue.h" +#include "store_file.h" +#include "sys_utils.h" + +#ifndef MAPNIK_XML +#define MAPNIK_XML "./utils/example-map/mapnik.xml" +#endif + +#ifndef MAPNIK_PLUGINS_DIR +#define MAPNIK_PLUGINS_DIR "/usr/local/lib64/mapnik/input" +#endif + +extern bool fail_next_asprintf; +extern bool fail_next_connect; +extern bool fail_next_getaddrinfo; +extern bool fail_next_getaddrinfo_empty_res; +extern bool fail_next_getloadavg; +extern bool fail_next_malloc; +extern bool fail_next_mkdir; +extern bool fail_next_open; +extern bool fail_next_socket; +extern bool fail_next_strndup; +extern bool fail_next_strtok; +extern bool fail_next_write; +extern int exit_status; + +extern jmp_buf exit_jump; +extern std::string err_log_lines; + +extern "C" { + bool fail_next_recv = false; + int fail_next_recv_reponse_size = -1; + int fail_next_recv_reponse_version = -1; + int fail_next_next_recv_reponse_size = -1; + int fail_next_next_recv_reponse_version = -1; + + struct storage_backend * init_storage_backend(const char * options) + { + struct storage_backend * store = init_storage_file(options); + return store; + } + + ssize_t mocked_recv(int fd, void *buf, size_t n, int flags) + { + if (fail_next_recv) { + fail_next_recv = false; + return -1; + } + + if (fail_next_recv_reponse_size != -1) { + int reponse_size = fail_next_recv_reponse_size; + fail_next_recv_reponse_size = (fail_next_next_recv_reponse_size != -1) ? fail_next_next_recv_reponse_size : -1; + fail_next_next_recv_reponse_size = -1; + + if (fail_next_recv_reponse_version != -1) { + struct protocol *cmd = (struct protocol *)malloc(sizeof(struct protocol)); + cmd->ver = fail_next_recv_reponse_version; + fail_next_recv_reponse_version = (fail_next_next_recv_reponse_version != -1) ? fail_next_next_recv_reponse_version : -1; + fail_next_next_recv_reponse_version = -1; + + memcpy(buf, cmd, reponse_size); + + free(cmd); + } + + return reponse_size; + } + + return recv(fd, buf, n, flags); + } +} + +#define connect mocked_connect +#define exit mocked_exit +#define g_logger mocked_g_logger +#define getaddrinfo mocked_getaddrinfo +#define getloadavg mocked_getloadavg +#define malloc mocked_malloc +#define mkdir mocked_mkdir +#define open mocked_open +#define recv mocked_recv +#define socket mocked_socket +#define strtok mocked_strtok +#define write mocked_write + +extern "C" { +#include "cache_expire.c" +#include "protocol_helper.c" +#include "render_submit_queue.c" +#include "store_file.c" +#include "store_file_utils.c" +#include "sys_utils.c" +} + +#include "metatile.cpp" +#include "parameterize_style.cpp" + +#undef connect +#undef exit +#undef g_logger +#undef getaddrinfo +#undef getloadavg +#undef malloc +#undef mkdir +#undef open +#undef recv +#undef socket +#undef strtok +#undef write + +#include "unit_test_cache_expire.cpp" +#include "unit_test_metatile.cpp" +#include "unit_test_parameterize_style.cpp" +#include "unit_test_protocol_helper.cpp" +#include "unit_test_render_submit_queue.cpp" +#include "unit_test_renderd_config.cpp" +#include "unit_test_sys_utils.cpp" From a1ec0176402fa5538da66553471b4622d6b56289 Mon Sep 17 00:00:00 2001 From: David Hummel <6109326+hummeltech@users.noreply.github.com> Date: Tue, 24 Mar 2026 06:51:24 -0700 Subject: [PATCH 10/10] Organizing --- .github/actions/autotools/build/action.yml | 4 + Makefile.am | 14 +-- docker/debian/Dockerfile.autotools | 2 + docker/ubuntu/Dockerfile.autotools | 2 + includes/render_submit_queue.h | 1 + src/CMakeLists.txt | 2 +- tests/catch_test_common.cpp | 71 +++++++++++---- tests/catch_test_common.hpp | 1 + tests/unit_test_cache_expire.cpp | 42 +++++---- tests/unit_test_metatile.cpp | 21 +++-- tests/unit_test_parameterize_style.cpp | 23 ++++- tests/unit_test_protocol_helper.cpp | 67 ++++++++------ tests/unit_test_render_submit_queue.cpp | 20 +++-- tests/unit_test_renderd_config.cpp | 45 ++++------ tests/unit_test_sys_utils.cpp | 8 +- tests/unit_tests.cpp | 100 ++++----------------- 16 files changed, 218 insertions(+), 205 deletions(-) diff --git a/.github/actions/autotools/build/action.yml b/.github/actions/autotools/build/action.yml index 5ea44c7a..38a591bf 100644 --- a/.github/actions/autotools/build/action.yml +++ b/.github/actions/autotools/build/action.yml @@ -10,6 +10,10 @@ runs: run: ./configure shell: bash --noprofile --norc -euxo pipefail {0} + - name: Override default `MAPNIK_PLUGINS_DIR` + run: echo "#define MAPNIK_PLUGINS_DIR \"$(find /usr -mindepth 1 -type d -name input | grep mapnik)\"" >> includes/config.h + shell: bash --noprofile --norc -euxo pipefail {0} + - name: Run `make` run: make shell: bash --noprofile --norc -euxo pipefail {0} diff --git a/Makefile.am b/Makefile.am index f46e980a..aa31f1c0 100644 --- a/Makefile.am +++ b/Makefile.am @@ -31,7 +31,7 @@ noinst_PROGRAMS = \ render_list_test \ render_old_test \ render_speedtest_test \ - unit_test_renderd_config + unit_tests man_MANS = \ docs/man/renderd.1 \ @@ -129,23 +129,23 @@ render_speedtest_test_SOURCES = \ tests/render_speedtest_test.cpp render_speedtest_test_LDADD = $(GLIB_LIBS) catch_main.o catch_test_common.o -unit_test_renderd_config_SOURCES = \ - tests/unit_test_renderd_config.cpp -unit_test_renderd_config_CXXFLAGS = -Isrc -unit_test_renderd_config_LDADD = $(GLIB_LIBS) $(INIPARSER_LDFLAGS) catch_main.o catch_test_common.o +unit_tests_SOURCES = \ + tests/unit_tests.cpp +unit_tests_CXXFLAGS = -Isrc +unit_tests_LDADD = $(GLIB_LIBS) $(INIPARSER_LDFLAGS) $(MAPNIK_LDFLAGS) $(PTHREAD_CFLAGS) catch_main.o catch_test_common.o CLEANFILES=*.slo mod_tile.la stderr.out src/*.slo src/*.lo src/.libs/* src/*.la COMMA=, -test: gen_tile_test renderd_test render_expired_test render_list_test render_old_test render_speedtest_test unit_test_renderd_config +test: gen_tile_test renderd_test render_expired_test render_list_test render_old_test render_speedtest_test unit_tests ./gen_tile_test ./renderd_test ./render_expired_test ./render_list_test ./render_old_test ./render_speedtest_test - ./unit_test_renderd_config + ./unit_tests all-local: $(APXS) -c $(DEF_LDLIBS) $(AM_CFLAGS) \ diff --git a/docker/debian/Dockerfile.autotools b/docker/debian/Dockerfile.autotools index 116a12f0..39d404d9 100644 --- a/docker/debian/Dockerfile.autotools +++ b/docker/debian/Dockerfile.autotools @@ -46,9 +46,11 @@ RUN --mount=type=cache,sharing=locked,id=debian:${debian_version}-/var/cache/apt ## Build, Test & Install `mod_tile` COPY . /tmp/mod_tile_src WORKDIR /tmp/mod_tile_src +SHELL ["/bin/bash", "-o", "pipefail", "-c"] RUN export DESTDIR=/tmp/mod_tile && \ ./autogen.sh && \ ./configure && \ + echo "#define MAPNIK_PLUGINS_DIR \"$(find /usr -mindepth 1 -type d -name input | grep mapnik)\"" >> includes/config.h && \ make DESTDIR=${DESTDIR} install install-mod_tile RUN make test diff --git a/docker/ubuntu/Dockerfile.autotools b/docker/ubuntu/Dockerfile.autotools index 66d48eac..3453fc7d 100644 --- a/docker/ubuntu/Dockerfile.autotools +++ b/docker/ubuntu/Dockerfile.autotools @@ -45,9 +45,11 @@ RUN --mount=type=cache,sharing=locked,id=ubuntu:${ubuntu_version}-/var/cache/apt ## Build, Test & Install `mod_tile` COPY . /tmp/mod_tile_src WORKDIR /tmp/mod_tile_src +SHELL ["/bin/bash", "-o", "pipefail", "-c"] RUN export DESTDIR=/tmp/mod_tile && \ ./autogen.sh && \ ./configure && \ + echo "#define MAPNIK_PLUGINS_DIR \"$(find /usr -mindepth 1 -type d -name input | grep mapnik)\"" >> includes/config.h && \ make DESTDIR=${DESTDIR} install install-mod_tile RUN make test diff --git a/includes/render_submit_queue.h b/includes/render_submit_queue.h index 71ba24bf..70e22f32 100644 --- a/includes/render_submit_queue.h +++ b/includes/render_submit_queue.h @@ -21,6 +21,7 @@ extern "C" { #endif +int make_connection(const char *spath); void enqueue(const char *xmlname, int x, int y, int z); void spawn_workers(int num, const char *socketpath, int maxLoad); void wait_for_empty_queue(void); diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index c115dcbf..7dcbd67a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -232,7 +232,7 @@ set(unit_tests_SRCS ${PROJECT_SOURCE_DIR}/tests/unit_tests.cpp ) set(unit_tests_LIBS - ${GLIB_LIBRARIES} + ${COMMON_LIBRARIES} ${INIPARSER_LIBRARIES} ${LIBMAPNIK_LIBRARIES} ) diff --git a/tests/catch_test_common.cpp b/tests/catch_test_common.cpp index 57864699..8830cf8b 100644 --- a/tests/catch_test_common.cpp +++ b/tests/catch_test_common.cpp @@ -35,25 +35,10 @@ #include "catch_test_common.hpp" #include "pstreams/pstream.hpp" +#include "protocol.h" + extern int foreground; -int exit_status = 0; -int mock_errno = 0; - -bool fail_next_asprintf = false; -bool fail_next_connect = false; -bool fail_next_getaddrinfo = false; -bool fail_next_getaddrinfo_empty_res = false; -bool fail_next_getloadavg = false; -bool fail_next_malloc = false; -bool fail_next_mkdir = false; -bool fail_next_open = false; -bool fail_next_socket = false; -bool fail_next_strndup = false; -bool fail_next_strtok = false; -bool fail_next_write = false; - -jmp_buf exit_jump; std::string err_log_lines, out_log_lines; captured_stdio captured_stderr; @@ -280,6 +265,29 @@ int delete_tile_dir(const std::string &tile_dir) } extern "C" { + int exit_status = 0; + int mock_errno = 0; + + bool fail_next_asprintf = false; + bool fail_next_connect = false; + bool fail_next_getaddrinfo = false; + bool fail_next_getaddrinfo_empty_res = false; + bool fail_next_getloadavg = false; + bool fail_next_malloc = false; + bool fail_next_mkdir = false; + bool fail_next_open = false; + bool fail_next_recv = false; + bool fail_next_socket = false; + bool fail_next_strndup = false; + bool fail_next_strtok = false; + bool fail_next_write = false; + int fail_next_next_recv_reponse_size = -1; + int fail_next_next_recv_reponse_version = -1; + int fail_next_recv_reponse_size = -1; + int fail_next_recv_reponse_version = -1; + + jmp_buf exit_jump; + void mocked_exit(int status) { exit_status = status; @@ -389,6 +397,35 @@ extern "C" { return result; } + ssize_t mocked_recv(int fd, void *buf, size_t n, int flags) + { + if (fail_next_recv) { + fail_next_recv = false; + return -1; + } + + if (fail_next_recv_reponse_size != -1) { + int reponse_size = fail_next_recv_reponse_size; + fail_next_recv_reponse_size = (fail_next_next_recv_reponse_size != -1) ? fail_next_next_recv_reponse_size : -1; + fail_next_next_recv_reponse_size = -1; + + if (fail_next_recv_reponse_version != -1) { + struct protocol *cmd = (struct protocol *)malloc(sizeof(struct protocol)); + cmd->ver = fail_next_recv_reponse_version; + fail_next_recv_reponse_version = (fail_next_next_recv_reponse_version != -1) ? fail_next_next_recv_reponse_version : -1; + fail_next_next_recv_reponse_version = -1; + + memcpy(buf, cmd, reponse_size); + + free(cmd); + } + + return reponse_size; + } + + return recv(fd, buf, n, flags); + } + int mocked_socket(int domain, int type, int protocol) noexcept { if (fail_next_socket) { diff --git a/tests/catch_test_common.hpp b/tests/catch_test_common.hpp index 26344db7..012a2a09 100644 --- a/tests/catch_test_common.hpp +++ b/tests/catch_test_common.hpp @@ -59,6 +59,7 @@ extern "C" { void *mocked_malloc(size_t size); int mocked_mkdir(const char *path, mode_t mode); int mocked_open(const char *pathname, int flags, ...); + ssize_t mocked_recv(int fd, void *buf, size_t n, int flags); int mocked_socket(int domain, int type, int protocol) noexcept; char *mocked_strndup(const char *s, size_t n); char *mocked_strtok(char *s, const char *delim); diff --git a/tests/unit_test_cache_expire.cpp b/tests/unit_test_cache_expire.cpp index f4a667ed..26b05ffa 100644 --- a/tests/unit_test_cache_expire.cpp +++ b/tests/unit_test_cache_expire.cpp @@ -1,6 +1,15 @@ #include "catch/catch.hpp" #include "catch_test_common.hpp" +#include "cache_expire.h" + +extern bool fail_next_connect; +extern bool fail_next_getaddrinfo; +extern bool fail_next_getaddrinfo_empty_res; +extern bool fail_next_malloc; +extern bool fail_next_socket; +extern std::string err_log_lines; + TEST_CASE("cache_expire.c", "[cache_expire]") { int x = 0; @@ -9,8 +18,7 @@ TEST_CASE("cache_expire.c", "[cache_expire]") std::string host("host"); std::string uri("/uri/"); - SECTION("cache_expire_url function") - { + SECTION("cache_expire_url function") { std::string url("http://" + host + uri + std::to_string(z) + "/" + std::to_string(x) + "/" + std::to_string(y) + ".png"); err_log_lines.clear(); @@ -39,8 +47,7 @@ TEST_CASE("cache_expire.c", "[cache_expire]") } } - SECTION("cache_expire function") - { + SECTION("cache_expire function") { err_log_lines.clear(); SECTION("cache_expire", "should return") { @@ -65,8 +72,7 @@ TEST_CASE("cache_expire.c", "[cache_expire]") } } - SECTION("init_cache_expire functiong") - { + SECTION("init_cache_expire function") { err_log_lines.clear(); SECTION("init_cache_expire", "should return") { @@ -79,14 +85,14 @@ TEST_CASE("cache_expire.c", "[cache_expire]") std::string htcphost("nonexistenthost"); init_cache_expire((char *)htcphost.c_str()); REQUIRE_THAT(err_log_lines, - Catch::Matchers::Contains("Failed to lookup HTCP cache host: Address family for hostname not supported") - || - Catch::Matchers::Contains("Failed to lookup HTCP cache host: Name or service not known") - || - Catch::Matchers::Contains("Failed to lookup HTCP cache host: Temporary failure in name resolution") - || - Catch::Matchers::Contains("Failed to lookup HTCP cache host: nodename nor servname provided, or not known") - ); + Catch::Matchers::Contains("Failed to lookup HTCP cache host: Address family for hostname not supported") + || + Catch::Matchers::Contains("Failed to lookup HTCP cache host: Name or service not known") + || + Catch::Matchers::Contains("Failed to lookup HTCP cache host: Temporary failure in name resolution") + || + Catch::Matchers::Contains("Failed to lookup HTCP cache host: nodename nor servname provided, or not known") + ); } SECTION("init_cache_expire with failed socket", "should return") { @@ -105,10 +111,10 @@ TEST_CASE("cache_expire.c", "[cache_expire]") init_cache_expire((char *)htcphost.c_str()); REQUIRE_THAT(err_log_lines, - Catch::Matchers::Contains("Failed to lookup HTCP cache host: Bad value for ai_flags") - || - Catch::Matchers::Contains("Failed to lookup HTCP cache host: Unknown error") - ); + Catch::Matchers::Contains("Failed to lookup HTCP cache host: Bad value for ai_flags") + || + Catch::Matchers::Contains("Failed to lookup HTCP cache host: Unknown error") + ); } SECTION("init_cache_expire with empty getaddrinfo response", "should return") { diff --git a/tests/unit_test_metatile.cpp b/tests/unit_test_metatile.cpp index 207f8dd5..817ae7c4 100644 --- a/tests/unit_test_metatile.cpp +++ b/tests/unit_test_metatile.cpp @@ -1,6 +1,15 @@ #include "catch/catch.hpp" #include "catch_test_common.hpp" +#include "metatile.h" +#include "store.h" + +extern bool fail_next_malloc; +extern bool fail_next_mkdir; +extern bool fail_next_open; +extern bool fail_next_write; +extern std::string err_log_lines; + TEST_CASE("metatile.cpp", "[metatile]") { int x = 1024; @@ -16,8 +25,7 @@ TEST_CASE("metatile.cpp", "[metatile]") metaTile tiles(xmlconfig.c_str(), "", x, y, z); - SECTION("metaTile::save function") - { + SECTION("metaTile::save function") { tile_dir = create_tile_dir("mod_tile.unit_test_metatile"); store = init_storage_backend(tile_dir.c_str()); @@ -71,8 +79,7 @@ TEST_CASE("metatile.cpp", "[metatile]") } - SECTION("metaTile::set function") - { + SECTION("metaTile::set function") { err_log_lines.clear(); SECTION("metaTile::set", "then metaTile::get, then metaTile::clear, then metaTile::get") { @@ -98,8 +105,7 @@ TEST_CASE("metatile.cpp", "[metatile]") } - SECTION("metaTile::expire_tiles function") - { + SECTION("metaTile::expire_tiles function") { err_log_lines.clear(); SECTION("metaTile::expire_tile", "should return") { @@ -119,8 +125,7 @@ TEST_CASE("metatile.cpp", "[metatile]") } - SECTION("metaTile::xyz_to_meta_offset function") - { + SECTION("metaTile::xyz_to_meta_offset function") { err_log_lines.clear(); SECTION("metaTile::xyz_to_meta_offset", "should return") { diff --git a/tests/unit_test_parameterize_style.cpp b/tests/unit_test_parameterize_style.cpp index 9a5516e1..8d0ed5cc 100644 --- a/tests/unit_test_parameterize_style.cpp +++ b/tests/unit_test_parameterize_style.cpp @@ -1,10 +1,26 @@ +#include +#include +#include +#include +#include +#include + +#if MAPNIK_MAJOR_VERSION < 4 +#include +#endif + #include "catch/catch.hpp" #include "catch_test_common.hpp" +#include "config.h" +#include "parameterize_style.hpp" + +extern bool fail_next_strtok; +extern std::string err_log_lines; + TEST_CASE("parameterize_style.cpp", "[parameterize_style]") { - SECTION("parameterize_map_language function") - { + SECTION("parameterize_map_language function") { const char * parameter = "en,de,_"; mapnik::datasource_cache::instance().register_datasources(MAPNIK_PLUGINS_DIR); mapnik::Map map(256, 256); @@ -39,8 +55,7 @@ TEST_CASE("parameterize_style.cpp", "[parameterize_style]") } } - SECTION("init_parameterization_function function") - { + SECTION("init_parameterization_function function") { err_log_lines.clear(); SECTION("init_parameterization_function with empty function_name", "should return NULL") { diff --git a/tests/unit_test_protocol_helper.cpp b/tests/unit_test_protocol_helper.cpp index 7cccba5a..9e303046 100644 --- a/tests/unit_test_protocol_helper.cpp +++ b/tests/unit_test_protocol_helper.cpp @@ -1,6 +1,17 @@ +#include + #include "catch/catch.hpp" #include "catch_test_common.hpp" +#include "protocol.h" +#include "protocol_helper.h" + +extern int fail_next_next_recv_reponse_size; +extern int fail_next_next_recv_reponse_version; +extern int fail_next_recv_reponse_size; +extern int fail_next_recv_reponse_version; +extern std::string err_log_lines; + TEST_CASE("protocol_helper.c", "[protocol_helper]") { int x = 1024; @@ -20,32 +31,15 @@ TEST_CASE("protocol_helper.c", "[protocol_helper]") cmd->y = y; cmd->z = z; - SECTION("send_cmd function") - { - fd = pipefd[1]; - - SECTION("send_cmd with invalid version", "should return -1") { - cmd->ver = GENERATE(0, 4); - - ret = send_cmd(cmd, fd); - - REQUIRE(ret == -1); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Failed to send render cmd with unknown protocol version " + std::to_string(cmd->ver))); - } - - SECTION("send_cmd with invalid fd", "should return -1") { - cmd->ver = GENERATE(1, 2, 3); + SECTION("recv_cmd function") { + fd = pipefd[0]; - ret = send_cmd(cmd, fd); + SECTION("recv_cmd with invalid fd", "should return -1") { + ret = recv_cmd(&rsp, fd, block); REQUIRE(ret == -1); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Failed to send render cmd on fd " + std::to_string(fd))); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Failed to read cmd on fd " + std::to_string(fd))); } - } - - SECTION("recv_cmd function") - { - fd = pipefd[0]; SECTION("recv_cmd with invalid version", "should return -1") { fail_next_recv_reponse_size = sizeof(struct protocol); @@ -58,13 +52,6 @@ TEST_CASE("protocol_helper.c", "[protocol_helper]") REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains(expected_message)); } - SECTION("recv_cmd with invalid fd", "should return -1") { - ret = recv_cmd(&rsp, fd, block); - - REQUIRE(ret == -1); - REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Failed to read cmd on fd " + std::to_string(fd))); - } - SECTION("recv_cmd with incomplete response", "should return 0") { fail_next_recv_reponse_size = 1; @@ -163,5 +150,27 @@ TEST_CASE("protocol_helper.c", "[protocol_helper]") } } + SECTION("send_cmd function") { + fd = pipefd[1]; + + SECTION("send_cmd with invalid version", "should return -1") { + cmd->ver = GENERATE(0, 4); + + ret = send_cmd(cmd, fd); + + REQUIRE(ret == -1); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Failed to send render cmd with unknown protocol version " + std::to_string(cmd->ver))); + } + + SECTION("send_cmd with invalid fd", "should return -1") { + cmd->ver = GENERATE(1, 2, 3); + + ret = send_cmd(cmd, fd); + + REQUIRE(ret == -1); + REQUIRE_THAT(err_log_lines, Catch::Matchers::Contains("Failed to send render cmd on fd " + std::to_string(fd))); + } + } + free(cmd); } diff --git a/tests/unit_test_render_submit_queue.cpp b/tests/unit_test_render_submit_queue.cpp index 7d1e0615..09345a22 100644 --- a/tests/unit_test_render_submit_queue.cpp +++ b/tests/unit_test_render_submit_queue.cpp @@ -1,10 +1,20 @@ +#include +#include + #include "catch/catch.hpp" #include "catch_test_common.hpp" +#include "render_submit_queue.h" + +extern bool fail_next_getloadavg; +extern bool fail_next_socket; +extern int exit_status; +extern jmp_buf exit_jump; +extern std::string err_log_lines; + TEST_CASE("render_submit_queue.c", "[render_submit_queue]") { - SECTION("check_load function") - { + SECTION("check_load function") { err_log_lines.clear(); maxLoad = 999; @@ -21,8 +31,7 @@ TEST_CASE("render_submit_queue.c", "[render_submit_queue]") } } - SECTION("process function") - { + SECTION("process function") { int fd, ret; int pipefd[2]; pipe(pipefd); @@ -41,8 +50,7 @@ TEST_CASE("render_submit_queue.c", "[render_submit_queue]") } } - SECTION("make_connection function") - { + SECTION("make_connection function") { int ret; std::string socket_path = std::string(P_tmpdir) + "/renderd.sock"; diff --git a/tests/unit_test_renderd_config.cpp b/tests/unit_test_renderd_config.cpp index 0bc89789..5726f4dc 100644 --- a/tests/unit_test_renderd_config.cpp +++ b/tests/unit_test_renderd_config.cpp @@ -1,27 +1,21 @@ -#include "renderd_config.h" - -#ifndef RENDERD_CONF -#define RENDERD_CONF "./etc/renderd/renderd.conf.examples" -#endif +#include +#include +#include -extern "C" { -#define asprintf mocked_asprintf -#define exit mocked_exit -#define g_logger mocked_g_logger -#define strndup mocked_strndup +#include "catch/catch.hpp" +#include "catch_test_common.hpp" -#include "renderd_config.c" +#include "renderd_config.h" -#undef asprintf -#undef exit -#undef g_logger -#undef strndup -} +extern bool fail_next_asprintf; +extern bool fail_next_strndup; +extern int exit_status; +extern jmp_buf exit_jump; +extern std::string err_log_lines; TEST_CASE("renderd_config.c", "[renderd_config]") { - SECTION("copy_string function") - { + SECTION("copy_string function") { err_log_lines.clear(); exit_status = 0; @@ -52,8 +46,7 @@ TEST_CASE("renderd_config.c", "[renderd_config]") } } - SECTION("name_with_section function") - { + SECTION("name_with_section function") { err_log_lines.clear(); exit_status = 0; @@ -119,8 +112,7 @@ TEST_CASE("renderd_config.c", "[renderd_config]") } } - SECTION("min_max_double_opt & min_max_int_opt functions") - { + SECTION("min_max_double_opt & min_max_int_opt functions") { const char *opt_type_name = "value"; double dmax = 1.15; double dmin = 1.10; @@ -242,8 +234,7 @@ TEST_CASE("renderd_config.c", "[renderd_config]") } } - SECTION("renderd.conf file processing functions") - { + SECTION("renderd.conf file processing functions") { err_log_lines.clear(); exit_status = 0; @@ -732,8 +723,7 @@ TEST_CASE("renderd_config.c", "[renderd_config]") std::remove(renderd_conf_path.c_str()); } - SECTION("process_config_bool, process_config_double, process_config_int & process_config_string functions") - { + SECTION("process_config_bool, process_config_double, process_config_int & process_config_string functions") { dictionary *ini = iniparser_load(RENDERD_CONF); std::string section = "section"; std::string name = "name"; @@ -799,8 +789,7 @@ TEST_CASE("renderd_config.c", "[renderd_config]") iniparser_freedict(ini); } - SECTION("free_map_section, free_map_sections, free_renderd_section & free_renderd_sections functions") - { + SECTION("free_map_section, free_map_sections, free_renderd_section & free_renderd_sections functions") { int active_renderd_section_num = 0; int map_section_num = 0; diff --git a/tests/unit_test_sys_utils.cpp b/tests/unit_test_sys_utils.cpp index 4cff179b..1623d0bd 100644 --- a/tests/unit_test_sys_utils.cpp +++ b/tests/unit_test_sys_utils.cpp @@ -1,10 +1,14 @@ #include "catch/catch.hpp" #include "catch_test_common.hpp" +#include "sys_utils.h" + +extern bool fail_next_getloadavg; +extern std::string err_log_lines; + TEST_CASE("sys_utils.c", "[sys_utils]") { - SECTION("get_load_avg function") - { + SECTION("get_load_avg function") { err_log_lines.clear(); SECTION("get_load_avg", "should return positive") { diff --git a/tests/unit_tests.cpp b/tests/unit_tests.cpp index 862042f1..1960b58b 100644 --- a/tests/unit_tests.cpp +++ b/tests/unit_tests.cpp @@ -1,37 +1,12 @@ -#include -#include -#include -#include #include -#include -#include -#include -#include -#include +#include #include -#include - -#include -#include -#include -#include - -#if MAPNIK_MAJOR_VERSION < 4 -#include -#endif #include "catch/catch.hpp" #include "catch_test_common.hpp" #include "config.h" - #include "metatile.h" -#include "parameterize_style.hpp" -#include "protocol.h" -#include "protocol_helper.h" -#include "render_submit_queue.h" -#include "store_file.h" -#include "sys_utils.h" #ifndef MAPNIK_XML #define MAPNIK_XML "./utils/example-map/mapnik.xml" @@ -41,66 +16,11 @@ #define MAPNIK_PLUGINS_DIR "/usr/local/lib64/mapnik/input" #endif -extern bool fail_next_asprintf; -extern bool fail_next_connect; -extern bool fail_next_getaddrinfo; -extern bool fail_next_getaddrinfo_empty_res; -extern bool fail_next_getloadavg; -extern bool fail_next_malloc; -extern bool fail_next_mkdir; -extern bool fail_next_open; -extern bool fail_next_socket; -extern bool fail_next_strndup; -extern bool fail_next_strtok; -extern bool fail_next_write; -extern int exit_status; - -extern jmp_buf exit_jump; -extern std::string err_log_lines; - -extern "C" { - bool fail_next_recv = false; - int fail_next_recv_reponse_size = -1; - int fail_next_recv_reponse_version = -1; - int fail_next_next_recv_reponse_size = -1; - int fail_next_next_recv_reponse_version = -1; - - struct storage_backend * init_storage_backend(const char * options) - { - struct storage_backend * store = init_storage_file(options); - return store; - } - - ssize_t mocked_recv(int fd, void *buf, size_t n, int flags) - { - if (fail_next_recv) { - fail_next_recv = false; - return -1; - } - - if (fail_next_recv_reponse_size != -1) { - int reponse_size = fail_next_recv_reponse_size; - fail_next_recv_reponse_size = (fail_next_next_recv_reponse_size != -1) ? fail_next_next_recv_reponse_size : -1; - fail_next_next_recv_reponse_size = -1; - - if (fail_next_recv_reponse_version != -1) { - struct protocol *cmd = (struct protocol *)malloc(sizeof(struct protocol)); - cmd->ver = fail_next_recv_reponse_version; - fail_next_recv_reponse_version = (fail_next_next_recv_reponse_version != -1) ? fail_next_next_recv_reponse_version : -1; - fail_next_next_recv_reponse_version = -1; - - memcpy(buf, cmd, reponse_size); - - free(cmd); - } - - return reponse_size; - } - - return recv(fd, buf, n, flags); - } -} +#ifndef RENDERD_CONF +#define RENDERD_CONF "./etc/renderd/renderd.conf.examples" +#endif +#define asprintf mocked_asprintf #define connect mocked_connect #define exit mocked_exit #define g_logger mocked_g_logger @@ -111,6 +31,7 @@ extern "C" { #define open mocked_open #define recv mocked_recv #define socket mocked_socket +#define strndup mocked_strndup #define strtok mocked_strtok #define write mocked_write @@ -118,14 +39,22 @@ extern "C" { #include "cache_expire.c" #include "protocol_helper.c" #include "render_submit_queue.c" +#include "renderd_config.c" #include "store_file.c" #include "store_file_utils.c" #include "sys_utils.c" + + struct storage_backend * init_storage_backend(const char * options) + { + struct storage_backend * store = init_storage_file(options); + return store; + } } #include "metatile.cpp" #include "parameterize_style.cpp" +#undef asprintf #undef connect #undef exit #undef g_logger @@ -136,6 +65,7 @@ extern "C" { #undef open #undef recv #undef socket +#undef strndup #undef strtok #undef write