From dd39de140c77ff68b4fd7cfa0a697f6c87b68c51 Mon Sep 17 00:00:00 2001 From: Vaclav Petras Date: Fri, 20 May 2022 11:45:50 -0400 Subject: [PATCH 01/14] r.mask.status: Check mask status through module Instead of using low-level test of file existence with hardcoded raster path and name, use a module to retrieve status of the raster mask. The new module r.mask.status reports presence or absence of the 2D raster mask and provides additional details about the mask. The PR requires additional changes in the library which are not yet included. --- include/grass/defs/raster.h | 1 + lib/init/grass.py | 2 +- raster/r.mask.status/Makefile | 10 ++ raster/r.mask.status/main.c | 168 ++++++++++++++++++++++++ raster/r.mask.status/r.mask.status.html | 35 +++++ 5 files changed, 215 insertions(+), 1 deletion(-) create mode 100644 raster/r.mask.status/Makefile create mode 100644 raster/r.mask.status/main.c create mode 100644 raster/r.mask.status/r.mask.status.html diff --git a/include/grass/defs/raster.h b/include/grass/defs/raster.h index bedf926bbee..899528e13bc 100644 --- a/include/grass/defs/raster.h +++ b/include/grass/defs/raster.h @@ -401,6 +401,7 @@ int Rast_option_to_interp_type(const struct Option *); /* mask_info.c */ char *Rast_mask_info(void); +bool Rast_mask_status(char *, char *, bool *); int Rast__mask_info(char *, char *); /* maskfd.c */ diff --git a/lib/init/grass.py b/lib/init/grass.py index e4308786615..50a23f67167 100755 --- a/lib/init/grass.py +++ b/lib/init/grass.py @@ -1998,8 +1998,8 @@ def sh_like_startup(location, location_name, grass_env_file, sh): ) ) + mask2d_test = 'r.mask.status -t' # TODO: have a function and/or module to test this - mask2d_test = 'test -f "$MAPSET_PATH/cell/MASK"' mask3d_test = 'test -d "$MAPSET_PATH/grid3/RASTER3D_MASK"' specific_addition = "" diff --git a/raster/r.mask.status/Makefile b/raster/r.mask.status/Makefile new file mode 100644 index 00000000000..0be917f1040 --- /dev/null +++ b/raster/r.mask.status/Makefile @@ -0,0 +1,10 @@ +MODULE_TOPDIR = ../.. + +PGM = r.mask.status + +LIBES = $(MANAGELIB) $(RASTERLIB) $(GISLIB) +DEPENDENCIES = $(MANAGEDEP) $(RASTERDEP) $(GISDEP) + +include $(MODULE_TOPDIR)/include/Make/Module.make + +default: cmd diff --git a/raster/r.mask.status/main.c b/raster/r.mask.status/main.c new file mode 100644 index 00000000000..3f3d736bdd6 --- /dev/null +++ b/raster/r.mask.status/main.c @@ -0,0 +1,168 @@ +/**************************************************************************** + * + * MODULE: r.mask.status + * AUTHORS: Vaclav Petras + * PURPOSE: Report status of raster mask + * COPYRIGHT: (C) 2022 by Vaclav Petras and the GRASS Development Team + * + * This program is free software under the GNU General Public + * License (>=v2). Read the file COPYING that comes with GRASS + * for details. + * + *****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include + +struct Parameters +{ + struct Option *format; + struct Flag *like_test; +}; + +void parse_parameters(struct Parameters *params, int argc, char **argv) +{ + struct GModule *module; + + module = G_define_module(); + G_add_keyword(_("raster")); + G_add_keyword(_("reclassification")); + module->label = _("Reclassify raster map based on category values."); + module->description = + _("Creates a new raster map whose category values are based " + "upon a reclassification of the categories in an existing " + "raster map."); + + params->format = G_define_option(); + params->format->key = "format"; + params->format->type = TYPE_STRING; + params->format->required = NO; + params->format->answer = "yaml"; + params->format->options = "yaml,json,bash"; + params->format->description = _("Format for reporting"); + + params->like_test = G_define_flag(); + params->like_test->key = 't'; + params->like_test->lable = + _("Return code 0 when mask present, 1 otherwise"); + params->like_test->description = + _("Behave like the test utility, 0 for true, 1 for false, no output"); + //flags.like_test->guisection = _(""); + // suppress_required + + if (G_parser(argc, argv)) + exit(EXIT_FAILURE); +} + +char *min_json_escape(const char *str) +{ + char *tmp1 = G_str_replace(str, "\\", "\\\\"); + char *tmp2 = G_str_replace(tmp1, "\"", "\\\""); + + G_free(tmp1); + return tmp2; +} + +void json_print_name_mapset(const char *name, const char *mapset) +{ + // Being paranoid about what is in the name. + char *escaped_name = min_json_escape(name); + char *escaped_mapset = min_json_escape(mapset); + + printf("\"%s@%s\"", name, mapset); + G_free(escaped_name); + G_free(escaped_mapset); +} + +int report_status(struct Parameters *params) +{ + + char name[GNAME_MAX]; + char mapset[GMAPSET_MAX]; + + bool is_mask_reclass; + bool present = + Rast_mask_status(name, mapset, reclass_name, reclass_mapset, + &is_mask_reclass); + bool present = Rast_mask_present(name, mapset); + + //printf("%s", Rast_mask_info()); + + if (params->like_test->answer) { + if (present) + return 0; + else + return 1; + } + else if (strcmp(params->format->answer, "json") == 0) { + printf("{\"present\":"); + if (present) + printf("true"); + else + printf("false"); + printf(",\n\"full_name\":"); + if (present) + json_print_name_mapset("MASK", G_mapset()); // Too much mask details here, move this to the library. + else + printf("null"); + printf(",\n\"is_reclass_of\": "); + if (is_mask_reclass) + json_print_name_mapset(name, mapset); + else + printf("null"); + printf("}\n"); + } + else if (strcmp(params->format->answer, "bash") == 0) { + printf("present="); + if (present) + printf("1"); // Good choice here or not? + else + printf("0"); + printf("\nfull_name="); + if (present) { + json_print_name_mapset(name, mapset); + } + printf("\n"); + } + else { + printf("present: "); + if (present) + printf("true"); + else + printf("false"); + printf("\nfull_name: "); + if (present) + printf("|-\n MASK@%s", G_mapset()); // MASK or MASK@current_mapset + else + printf("null"); + printf("\nis_reclass_of: "); + // Using block scalar with |- to avoid need for escaping. + if (is_mask_reclass) + printf("|-\n %s@%s", name, mapset); + else + printf("null"); + // true if MASK in current mapset and is reclass, false otherwise, + // then also outputting mask cats is needed to inform user about the portion of the map + // printf("\nmask_reclass: "); + // if (is_mask_reclass) + // printf("true"); + // else + // printf("false"); + printf("\n"); + } + return EXIT_SUCCESS; +} + +int main(int argc, char **argv) +{ + struct Parameters params; + + G_gisinit(argv[0]); + parse_parameters(¶ms, argc, argv); + return report_status(¶ms); +} diff --git a/raster/r.mask.status/r.mask.status.html b/raster/r.mask.status/r.mask.status.html new file mode 100644 index 00000000000..f8cb8255c5e --- /dev/null +++ b/raster/r.mask.status/r.mask.status.html @@ -0,0 +1,35 @@ +

DESCRIPTION

+ +r.mask.status reports presence or absence of a raster mask. + +

NOTES

+ +

EXAMPLES

+ +

Generate JSON output

+ +
+r.mask.status format=json
+
+ +

Use as the test utility

+ +The POSIX test utility uses return code 0 to indicate true +and 1 to indicate false, so testing existence of a file with test -f +gives return code 0 when the file exists. r.mask.status can be used +in the same with the the -t flag: + +
+r.mask.status -t
+
+ +

SEE ALSO

+ + +r.mask, +g.region + + +

AUTHORS

+ +Vaclav Petras, NC State University, Center for Geospatial Analytics From d88dd0285129742a05615389312dacec03e1f1a3 Mon Sep 17 00:00:00 2001 From: Vaclav Petras Date: Fri, 20 May 2022 15:08:44 -0400 Subject: [PATCH 02/14] Add a new mask info/status function and add doc for the file. --- lib/raster/mask_info.c | 102 ++++++++++++++++++++++++++++++----------- 1 file changed, 76 insertions(+), 26 deletions(-) diff --git a/lib/raster/mask_info.c b/lib/raster/mask_info.c index 792510879dc..51b0a3e0f58 100644 --- a/lib/raster/mask_info.c +++ b/lib/raster/mask_info.c @@ -1,30 +1,17 @@ -/* - ************************************************************* - * char * Rast_mask_info () +/** + * \file lib/raster/mask_info.c * - * returns a printable text of mask information + * \brief Raster Library - Get mask information * - ************************************************************ - * Rast__mask_info (name, mapset) + * (C) 1999-2022 by Vaclav Petras and the GRASS Development Team * - * char name[GNAME_MAX], mapset[GMAPSET_MAX]; + * This program is free software under the GNU General Public + * License (>=v2). Read the file COPYING that comes with GRASS + * for details. * - * function: - * determine the status off the automatic masking - * and the name of the cell file which forms the mask - * - * (the mask file is actually MASK in the current mapset, - * but is usually a reclassed cell file, and the reclass - * name and mapset are returned) - * - * returns: - * -1 no masking (name, mapset undefined) - * name, mapset are undefined - * - * 1 mask file present, masking on - * name, mapset hold mask file name, mapset - * - ***************************************************************/ + * \author CERL + * \author Vaclav Petras, NC State University, Center for Geospatial Analytics + */ #include @@ -32,6 +19,15 @@ #include #include +/** + * @brief Get a printable text with information about raster mask + * + * Determines if 2D raster mask is present and returns textual information about the + * mask suitable for end-user display. The resulting text is translated. + * Caller is responsible for freeing the memory of the returned string. + * + * @return New string with textual information + */ char *Rast_mask_info(void) { char text[GNAME_MAX + GMAPSET_MAX + 16]; @@ -53,16 +49,70 @@ char *Rast_mask_info(void) return G_store(text); } +/** + * @brief Get raster mask status information + * + * @param[out] name Name of the raster map used as mask + * @param[out] mapset Mapset name of the map + * @param[out] is_mask_reclass True, if MASK raster is a reclass + * + * @return true if mask is present, false otherwise + */ +bool Rast_mask_status(char *name, char *mapset, bool *is_mask_reclass) +{ + int present = Rast__mask_info(name, mapset); + + if (is_mask_reclass) { + if (present) { + char rname[GNAME_MAX], rmapset[GMAPSET_MAX]; + + *is_mask_reclass = + Rast_is_reclass("MASK", G_mapset(), rname, rmapset) > 0; + } + else { + *is_mask_reclass = false; + } + } + + if (present == 1) + return true; + else + return false; +} + +/** + * @brief Get information about the current mask + * + * Determines the status of the automatic masking and the name of the 2D + * raster which forms the mask. Typically, mask is raster called MASK in the + * current mapset, but when used with r.mask, it is usually a reclassed + * raster, and so when a MASK raster is present and it is a reclass raster, + * the name and mapset of the underlying reclassed raster are returned. + * + * The name and mapset is written to the parameter which need to be defined + * with a suffient size, least as `char name[GNAME_MAX], mapset[GMAPSET_MAX]`. + * + * When the masking is not active, -1 is returned and name and mapset are + * undefined. When the masking is active, 1 is returned and name and mapset + * will hold the name and mapset of the underlying raster. + * + * @param[out] name Name of the raster map used as mask + * @param[out] mapset Mapset name of the map + * + * @return 1 if mask is present, -1 otherwise + */ int Rast__mask_info(char *name, char *mapset) { char rname[GNAME_MAX], rmapset[GMAPSET_MAX]; - strcpy(name, "MASK"); - strcpy(mapset, G_mapset()); + strcpy(rname, "MASK"); + strcpy(rmapset, G_mapset()); - if (!G_find_raster(name, mapset)) + if (!G_find_raster(rname, rmapset)) return -1; + strcpy(name, rname); + strcpy(mapset, rmapset); if (Rast_is_reclass(name, mapset, rname, rmapset) > 0) { strcpy(name, rname); strcpy(mapset, rmapset); From c92ae7f8df406ccd64580be125bfd53cefcef40d Mon Sep 17 00:00:00 2001 From: Vaclav Petras Date: Fri, 20 May 2022 15:38:25 -0400 Subject: [PATCH 03/14] Provide all information about the possible reclass with the API and the module --- include/grass/defs/raster.h | 2 +- lib/raster/mask_info.c | 9 ++++----- raster/Makefile | 1 + raster/r.mask.status/main.c | 9 +++++---- 4 files changed, 11 insertions(+), 10 deletions(-) diff --git a/include/grass/defs/raster.h b/include/grass/defs/raster.h index 899528e13bc..819ec7a5ff9 100644 --- a/include/grass/defs/raster.h +++ b/include/grass/defs/raster.h @@ -401,7 +401,7 @@ int Rast_option_to_interp_type(const struct Option *); /* mask_info.c */ char *Rast_mask_info(void); -bool Rast_mask_status(char *, char *, bool *); +bool Rast_mask_status(char *, char *, bool *, char *, char *); int Rast__mask_info(char *, char *); /* maskfd.c */ diff --git a/lib/raster/mask_info.c b/lib/raster/mask_info.c index 51b0a3e0f58..77f49ec4b68 100644 --- a/lib/raster/mask_info.c +++ b/lib/raster/mask_info.c @@ -58,16 +58,15 @@ char *Rast_mask_info(void) * * @return true if mask is present, false otherwise */ -bool Rast_mask_status(char *name, char *mapset, bool *is_mask_reclass) +bool Rast_mask_status(char *name, char *mapset, bool *is_mask_reclass, + char *reclass_name, char *reclass_mapset) { int present = Rast__mask_info(name, mapset); - if (is_mask_reclass) { + if (is_mask_reclass && reclass_name && reclass_mapset) { if (present) { - char rname[GNAME_MAX], rmapset[GMAPSET_MAX]; - *is_mask_reclass = - Rast_is_reclass("MASK", G_mapset(), rname, rmapset) > 0; + Rast_is_reclass("MASK", G_mapset(), reclass_name, reclass_mapset) > 0; } else { *is_mask_reclass = false; diff --git a/raster/Makefile b/raster/Makefile index fce16487e8d..bbee50b15a6 100644 --- a/raster/Makefile +++ b/raster/Makefile @@ -45,6 +45,7 @@ SUBDIRS = \ r.lake \ r.li \ r.mapcalc \ + r.mask.status \ r.mfilter \ r.mode \ r.neighbors \ diff --git a/raster/r.mask.status/main.c b/raster/r.mask.status/main.c index 3f3d736bdd6..c7140720250 100644 --- a/raster/r.mask.status/main.c +++ b/raster/r.mask.status/main.c @@ -48,7 +48,7 @@ void parse_parameters(struct Parameters *params, int argc, char **argv) params->like_test = G_define_flag(); params->like_test->key = 't'; - params->like_test->lable = + params->like_test->label = _("Return code 0 when mask present, 1 otherwise"); params->like_test->description = _("Behave like the test utility, 0 for true, 1 for false, no output"); @@ -84,12 +84,13 @@ int report_status(struct Parameters *params) char name[GNAME_MAX]; char mapset[GMAPSET_MAX]; + char reclass_name[GNAME_MAX]; + char reclass_mapset[GMAPSET_MAX]; bool is_mask_reclass; bool present = - Rast_mask_status(name, mapset, reclass_name, reclass_mapset, - &is_mask_reclass); - bool present = Rast_mask_present(name, mapset); + Rast_mask_status(name, mapset, &is_mask_reclass, reclass_name, reclass_mapset); + // bool present = Rast_mask_present(name, mapset); // This would check the map presence rather than the automasking state in the library. //printf("%s", Rast_mask_info()); From f84b041695851e5a0ce78adf65c4448f1a82fa7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Mon, 9 Sep 2024 17:43:21 -0400 Subject: [PATCH 04/14] Apply suggestions from code review Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- lib/raster/mask_info.c | 8 ++++---- raster/r.mask.status/main.c | 9 ++++----- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/lib/raster/mask_info.c b/lib/raster/mask_info.c index 77f49ec4b68..0ed0f2a0751 100644 --- a/lib/raster/mask_info.c +++ b/lib/raster/mask_info.c @@ -22,8 +22,8 @@ /** * @brief Get a printable text with information about raster mask * - * Determines if 2D raster mask is present and returns textual information about the - * mask suitable for end-user display. The resulting text is translated. + * Determines if 2D raster mask is present and returns textual information about + * the mask suitable for end-user display. The resulting text is translated. * Caller is responsible for freeing the memory of the returned string. * * @return New string with textual information @@ -65,8 +65,8 @@ bool Rast_mask_status(char *name, char *mapset, bool *is_mask_reclass, if (is_mask_reclass && reclass_name && reclass_mapset) { if (present) { - *is_mask_reclass = - Rast_is_reclass("MASK", G_mapset(), reclass_name, reclass_mapset) > 0; + *is_mask_reclass = Rast_is_reclass("MASK", G_mapset(), reclass_name, + reclass_mapset) > 0; } else { *is_mask_reclass = false; diff --git a/raster/r.mask.status/main.c b/raster/r.mask.status/main.c index c7140720250..bd265c79c8f 100644 --- a/raster/r.mask.status/main.c +++ b/raster/r.mask.status/main.c @@ -19,8 +19,7 @@ #include #include -struct Parameters -{ +struct Parameters { struct Option *format; struct Flag *like_test; }; @@ -92,7 +91,7 @@ int report_status(struct Parameters *params) Rast_mask_status(name, mapset, &is_mask_reclass, reclass_name, reclass_mapset); // bool present = Rast_mask_present(name, mapset); // This would check the map presence rather than the automasking state in the library. - //printf("%s", Rast_mask_info()); + // printf("%s", Rast_mask_info()); if (params->like_test->answer) { if (present) @@ -121,7 +120,7 @@ int report_status(struct Parameters *params) else if (strcmp(params->format->answer, "bash") == 0) { printf("present="); if (present) - printf("1"); // Good choice here or not? + printf("1"); // Good choice here or not? else printf("0"); printf("\nfull_name="); @@ -138,7 +137,7 @@ int report_status(struct Parameters *params) printf("false"); printf("\nfull_name: "); if (present) - printf("|-\n MASK@%s", G_mapset()); // MASK or MASK@current_mapset + printf("|-\n MASK@%s", G_mapset()); // MASK or MASK@current_mapset else printf("null"); printf("\nis_reclass_of: "); From 8994d40ba6ef411bb21c88ae662fb23b71cad165 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Mon, 9 Sep 2024 17:54:51 -0400 Subject: [PATCH 05/14] Apply suggestions from code review Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- lib/init/grass.py | 2 +- raster/r.mask.status/main.c | 13 ++++++++----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/lib/init/grass.py b/lib/init/grass.py index 8f6c4b89237..de2f5035b14 100755 --- a/lib/init/grass.py +++ b/lib/init/grass.py @@ -1728,7 +1728,7 @@ def sh_like_startup(location, location_name, grass_env_file, sh): ) ) - mask2d_test = 'r.mask.status -t' + mask2d_test = "r.mask.status -t" # TODO: have a function and/or module to test this mask3d_test = 'test -d "$MAPSET_PATH/grid3/RASTER3D_MASK"' diff --git a/raster/r.mask.status/main.c b/raster/r.mask.status/main.c index bd265c79c8f..eef01525d12 100644 --- a/raster/r.mask.status/main.c +++ b/raster/r.mask.status/main.c @@ -51,7 +51,7 @@ void parse_parameters(struct Parameters *params, int argc, char **argv) _("Return code 0 when mask present, 1 otherwise"); params->like_test->description = _("Behave like the test utility, 0 for true, 1 for false, no output"); - //flags.like_test->guisection = _(""); + // flags.like_test->guisection = _(""); // suppress_required if (G_parser(argc, argv)) @@ -87,9 +87,10 @@ int report_status(struct Parameters *params) char reclass_mapset[GMAPSET_MAX]; bool is_mask_reclass; - bool present = - Rast_mask_status(name, mapset, &is_mask_reclass, reclass_name, reclass_mapset); - // bool present = Rast_mask_present(name, mapset); // This would check the map presence rather than the automasking state in the library. + bool present = Rast_mask_status(name, mapset, &is_mask_reclass, + reclass_name, reclass_mapset); + // bool present = Rast_mask_present(name, mapset); // This would check the + // map presence rather than the automasking state in the library. // printf("%s", Rast_mask_info()); @@ -107,7 +108,9 @@ int report_status(struct Parameters *params) printf("false"); printf(",\n\"full_name\":"); if (present) - json_print_name_mapset("MASK", G_mapset()); // Too much mask details here, move this to the library. + json_print_name_mapset("MASK", + G_mapset()); // Too much mask details here, + // move this to the library. else printf("null"); printf(",\n\"is_reclass_of\": "); From ac83e115164c860b92504666de35518232c17d95 Mon Sep 17 00:00:00 2001 From: Vaclav Petras Date: Tue, 24 Sep 2024 16:44:30 -0400 Subject: [PATCH 06/14] Use parson for the JSON output --- raster/r.mask.status/Makefile | 2 +- raster/r.mask.status/main.c | 49 +++++++++++++++++++++++------------ 2 files changed, 33 insertions(+), 18 deletions(-) diff --git a/raster/r.mask.status/Makefile b/raster/r.mask.status/Makefile index 0be917f1040..62c968d044e 100644 --- a/raster/r.mask.status/Makefile +++ b/raster/r.mask.status/Makefile @@ -2,7 +2,7 @@ MODULE_TOPDIR = ../.. PGM = r.mask.status -LIBES = $(MANAGELIB) $(RASTERLIB) $(GISLIB) +LIBES = $(MANAGELIB) $(RASTERLIB) $(GISLIB) $(PARSONLIB) DEPENDENCIES = $(MANAGEDEP) $(RASTERDEP) $(GISDEP) include $(MODULE_TOPDIR)/include/Make/Module.make diff --git a/raster/r.mask.status/main.c b/raster/r.mask.status/main.c index eef01525d12..d03bff8a33b 100644 --- a/raster/r.mask.status/main.c +++ b/raster/r.mask.status/main.c @@ -15,7 +15,9 @@ #include #include #include + #include +#include #include #include @@ -67,6 +69,7 @@ char *min_json_escape(const char *str) return tmp2; } +// We are using this in shell script style output. void json_print_name_mapset(const char *name, const char *mapset) { // Being paranoid about what is in the name. @@ -101,24 +104,37 @@ int report_status(struct Parameters *params) return 1; } else if (strcmp(params->format->answer, "json") == 0) { - printf("{\"present\":"); - if (present) - printf("true"); - else - printf("false"); - printf(",\n\"full_name\":"); + JSON_Value *root_value = json_value_init_object(); + JSON_Object *root_object = json_object(root_value); + json_object_set_boolean(root_object, "present", present); + size_t full_name_size = GNAME_MAX + GMAPSET_MAX + 2; + char full[full_name_size]; + // Mask raster + // TODO: Too much mask details here, refactor this to the library. + // Specifics about mask name and mapset should be in the library, + // but that's better done in #2392 (mask from env variable). + // (Same applies to the other formats.) + G_strlcat(full, "MASK", full_name_size); + G_strlcat(full, "@", full_name_size); + G_strlcat(full, G_mapset(), full_name_size); + // Is sprintf a better choice for the above? if (present) - json_print_name_mapset("MASK", - G_mapset()); // Too much mask details here, - // move this to the library. + json_object_set_string(root_object, "full_name", full); else - printf("null"); - printf(",\n\"is_reclass_of\": "); + json_object_set_null(root_object, "full_name"); + // Underlying raster if applicable + full[0] = '\0'; + G_strlcat(full, reclass_name, full_name_size); + G_strlcat(full, "@", full_name_size); + G_strlcat(full, reclass_mapset, full_name_size); if (is_mask_reclass) - json_print_name_mapset(name, mapset); + json_object_set_string(root_object, "is_reclass_of", full); else - printf("null"); - printf("}\n"); + json_object_set_null(root_object, "is_reclass_of"); + char *serialized_string = json_serialize_to_string_pretty(root_value); + puts(serialized_string); + json_free_serialized_string(serialized_string); + json_value_free(root_value); } else if (strcmp(params->format->answer, "bash") == 0) { printf("present="); @@ -150,9 +166,8 @@ int report_status(struct Parameters *params) else printf("null"); // true if MASK in current mapset and is reclass, false otherwise, - // then also outputting mask cats is needed to inform user about the portion of the map - // printf("\nmask_reclass: "); - // if (is_mask_reclass) + // then also outputting mask cats is needed to inform user about the + // portion of the map printf("\nmask_reclass: "); if (is_mask_reclass) // printf("true"); // else // printf("false"); From b22c85b611c9a20a1cd5e86f7ad3bdf75e1f9b79 Mon Sep 17 00:00:00 2001 From: Vaclav Petras Date: Wed, 25 Sep 2024 08:52:39 -0400 Subject: [PATCH 07/14] Use lib function to get full name --- raster/r.mask.status/main.c | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/raster/r.mask.status/main.c b/raster/r.mask.status/main.c index d03bff8a33b..b10fa2ed1f9 100644 --- a/raster/r.mask.status/main.c +++ b/raster/r.mask.status/main.c @@ -107,26 +107,19 @@ int report_status(struct Parameters *params) JSON_Value *root_value = json_value_init_object(); JSON_Object *root_object = json_object(root_value); json_object_set_boolean(root_object, "present", present); - size_t full_name_size = GNAME_MAX + GMAPSET_MAX + 2; - char full[full_name_size]; // Mask raster // TODO: Too much mask details here, refactor this to the library. // Specifics about mask name and mapset should be in the library, // but that's better done in #2392 (mask from env variable). // (Same applies to the other formats.) - G_strlcat(full, "MASK", full_name_size); - G_strlcat(full, "@", full_name_size); - G_strlcat(full, G_mapset(), full_name_size); + char *full = G_fully_qualified_name("MASK", G_mapset()); // Is sprintf a better choice for the above? if (present) json_object_set_string(root_object, "full_name", full); else json_object_set_null(root_object, "full_name"); // Underlying raster if applicable - full[0] = '\0'; - G_strlcat(full, reclass_name, full_name_size); - G_strlcat(full, "@", full_name_size); - G_strlcat(full, reclass_mapset, full_name_size); + full = G_fully_qualified_name(reclass_name, reclass_mapset); if (is_mask_reclass) json_object_set_string(root_object, "is_reclass_of", full); else @@ -135,6 +128,7 @@ int report_status(struct Parameters *params) puts(serialized_string); json_free_serialized_string(serialized_string); json_value_free(root_value); + G_free(full); } else if (strcmp(params->format->answer, "bash") == 0) { printf("present="); From 1f2fcdcd358cfeba0f1160c78d535ab3cc52b47c Mon Sep 17 00:00:00 2001 From: Vaclav Petras Date: Wed, 25 Sep 2024 09:10:51 -0400 Subject: [PATCH 08/14] Make all three formats work in a similar way --- raster/r.mask.status/main.c | 82 +++++++++++++++++-------------------- 1 file changed, 38 insertions(+), 44 deletions(-) diff --git a/raster/r.mask.status/main.c b/raster/r.mask.status/main.c index b10fa2ed1f9..23b440b06ab 100644 --- a/raster/r.mask.status/main.c +++ b/raster/r.mask.status/main.c @@ -60,27 +60,6 @@ void parse_parameters(struct Parameters *params, int argc, char **argv) exit(EXIT_FAILURE); } -char *min_json_escape(const char *str) -{ - char *tmp1 = G_str_replace(str, "\\", "\\\\"); - char *tmp2 = G_str_replace(tmp1, "\"", "\\\""); - - G_free(tmp1); - return tmp2; -} - -// We are using this in shell script style output. -void json_print_name_mapset(const char *name, const char *mapset) -{ - // Being paranoid about what is in the name. - char *escaped_name = min_json_escape(name); - char *escaped_mapset = min_json_escape(mapset); - - printf("\"%s@%s\"", name, mapset); - G_free(escaped_name); - G_free(escaped_mapset); -} - int report_status(struct Parameters *params) { @@ -90,6 +69,8 @@ int report_status(struct Parameters *params) char reclass_mapset[GMAPSET_MAX]; bool is_mask_reclass; + // TODO: Review need the new function. Can it be made more universal now? + // (to avoid need to repeat MASK here anyway) bool present = Rast_mask_status(name, mapset, &is_mask_reclass, reclass_name, reclass_mapset); // bool present = Rast_mask_present(name, mapset); // This would check the @@ -97,38 +78,43 @@ int report_status(struct Parameters *params) // printf("%s", Rast_mask_info()); + // This does not have to be exclusive with the printing, but perhaps there + // is a different boolean flag which does the return code and printing and + // this really behaves like the test utility facilitate the primary usage of + // this which is prompt building (there any output would be noise). if (params->like_test->answer) { if (present) return 0; - else - return 1; + return 1; } - else if (strcmp(params->format->answer, "json") == 0) { + + // Mask raster + // TODO: Too much mask details here, refactor this to the library. + // Specifics about mask name and mapset should be in the library, + // but that's likely better done in #2392 (mask from env variable). + char *full_mask = G_fully_qualified_name("MASK", G_mapset()); + // Underlying raster if applicable + char *full_underlying = NULL; + if (is_mask_reclass) + full_underlying = G_fully_qualified_name(reclass_name, reclass_mapset); + + if (strcmp(params->format->answer, "json") == 0) { JSON_Value *root_value = json_value_init_object(); JSON_Object *root_object = json_object(root_value); json_object_set_boolean(root_object, "present", present); - // Mask raster - // TODO: Too much mask details here, refactor this to the library. - // Specifics about mask name and mapset should be in the library, - // but that's better done in #2392 (mask from env variable). - // (Same applies to the other formats.) - char *full = G_fully_qualified_name("MASK", G_mapset()); - // Is sprintf a better choice for the above? if (present) - json_object_set_string(root_object, "full_name", full); + json_object_set_string(root_object, "full_name", full_mask); else json_object_set_null(root_object, "full_name"); - // Underlying raster if applicable - full = G_fully_qualified_name(reclass_name, reclass_mapset); if (is_mask_reclass) - json_object_set_string(root_object, "is_reclass_of", full); + json_object_set_string(root_object, "is_reclass_of", + full_underlying); else json_object_set_null(root_object, "is_reclass_of"); char *serialized_string = json_serialize_to_string_pretty(root_value); puts(serialized_string); json_free_serialized_string(serialized_string); json_value_free(root_value); - G_free(full); } else if (strcmp(params->format->answer, "bash") == 0) { printf("present="); @@ -137,9 +123,11 @@ int report_status(struct Parameters *params) else printf("0"); printf("\nfull_name="); - if (present) { - json_print_name_mapset(name, mapset); - } + if (present) + printf("%s", full_mask); + printf("\nis_reclass_of="); + if (is_mask_reclass) + printf("%s", full_underlying); printf("\n"); } else { @@ -150,23 +138,29 @@ int report_status(struct Parameters *params) printf("false"); printf("\nfull_name: "); if (present) - printf("|-\n MASK@%s", G_mapset()); // MASK or MASK@current_mapset + printf("|-\n %s", full_mask); else printf("null"); printf("\nis_reclass_of: "); // Using block scalar with |- to avoid need for escaping. if (is_mask_reclass) - printf("|-\n %s@%s", name, mapset); + printf("|-\n %s", full_underlying); else printf("null"); - // true if MASK in current mapset and is reclass, false otherwise, - // then also outputting mask cats is needed to inform user about the - // portion of the map printf("\nmask_reclass: "); if (is_mask_reclass) + // We could also produce true if is reclass, false otherwise. + // printf("\nmask_reclass: "); + // if (is_mask_reclass) // printf("true"); // else // printf("false"); + // We could also outputting mask cats to inform user about the + // relevant portion of the map, but that should be done by accessing + // the actual mask anyway. printf("\n"); } + + G_free(full_mask); + G_free(full_underlying); return EXIT_SUCCESS; } From 99929c82d21a3e5b759d8241a33da80f0788ddb6 Mon Sep 17 00:00:00 2001 From: Vaclav Petras Date: Thu, 26 Sep 2024 09:04:00 -0400 Subject: [PATCH 09/14] Limit mask name hardcoding to the library. Polish comments. --- lib/raster/mask_info.c | 10 +++++++--- raster/r.mask.status/main.c | 31 +++++++++++++------------------ 2 files changed, 20 insertions(+), 21 deletions(-) diff --git a/lib/raster/mask_info.c b/lib/raster/mask_info.c index 0ed0f2a0751..6c396adc36b 100644 --- a/lib/raster/mask_info.c +++ b/lib/raster/mask_info.c @@ -3,7 +3,7 @@ * * \brief Raster Library - Get mask information * - * (C) 1999-2022 by Vaclav Petras and the GRASS Development Team + * (C) 1999-2024 by Vaclav Petras and the GRASS Development Team * * This program is free software under the GNU General Public * License (>=v2). Read the file COPYING that comes with GRASS @@ -67,6 +67,10 @@ bool Rast_mask_status(char *name, char *mapset, bool *is_mask_reclass, if (present) { *is_mask_reclass = Rast_is_reclass("MASK", G_mapset(), reclass_name, reclass_mapset) > 0; + // The original mask values are overwritten, put back the original + // values, so that we can report them to the caller. + strcpy(name, "MASK"); + strcpy(mapset, G_mapset()); } else { *is_mask_reclass = false; @@ -89,14 +93,14 @@ bool Rast_mask_status(char *name, char *mapset, bool *is_mask_reclass, * the name and mapset of the underlying reclassed raster are returned. * * The name and mapset is written to the parameter which need to be defined - * with a suffient size, least as `char name[GNAME_MAX], mapset[GMAPSET_MAX]`. + * with a sufficient size, least as `char name[GNAME_MAX], mapset[GMAPSET_MAX]`. * * When the masking is not active, -1 is returned and name and mapset are * undefined. When the masking is active, 1 is returned and name and mapset * will hold the name and mapset of the underlying raster. * * @param[out] name Name of the raster map used as mask - * @param[out] mapset Mapset name of the map + * @param[out] mapset Name of the map's mapset * * @return 1 if mask is present, -1 otherwise */ diff --git a/raster/r.mask.status/main.c b/raster/r.mask.status/main.c index 23b440b06ab..a155783c43a 100644 --- a/raster/r.mask.status/main.c +++ b/raster/r.mask.status/main.c @@ -3,7 +3,7 @@ * MODULE: r.mask.status * AUTHORS: Vaclav Petras * PURPOSE: Report status of raster mask - * COPYRIGHT: (C) 2022 by Vaclav Petras and the GRASS Development Team + * COPYRIGHT: (C) 2024 by Vaclav Petras and the GRASS Development Team * * This program is free software under the GNU General Public * License (>=v2). Read the file COPYING that comes with GRASS @@ -32,17 +32,17 @@ void parse_parameters(struct Parameters *params, int argc, char **argv) module = G_define_module(); G_add_keyword(_("raster")); + G_add_keyword(_("mask")); G_add_keyword(_("reclassification")); - module->label = _("Reclassify raster map based on category values."); + module->label = _("Reports presence or absence of a raster mask"); module->description = - _("Creates a new raster map whose category values are based " - "upon a reclassification of the categories in an existing " - "raster map."); + _("Provides information about present of a 2D raster mask" + "as text output or return code"); params->format = G_define_option(); params->format->key = "format"; params->format->type = TYPE_STRING; - params->format->required = NO; + params->format->required = YES; params->format->answer = "yaml"; params->format->options = "yaml,json,bash"; params->format->description = _("Format for reporting"); @@ -53,8 +53,7 @@ void parse_parameters(struct Parameters *params, int argc, char **argv) _("Return code 0 when mask present, 1 otherwise"); params->like_test->description = _("Behave like the test utility, 0 for true, 1 for false, no output"); - // flags.like_test->guisection = _(""); - // suppress_required + // suppress_required is not required given the default value for format. if (G_parser(argc, argv)) exit(EXIT_FAILURE); @@ -69,14 +68,8 @@ int report_status(struct Parameters *params) char reclass_mapset[GMAPSET_MAX]; bool is_mask_reclass; - // TODO: Review need the new function. Can it be made more universal now? - // (to avoid need to repeat MASK here anyway) bool present = Rast_mask_status(name, mapset, &is_mask_reclass, reclass_name, reclass_mapset); - // bool present = Rast_mask_present(name, mapset); // This would check the - // map presence rather than the automasking state in the library. - - // printf("%s", Rast_mask_info()); // This does not have to be exclusive with the printing, but perhaps there // is a different boolean flag which does the return code and printing and @@ -89,10 +82,7 @@ int report_status(struct Parameters *params) } // Mask raster - // TODO: Too much mask details here, refactor this to the library. - // Specifics about mask name and mapset should be in the library, - // but that's likely better done in #2392 (mask from env variable). - char *full_mask = G_fully_qualified_name("MASK", G_mapset()); + char *full_mask = G_fully_qualified_name(name, mapset); // Underlying raster if applicable char *full_underlying = NULL; if (is_mask_reclass) @@ -131,6 +121,7 @@ int report_status(struct Parameters *params) printf("\n"); } else { + // Using YAML as the human-readable default format. How does that work? printf("present: "); if (present) printf("true"); @@ -141,8 +132,12 @@ int report_status(struct Parameters *params) printf("|-\n %s", full_mask); else printf("null"); + // Null values in YAML can be an empty (no) value (rather than null), + // so we could use that. printf("\nis_reclass_of: "); // Using block scalar with |- to avoid need for escaping. + // Alternatively, we could check mapset naming limits against YAML + // escaping needs for different types of strings. if (is_mask_reclass) printf("|-\n %s", full_underlying); else From caad7ead65bddc40b024504c813b94b6c9ea591f Mon Sep 17 00:00:00 2001 From: Vaclav Petras Date: Thu, 26 Sep 2024 11:12:38 -0400 Subject: [PATCH 10/14] Add missing space --- raster/r.mask.status/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/raster/r.mask.status/main.c b/raster/r.mask.status/main.c index a155783c43a..830b3d10eaf 100644 --- a/raster/r.mask.status/main.c +++ b/raster/r.mask.status/main.c @@ -37,7 +37,7 @@ void parse_parameters(struct Parameters *params, int argc, char **argv) module->label = _("Reports presence or absence of a raster mask"); module->description = _("Provides information about present of a 2D raster mask" - "as text output or return code"); + " as text output or return code"); params->format = G_define_option(); params->format->key = "format"; From cc53fb8fb33d50f4a351e7867909b0fcb8a4b0a4 Mon Sep 17 00:00:00 2001 From: Vaclav Petras Date: Thu, 26 Sep 2024 11:34:52 -0400 Subject: [PATCH 11/14] Add examples --- raster/r.mask.status/r.mask.status.html | 33 ++++++++++++++++++++----- 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/raster/r.mask.status/r.mask.status.html b/raster/r.mask.status/r.mask.status.html index f8cb8255c5e..b4f40f9087b 100644 --- a/raster/r.mask.status/r.mask.status.html +++ b/raster/r.mask.status/r.mask.status.html @@ -2,27 +2,48 @@

DESCRIPTION

r.mask.status reports presence or absence of a raster mask. -

NOTES

-

EXAMPLES

Generate JSON output

+To generate JSON output, use the format option: +
 r.mask.status format=json
 
+In Python: + +
+import grass.script as gs
+gs.parse_command("r.mask.status", format="json")
+
+ +This returns a dictionary with keys present, +full_name, and is_reclass_of. +

Use as the test utility

-The POSIX test utility uses return code 0 to indicate true -and 1 to indicate false, so testing existence of a file with test -f -gives return code 0 when the file exists. r.mask.status can be used -in the same with the the -t flag: +The POSIX test utility uses return code 0 to indicate presence +and 1 to indicate absence of a file, so testing existence of a file with +test -f gives return code 0 when the file exists. +r.mask.status can be used in the same with the the -t flag:
 r.mask.status -t
 
+In a Bash script: + +
+# Bash
+if r.mask.status -t; then
+    echo "Masking is active"
+else
+    echo "Masking is not active"
+fi
+
+

SEE ALSO

From f522e5af242d196098b8bcd0494b0c39629cf489 Mon Sep 17 00:00:00 2001 From: Vaclav Petras Date: Fri, 27 Sep 2024 16:13:56 -0400 Subject: [PATCH 12/14] Add tests --- raster/r.mask.status/tests/conftest.py | 25 ++++++ .../r.mask.status/tests/r_mask_status_test.py | 89 +++++++++++++++++++ 2 files changed, 114 insertions(+) create mode 100644 raster/r.mask.status/tests/conftest.py create mode 100644 raster/r.mask.status/tests/r_mask_status_test.py diff --git a/raster/r.mask.status/tests/conftest.py b/raster/r.mask.status/tests/conftest.py new file mode 100644 index 00000000000..5a930158bd0 --- /dev/null +++ b/raster/r.mask.status/tests/conftest.py @@ -0,0 +1,25 @@ +"""Fixtures for simple sessions""" + +import os +import pytest +import grass.script as gs + + +@pytest.fixture() +def session_no_data(tmp_path): + """Set up a GRASS session for the tests.""" + project = "test_project" + gs.create_project(tmp_path, project) + with gs.setup.init(tmp_path / project, env=os.environ.copy()) as session: + yield session + + +@pytest.fixture() +def session_with_data(tmp_path): + """Set up a GRASS session for the tests.""" + project = tmp_path / "test_project" + gs.create_project(project) + with gs.setup.init(project, env=os.environ.copy()) as session: + gs.run_command("g.region", rows=2, cols=2, env=session.env) + gs.mapcalc("a = 1", env=session.env) + yield session diff --git a/raster/r.mask.status/tests/r_mask_status_test.py b/raster/r.mask.status/tests/r_mask_status_test.py new file mode 100644 index 00000000000..18bbf11ce4c --- /dev/null +++ b/raster/r.mask.status/tests/r_mask_status_test.py @@ -0,0 +1,89 @@ +"""Tests of r.mask.status""" + +import pytest + +try: + import yaml +except ImportError: + yaml = None + +import grass.script as gs + + +def test_json_no_mask(session_no_data): + """Check JSON format for no mask""" + session = session_no_data + data = gs.parse_command("r.mask.status", format="json", env=session.env) + assert "present" in data + assert "full_name" in data + assert "is_reclass_of" in data + assert data["present"] is False + assert not data["full_name"] + assert not data["is_reclass_of"] + + +def test_json_with_r_mask(session_with_data): + """Check JSON format for the r.mask case""" + session = session_with_data + gs.run_command("r.mask", raster="a", env=session.env) + data = gs.parse_command("r.mask.status", format="json", env=session.env) + assert data["present"] is True + assert data["full_name"] == "MASK@PERMANENT" + assert data["is_reclass_of"] == "a@PERMANENT" + # Now remove the mask. + gs.run_command("r.mask", flags="r", env=session.env) + data = gs.parse_command("r.mask.status", format="json", env=session.env) + assert data["present"] is False + assert not data["full_name"] + assert not data["is_reclass_of"] + + +def test_json_with_g_copy(session_with_data): + """Check JSON format for the low-level g.copy case""" + session = session_with_data + gs.run_command("g.copy", raster="a,MASK", env=session.env) + data = gs.parse_command("r.mask.status", format="json", env=session.env) + assert data["present"] is True + assert data["full_name"] == "MASK@PERMANENT" + assert not data["is_reclass_of"] + # Now remove the mask. + gs.run_command("g.remove", type="raster", name="MASK", flags="f", env=session.env) + data = gs.parse_command("r.mask.status", format="json", env=session.env) + assert data["present"] is False + assert not data["full_name"] + assert not data["is_reclass_of"] + + +def test_shell_with_r_mask(session_with_data): + """Check shell format for the r.mask case""" + session = session_with_data + gs.run_command("r.mask", raster="a", env=session.env) + data = gs.parse_command("r.mask.status", format="bash", env=session.env) + assert int(data["present"]) + assert data["full_name"] == "MASK@PERMANENT" + assert data["is_reclass_of"] == "a@PERMANENT" + # Now remove the mask. + gs.run_command("r.mask", flags="r", env=session.env) + data = gs.parse_command("r.mask.status", format="bash", env=session.env) + assert not int(data["present"]) + assert not data["full_name"] + assert not data["is_reclass_of"] + + +@pytest.mark.skipif(yaml is None, reason="PyYAML package not available") +def test_yaml_with_r_mask(session_with_data): + """Check YAML format for the r.mask case""" + session = session_with_data + gs.run_command("r.mask", raster="a", env=session.env) + text = gs.read_command("r.mask.status", format="yaml", env=session.env) + data = yaml.safe_load(text) + assert data["present"] is True + assert data["full_name"] == "MASK@PERMANENT" + assert data["is_reclass_of"] == "a@PERMANENT" + # Now remove the mask. + gs.run_command("r.mask", flags="r", env=session.env) + text = gs.read_command("r.mask.status", format="yaml", env=session.env) + data = yaml.safe_load(text) + assert data["present"] is False + assert not data["full_name"] + assert not data["is_reclass_of"] From c68e41fde302db844e8469c5654d71173a45c97f Mon Sep 17 00:00:00 2001 From: Vaclav Petras Date: Fri, 27 Sep 2024 16:19:43 -0400 Subject: [PATCH 13/14] Add plain text output (YAML is not translatable and plain text is 'plainer'). The format option cannot be required otherwise running without parameters through the Python API does not work. --- raster/r.mask.status/main.c | 25 +++++++++++++++---- .../r.mask.status/tests/r_mask_status_test.py | 21 ++++++++++++++++ 2 files changed, 41 insertions(+), 5 deletions(-) diff --git a/raster/r.mask.status/main.c b/raster/r.mask.status/main.c index 830b3d10eaf..b6e251aa868 100644 --- a/raster/r.mask.status/main.c +++ b/raster/r.mask.status/main.c @@ -42,9 +42,9 @@ void parse_parameters(struct Parameters *params, int argc, char **argv) params->format = G_define_option(); params->format->key = "format"; params->format->type = TYPE_STRING; - params->format->required = YES; - params->format->answer = "yaml"; - params->format->options = "yaml,json,bash"; + params->format->required = NO; + params->format->answer = "plain"; + params->format->options = "plain,json,bash,yaml"; params->format->description = _("Format for reporting"); params->like_test = G_define_flag(); @@ -120,8 +120,7 @@ int report_status(struct Parameters *params) printf("%s", full_underlying); printf("\n"); } - else { - // Using YAML as the human-readable default format. How does that work? + else if (strcmp(params->format->answer, "yaml") == 0) { printf("present: "); if (present) printf("true"); @@ -153,6 +152,22 @@ int report_status(struct Parameters *params) // the actual mask anyway. printf("\n"); } + else { + if (present) + printf(_("Mask is active")); + else + printf(_("Mask is not present")); + if (present) { + printf("\n"); + printf(_("Mask name: %s"), full_mask); + } + if (is_mask_reclass) { + printf("\n"); + printf(_("Mask is a raster reclassified from: %s"), + full_underlying); + } + printf("\n"); + } G_free(full_mask); G_free(full_underlying); diff --git a/raster/r.mask.status/tests/r_mask_status_test.py b/raster/r.mask.status/tests/r_mask_status_test.py index 18bbf11ce4c..ff013d23dac 100644 --- a/raster/r.mask.status/tests/r_mask_status_test.py +++ b/raster/r.mask.status/tests/r_mask_status_test.py @@ -87,3 +87,24 @@ def test_yaml_with_r_mask(session_with_data): assert data["present"] is False assert not data["full_name"] assert not data["is_reclass_of"] + + +def test_plain_with_r_mask(session_with_data): + """Check plain text format for the r.mask case""" + session = session_with_data + gs.run_command("r.mask", raster="a", env=session.env) + text = gs.read_command("r.mask.status", format="plain", env=session.env) + assert text + assert "MASK@PERMANENT" in text + assert "a@PERMANENT" in text + # Now remove the mask. + gs.run_command("r.mask", flags="r", env=session.env) + text = gs.read_command("r.mask.status", format="plain", env=session.env) + assert text + + +def test_without_parameters(session_no_data): + """Check output is generated with no parameters""" + session = session_no_data + text = gs.read_command("r.mask.status", env=session.env) + assert text From 2841aeb973bbe51547af6557c3d63775eb40a7e8 Mon Sep 17 00:00:00 2001 From: Vaclav Petras Date: Fri, 27 Sep 2024 16:24:41 -0400 Subject: [PATCH 14/14] Add test for return code --- .../r.mask.status/tests/r_mask_status_test.py | 22 ++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/raster/r.mask.status/tests/r_mask_status_test.py b/raster/r.mask.status/tests/r_mask_status_test.py index ff013d23dac..37b556b30e0 100644 --- a/raster/r.mask.status/tests/r_mask_status_test.py +++ b/raster/r.mask.status/tests/r_mask_status_test.py @@ -54,7 +54,7 @@ def test_json_with_g_copy(session_with_data): assert not data["is_reclass_of"] -def test_shell_with_r_mask(session_with_data): +def test_shell(session_with_data): """Check shell format for the r.mask case""" session = session_with_data gs.run_command("r.mask", raster="a", env=session.env) @@ -71,7 +71,7 @@ def test_shell_with_r_mask(session_with_data): @pytest.mark.skipif(yaml is None, reason="PyYAML package not available") -def test_yaml_with_r_mask(session_with_data): +def test_yaml(session_with_data): """Check YAML format for the r.mask case""" session = session_with_data gs.run_command("r.mask", raster="a", env=session.env) @@ -89,7 +89,7 @@ def test_yaml_with_r_mask(session_with_data): assert not data["is_reclass_of"] -def test_plain_with_r_mask(session_with_data): +def test_plain(session_with_data): """Check plain text format for the r.mask case""" session = session_with_data gs.run_command("r.mask", raster="a", env=session.env) @@ -108,3 +108,19 @@ def test_without_parameters(session_no_data): session = session_no_data text = gs.read_command("r.mask.status", env=session.env) assert text + + +def test_behavior_mimicking_test_program(session_with_data): + """Check test program like behavior for the r.mask case""" + session = session_with_data + gs.run_command("r.mask", raster="a", env=session.env) + returncode = gs.run_command( + "r.mask.status", flags="t", env=session.env, errors="status" + ) + assert returncode == 0 + # Now remove the mask. + gs.run_command("r.mask", flags="r", env=session.env) + returncode = gs.run_command( + "r.mask.status", flags="t", env=session.env, errors="status" + ) + assert returncode == 1