From dd376b2c7a1c20594848b235f202024f5006ee7f Mon Sep 17 00:00:00 2001 From: Anna Petrasova Date: Mon, 13 Jul 2026 09:06:39 -0400 Subject: [PATCH 1/2] t.info: Add tests Add pytest tests covering the plain, shell (-g), history (-h) and temporal database backend (-d) output of t.info for space time datasets and maps of all types. Include a dataset without registered maps, a relative time dataset, a map registered in two datasets and non-ASCII title and description. Remove the shell script test, which the new tests replace. The tests were written with the help of Claude Code. --- temporal/t.info/tests/conftest.py | 140 +++++++++ temporal/t.info/tests/test_t_info_backend.py | 69 +++++ temporal/t.info/tests/test_t_info_str3ds.py | 188 ++++++++++++ temporal/t.info/tests/test_t_info_strds.py | 302 +++++++++++++++++++ temporal/t.info/tests/test_t_info_stvds.py | 195 ++++++++++++ temporal/t.info/testsuite/test.t.info.sh | 84 ------ 6 files changed, 894 insertions(+), 84 deletions(-) create mode 100644 temporal/t.info/tests/conftest.py create mode 100644 temporal/t.info/tests/test_t_info_backend.py create mode 100644 temporal/t.info/tests/test_t_info_str3ds.py create mode 100644 temporal/t.info/tests/test_t_info_strds.py create mode 100644 temporal/t.info/tests/test_t_info_stvds.py delete mode 100755 temporal/t.info/testsuite/test.t.info.sh diff --git a/temporal/t.info/tests/conftest.py b/temporal/t.info/tests/conftest.py new file mode 100644 index 00000000000..4729a6d68de --- /dev/null +++ b/temporal/t.info/tests/conftest.py @@ -0,0 +1,140 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +"""Fixture for t.info tests + +Creates one project holding every dataset type t.info can report on, so that +the reported metadata can be compared against known values. +""" + +import os +from types import SimpleNamespace + +import pytest + +import grass.script as gs +from grass.grassdb.create import create_mapset +from grass.tools import Tools + +MAPSET = "tinfo_mapset" + + +@pytest.fixture(scope="session") +def temporal_data(tmp_path_factory): + """Start a session with space time datasets of all types and their maps""" + tmp_path = tmp_path_factory.mktemp("t_info") + project = tmp_path / "test_project" + gs.create_project(project) + create_mapset(project / MAPSET) + + with gs.setup.init(project / MAPSET, env=os.environ.copy()) as session: + tools = Tools(session=session) + tools.g_region(s=0, n=80, w=0, e=120, b=0, t=50, res=10, res3=10, tbres=10) + + # Maps with constant values keep the min and max metadata predictable. + for i in range(1, 4): + tools.r_mapcalc(expression=f"prec_{i} = {i}") + for i in range(1, 3): + tools.r3_mapcalc(expression=f"vol_{i} = {i}") + tools.v_random(output=f"vect_{i}", npoints=20, seed=i) + tools.r_mapcalc(expression=f"rel_{i} = {i}") + + tools.t_create( + type="strds", + temporaltype="absolute", + output="precip_abs1", + title="A test", + description="A test", + ) + tools.t_register( + type="raster", + flags="i", + input="precip_abs1", + maps="prec_1,prec_2,prec_3", + start="2001-01-01", + increment="1 months", + ) + + # A map can be registered in more than one dataset, which t.info + # reports for the map. + tools.t_create( + type="strds", + temporaltype="absolute", + output="precip_abs2", + title="A second test", + description="A second test", + ) + tools.t_register( + type="raster", + input="precip_abs2", + maps="prec_2", + ) + + tools.t_create( + type="str3ds", + temporaltype="absolute", + output="volume_abs1", + title="A 3D test", + description="A 3D test", + ) + tools.t_register( + type="raster_3d", + flags="i", + input="volume_abs1", + maps="vol_1,vol_2", + start="2001-01-01", + increment="1 months", + ) + + tools.t_create( + type="stvds", + temporaltype="absolute", + output="vect_abs1", + title="A vector test", + description="A vector test", + ) + tools.t_register( + type="vector", + flags="i", + input="vect_abs1", + maps="vect_1,vect_2", + start="2001-01-01", + increment="1 months", + ) + + # Relative time datasets need their own maps because a map can hold + # only one time stamp. + tools.t_create( + type="strds", + temporaltype="relative", + output="precip_rel1", + title="A relative test", + description="A relative test", + ) + tools.t_register( + type="raster", + flags="i", + input="precip_rel1", + maps="rel_1,rel_2", + start=1, + increment=1, + unit="days", + ) + + # A dataset without registered maps reports empty metadata fields. + tools.t_create( + type="strds", + temporaltype="absolute", + output="empty_ds", + title="An empty test", + description="An empty test", + ) + + # Non-ASCII text has to survive the round trip through the database. + tools.t_create( + type="strds", + temporaltype="absolute", + output="accent_ds", + title="Précipitation totale", + description="Précipitation mensuelle", + ) + + yield SimpleNamespace(session=session, mapset=MAPSET, tools=tools) diff --git a/temporal/t.info/tests/test_t_info_backend.py b/temporal/t.info/tests/test_t_info_backend.py new file mode 100644 index 00000000000..1800489d42d --- /dev/null +++ b/temporal/t.info/tests/test_t_info_backend.py @@ -0,0 +1,69 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +"""Test the t.info report about the temporal database backend (-d flag) + +The expected values capture the output of t.info as it is now, so that later +changes to the interface can be checked against the established behavior. +""" + +import re + +import grass.script as gs +from grass.tools import Tools + +BACKEND_SHELL_KEYS = [ + "dbmi_python_interface", + "dbmi_string", + "sql_template_path", + "tgis_version", + "tgis_db_version", + "creation_time", +] + + +def test_backend_plain(temporal_data): + """The -d flag reports the backend in a human readable table""" + tools = Tools(session=temporal_data.session) + text = tools.t_info(flags="d").text + lines = text.splitlines() + + assert "Temporal DBMI backend information" in lines[0] + assert " | DBMI Python interface:...... sqlite3" in lines + assert any(line.startswith(" | Temporal database string:...") for line in lines) + assert any(line.startswith(" | SQL template path:..........") for line in lines) + assert any(line.startswith(" | tgis_version ..........") for line in lines) + assert any(line.startswith(" | tgis_db_version ..........") for line in lines) + assert any(line.startswith(" | creation_time ..........") for line in lines) + assert lines[-1].startswith(" +---------") + assert "" not in [line.strip() for line in lines] + + +def test_backend_shell(temporal_data): + """The -d flag together with -g reports the backend as key value pairs""" + tools = Tools(session=temporal_data.session) + text = tools.t_info(flags="dg").text + info = gs.parse_key_val(text) + + assert list(info.keys()) == BACKEND_SHELL_KEYS + assert info["dbmi_python_interface"] == "'sqlite3'" + assert info["dbmi_string"].endswith("sqlite.db'") + assert info["tgis_version"] == "'2'" + assert info["tgis_db_version"] == "'3'" + + +def test_backend_shell_values_are_quoted(temporal_data): + """All values of the backend information are quoted in the shell output""" + tools = Tools(session=temporal_data.session) + text = tools.t_info(flags="dg").text + + for line in text.splitlines(): + assert re.fullmatch(r"\w+='.*'", line), line + + +def test_backend_ignores_input(temporal_data): + """The -d flag reports the backend only, even if a dataset is given""" + tools = Tools(session=temporal_data.session) + with_input = tools.t_info(flags="d", type="strds", input="precip_abs1").text + without_input = tools.t_info(flags="d").text + + assert with_input == without_input + assert "precip_abs1" not in with_input diff --git a/temporal/t.info/tests/test_t_info_str3ds.py b/temporal/t.info/tests/test_t_info_str3ds.py new file mode 100644 index 00000000000..edd04577d15 --- /dev/null +++ b/temporal/t.info/tests/test_t_info_str3ds.py @@ -0,0 +1,188 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +"""Test t.info with space time 3D raster datasets and 3D raster maps + +The expected values capture the output of t.info as it is now, so that later +changes to the interface can be checked against the established behavior. +""" + +import grass.script as gs +from grass.tools import Tools + +# Keys whose values differ between runs, systems, or users. +VOLATILE_KEYS = ("creation_time", "modification_time", "creator", "raster3d_register") + +STR3DS_SHELL_KEYS = [ + "id", + "name", + "mapset", + "creator", + "temporal_type", + "creation_time", + "modification_time", + "semantic_type", + "start_time", + "end_time", + "granularity", + "map_time", + "north", + "south", + "east", + "west", + "top", + "bottom", + "raster3d_register", + "tbres_min", + "tbres_max", + "nsres_min", + "nsres_max", + "ewres_min", + "ewres_max", + "min_min", + "min_max", + "max_min", + "max_max", + "aggregation_type", + "number_of_maps", +] + +RASTER3D_SHELL_KEYS = [ + "id", + "name", + "mapset", + "creator", + "temporal_type", + "creation_time", + "start_time", + "end_time", + "north", + "south", + "east", + "west", + "top", + "bottom", + "datatype", + "cols", + "rows", + "number_of_cells", + "nsres", + "ewres", + "min", + "max", + "depths", + "tbres", + "registered_datasets", +] + + +def shell_info(tools, **kwargs): + """Return the shell style output of t.info parsed into a dictionary""" + return gs.parse_key_val(tools.t_info(flags="g", **kwargs).text) + + +def stable_items(info): + """Return the parsed shell output without the keys which cannot be compared""" + return {key: value for key, value in info.items() if key not in VOLATILE_KEYS} + + +def test_shell_keys(temporal_data): + """Shell output has the expected keys in the expected order""" + tools = Tools(session=temporal_data.session) + info = shell_info(tools, type="str3ds", input="volume_abs1") + + assert list(info.keys()) == STR3DS_SHELL_KEYS + + +def test_shell_values(temporal_data): + """Shell output reports the metadata of the dataset""" + tools = Tools(session=temporal_data.session) + info = shell_info(tools, type="str3ds", input="volume_abs1") + + assert stable_items(info) == { + "id": f"volume_abs1@{temporal_data.mapset}", + "name": "volume_abs1", + "mapset": temporal_data.mapset, + "temporal_type": "absolute", + "semantic_type": "mean", + "start_time": "'2001-01-01 00:00:00'", + "end_time": "'2001-03-01 00:00:00'", + "granularity": "'1 month'", + "map_time": "interval", + "north": "80.0", + "south": "0.0", + "east": "120.0", + "west": "0.0", + "top": "50.0", + "bottom": "0.0", + "tbres_min": "10.0", + "tbres_max": "10.0", + "nsres_min": "10.0", + "nsres_max": "10.0", + "ewres_min": "10.0", + "ewres_max": "10.0", + "min_min": "1.0", + "min_max": "2.0", + "max_min": "1.0", + "max_max": "2.0", + "aggregation_type": "None", + "number_of_maps": "2", + } + assert info["raster3d_register"].startswith("raster3d_map_register_") + + +def test_plain_info(temporal_data): + """Plain output reports the metadata in a human readable table""" + tools = Tools(session=temporal_data.session) + text = tools.t_info(type="str3ds", input="volume_abs1").text + + assert "Space Time 3D Raster Dataset" in text + assert f"Id: ........................ volume_abs1@{temporal_data.mapset}" in text + assert "Granularity:................ 1 month" in text + assert "Number of registered maps:.. 2" in text + assert "Title:" in text + assert "Command history:" in text + + +def test_raster3d_map_shell(temporal_data): + """Shell output for a registered 3D raster map reports the map metadata""" + tools = Tools(session=temporal_data.session) + info = shell_info(tools, type="raster_3d", input="vol_1") + + assert list(info.keys()) == RASTER3D_SHELL_KEYS + assert stable_items(info) == { + "id": f"vol_1@{temporal_data.mapset}", + "name": "vol_1", + "mapset": temporal_data.mapset, + "temporal_type": "absolute", + "start_time": "'2001-01-01 00:00:00'", + "end_time": "'2001-02-01 00:00:00'", + "north": "80.0", + "south": "0.0", + "east": "120.0", + "west": "0.0", + "top": "50.0", + "bottom": "0.0", + "datatype": "DCELL", + "cols": "12", + "rows": "8", + "number_of_cells": "480", + "nsres": "10.0", + "ewres": "10.0", + "min": "1.0", + "max": "1.0", + "depths": "5", + "tbres": "10.0", + "registered_datasets": f"volume_abs1@{temporal_data.mapset}", + } + + +def test_raster3d_map_plain(temporal_data): + """Plain output for a registered 3D raster map reports the map metadata""" + tools = Tools(session=temporal_data.session) + text = tools.t_info(type="raster_3d", input="vol_1").text + + assert "3D Raster Dataset" in text + assert "Datatype:................... DCELL" in text + assert "Number of columns:.......... 12" in text + assert "Number of rows:............. 8" in text + assert "Number of cells:............ 480" in text + assert f"Registered datasets ........ volume_abs1@{temporal_data.mapset}" in text diff --git a/temporal/t.info/tests/test_t_info_strds.py b/temporal/t.info/tests/test_t_info_strds.py new file mode 100644 index 00000000000..819db49f732 --- /dev/null +++ b/temporal/t.info/tests/test_t_info_strds.py @@ -0,0 +1,302 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +"""Test t.info with space time raster datasets and raster maps + +The expected values capture the output of t.info as it is now, so that later +changes to the interface can be checked against the established behavior. +""" + +import pytest + +import grass.script as gs +from grass.tools import Tools +from grass.tools.session_tools import ToolError + +# Keys whose values differ between runs, systems, or users. +VOLATILE_KEYS = ("creation_time", "modification_time", "creator", "raster_register") + +STRDS_SHELL_KEYS = [ + "id", + "name", + "mapset", + "creator", + "temporal_type", + "creation_time", + "modification_time", + "semantic_type", + "start_time", + "end_time", + "granularity", + "map_time", + "north", + "south", + "east", + "west", + "top", + "bottom", + "raster_register", + "nsres_min", + "nsres_max", + "ewres_min", + "ewres_max", + "min_min", + "min_max", + "max_min", + "max_max", + "aggregation_type", + "number_of_semantic_labels", + "semantic_labels", + "number_of_maps", +] + +RASTER_SHELL_KEYS = [ + "id", + "name", + "mapset", + "creator", + "temporal_type", + "creation_time", + "start_time", + "end_time", + "north", + "south", + "east", + "west", + "top", + "bottom", + "datatype", + "cols", + "rows", + "number_of_cells", + "nsres", + "ewres", + "min", + "max", + "semantic_label", + "registered_datasets", +] + + +def shell_info(tools, **kwargs): + """Return the shell style output of t.info parsed into a dictionary""" + return gs.parse_key_val(tools.t_info(flags="g", **kwargs).text) + + +def stable_items(info): + """Return the parsed shell output without the keys which cannot be compared""" + return {key: value for key, value in info.items() if key not in VOLATILE_KEYS} + + +def test_shell_keys(temporal_data): + """Shell output has the expected keys in the expected order""" + tools = Tools(session=temporal_data.session) + info = shell_info(tools, type="strds", input="precip_abs1") + + assert list(info.keys()) == STRDS_SHELL_KEYS + + +def test_shell_values(temporal_data): + """Shell output reports the metadata of the dataset""" + tools = Tools(session=temporal_data.session) + info = shell_info(tools, type="strds", input="precip_abs1") + + assert stable_items(info) == { + "id": f"precip_abs1@{temporal_data.mapset}", + "name": "precip_abs1", + "mapset": temporal_data.mapset, + "temporal_type": "absolute", + "semantic_type": "mean", + "start_time": "'2001-01-01 00:00:00'", + "end_time": "'2001-04-01 00:00:00'", + "granularity": "'1 month'", + "map_time": "interval", + "north": "80.0", + "south": "0.0", + "east": "120.0", + "west": "0.0", + "top": "0.0", + "bottom": "0.0", + "nsres_min": "10.0", + "nsres_max": "10.0", + "ewres_min": "10.0", + "ewres_max": "10.0", + "min_min": "1.0", + "min_max": "3.0", + "max_min": "1.0", + "max_max": "3.0", + "aggregation_type": "None", + "number_of_semantic_labels": "0", + "semantic_labels": "None", + "number_of_maps": "3", + } + assert info["creator"] + assert info["raster_register"].startswith("raster_map_register_") + + +def test_shell_quoting(temporal_data): + """Time values and granularity are quoted, other values are not""" + tools = Tools(session=temporal_data.session) + text = tools.t_info(flags="g", type="strds", input="precip_abs1").text + + assert "start_time='2001-01-01 00:00:00'" in text + assert "end_time='2001-04-01 00:00:00'" in text + assert "granularity='1 month'" in text + assert "north=80.0" in text + assert "map_time=interval" in text + assert "aggregation_type=None" in text + + +def test_plain_info(temporal_data): + """Plain output reports the metadata in a human readable table""" + tools = Tools(session=temporal_data.session) + text = tools.t_info(type="strds", input="precip_abs1").text + + assert "Space Time Raster Dataset" in text + assert f"Id: ........................ precip_abs1@{temporal_data.mapset}" in text + assert "Start time:................. 2001-01-01 00:00:00" in text + assert "Granularity:................ 1 month" in text + assert "Number of registered maps:.. 3" in text + + +def test_plain_reports_title_description_and_history(temporal_data): + """Title, description and command history are in the plain output only""" + tools = Tools(session=temporal_data.session) + plain = tools.t_info(type="strds", input="precip_abs1").text + shell = tools.t_info(flags="g", type="strds", input="precip_abs1").text + + assert "Title:" in plain + assert "Description:" in plain + assert "Command history:" in plain + assert 't.create type="strds"' in plain + + assert "Title:" not in shell + assert "Description:" not in shell + assert "t.create" not in shell + + +def test_history_flag(temporal_data): + """The -h flag reports title, description and the commands used""" + tools = Tools(session=temporal_data.session) + text = tools.t_info(flags="h", type="strds", input="precip_abs1").text + + assert "# Title:" in text + assert "# Description:" in text + assert "# Command history:" in text + assert 't.create type="strds"' in text + assert "t.register" in text + assert 'maps="prec_1,prec_2,prec_3"' in text + + +def test_history_flag_on_map(temporal_data): + """The -h flag on a map reports the plain map metadata""" + tools = Tools(session=temporal_data.session) + text = tools.t_info(flags="h", type="raster", input="prec_1").text + + assert "Raster Dataset" in text + assert "Command history:" not in text + + +def test_nonascii_title_and_description(temporal_data): + """Non-ASCII title and description are reported unchanged""" + tools = Tools(session=temporal_data.session) + text = tools.t_info(type="strds", input="accent_ds").text + + assert "Précipitation totale" in text + assert "Précipitation mensuelle" in text + + +def test_dataset_without_maps(temporal_data): + """A dataset without registered maps reports None for the empty fields""" + tools = Tools(session=temporal_data.session) + info = shell_info(tools, type="strds", input="empty_ds") + + assert list(info.keys()) == STRDS_SHELL_KEYS + assert info["start_time"] == "'None'" + assert info["end_time"] == "'None'" + assert info["granularity"] == "'None'" + assert info["map_time"] == "None" + assert info["north"] == "None" + assert info["min_min"] == "None" + assert info["number_of_maps"] == "None" + + +def test_relative_time_dataset(temporal_data): + """A relative time dataset reports its time unit""" + tools = Tools(session=temporal_data.session) + info = shell_info(tools, type="strds", input="precip_rel1") + + assert info["temporal_type"] == "relative" + assert info["unit"] == "days" + assert info["start_time"] == "'1'" + assert info["end_time"] == "'3'" + assert info["granularity"] == "1" + assert info["number_of_maps"] == "2" + + +def test_raster_map_shell(temporal_data): + """Shell output for a registered raster map reports the map metadata""" + tools = Tools(session=temporal_data.session) + info = shell_info(tools, type="raster", input="prec_1") + + assert list(info.keys()) == RASTER_SHELL_KEYS + assert stable_items(info) == { + "id": f"prec_1@{temporal_data.mapset}", + "name": "prec_1", + "mapset": temporal_data.mapset, + "temporal_type": "absolute", + "start_time": "'2001-01-01 00:00:00'", + "end_time": "'2001-02-01 00:00:00'", + "north": "80.0", + "south": "0.0", + "east": "120.0", + "west": "0.0", + "top": "0.0", + "bottom": "0.0", + "datatype": "CELL", + "cols": "12", + "rows": "8", + "number_of_cells": "96", + "nsres": "10.0", + "ewres": "10.0", + "min": "1.0", + "max": "1.0", + "semantic_label": "None", + "registered_datasets": f"precip_abs1@{temporal_data.mapset}", + } + + +def test_raster_map_plain(temporal_data): + """Plain output for a registered raster map reports the map metadata""" + tools = Tools(session=temporal_data.session) + text = tools.t_info(type="raster", input="prec_1").text + + assert "Raster Dataset" in text + assert "Datatype:................... CELL" in text + assert "Number of columns:.......... 12" in text + assert "Number of rows:............. 8" in text + assert "Number of cells:............ 96" in text + assert f"Registered datasets ........ precip_abs1@{temporal_data.mapset}" in text + + +def test_map_registered_in_two_datasets(temporal_data): + """A map registered in more than one dataset reports all of them""" + tools = Tools(session=temporal_data.session) + info = shell_info(tools, type="raster", input="prec_2") + + mapset = temporal_data.mapset + assert info["registered_datasets"] == f"precip_abs1@{mapset},precip_abs2@{mapset}" + + +def test_dataset_not_found(temporal_data): + """A dataset which is not in the temporal database is an error""" + tools = Tools(session=temporal_data.session) + + with pytest.raises(ToolError, match="not found in"): + tools.t_info(type="strds", input="does_not_exist") + + +def test_input_is_required(temporal_data): + """Without input and without the -d flag, t.info fails""" + tools = Tools(session=temporal_data.session) + + with pytest.raises(ToolError): + tools.t_info(type="strds") diff --git a/temporal/t.info/tests/test_t_info_stvds.py b/temporal/t.info/tests/test_t_info_stvds.py new file mode 100644 index 00000000000..2cc3476a135 --- /dev/null +++ b/temporal/t.info/tests/test_t_info_stvds.py @@ -0,0 +1,195 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +"""Test t.info with space time vector datasets and vector maps + +The expected values capture the output of t.info as it is now, so that later +changes to the interface can be checked against the established behavior. +""" + +import grass.script as gs +from grass.tools import Tools + +# Keys whose values differ between runs, systems, or users. The spatial extent +# of the randomly generated vector maps is not fixed either. +VOLATILE_KEYS = ( + "creation_time", + "modification_time", + "creator", + "vector_register", + "north", + "south", + "east", + "west", +) + +STVDS_SHELL_KEYS = [ + "id", + "name", + "mapset", + "creator", + "temporal_type", + "creation_time", + "modification_time", + "semantic_type", + "start_time", + "end_time", + "granularity", + "map_time", + "north", + "south", + "east", + "west", + "top", + "bottom", + "vector_register", + "points", + "lines", + "boundaries", + "centroids", + "faces", + "kernels", + "primitives", + "nodes", + "areas", + "islands", + "holes", + "volumes", + "number_of_maps", +] + +VECTOR_SHELL_KEYS = [ + "id", + "name", + "mapset", + "creator", + "temporal_type", + "creation_time", + "start_time", + "end_time", + "north", + "south", + "east", + "west", + "top", + "bottom", + "is_3d", + "points", + "lines", + "boundaries", + "centroids", + "faces", + "kernels", + "primitives", + "nodes", + "areas", + "islands", + "holes", + "volumes", + "registered_datasets", +] + + +def shell_info(tools, **kwargs): + """Return the shell style output of t.info parsed into a dictionary""" + return gs.parse_key_val(tools.t_info(flags="g", **kwargs).text) + + +def stable_items(info): + """Return the parsed shell output without the keys which cannot be compared""" + return {key: value for key, value in info.items() if key not in VOLATILE_KEYS} + + +def test_shell_keys(temporal_data): + """Shell output has the expected keys in the expected order""" + tools = Tools(session=temporal_data.session) + info = shell_info(tools, type="stvds", input="vect_abs1") + + assert list(info.keys()) == STVDS_SHELL_KEYS + + +def test_shell_values(temporal_data): + """Shell output reports the metadata of the dataset""" + tools = Tools(session=temporal_data.session) + info = shell_info(tools, type="stvds", input="vect_abs1") + + assert stable_items(info) == { + "id": f"vect_abs1@{temporal_data.mapset}", + "name": "vect_abs1", + "mapset": temporal_data.mapset, + "temporal_type": "absolute", + "semantic_type": "mean", + "start_time": "'2001-01-01 00:00:00'", + "end_time": "'2001-03-01 00:00:00'", + "granularity": "'1 month'", + "map_time": "interval", + "top": "0.0", + "bottom": "0.0", + "points": "40", + "lines": "0", + "boundaries": "0", + "centroids": "0", + "faces": "0", + "kernels": "0", + "primitives": "40", + "nodes": "0", + "areas": "0", + "islands": "0", + "holes": "0", + "volumes": "0", + "number_of_maps": "2", + } + assert info["vector_register"].startswith("vector_map_register_") + + +def test_plain_info(temporal_data): + """Plain output reports the metadata in a human readable table""" + tools = Tools(session=temporal_data.session) + text = tools.t_info(type="stvds", input="vect_abs1").text + + assert "Space Time Vector Dataset" in text + assert f"Id: ........................ vect_abs1@{temporal_data.mapset}" in text + assert "Granularity:................ 1 month" in text + assert "Number of registered maps:.. 2" in text + assert "Title:" in text + assert "Command history:" in text + + +def test_vector_map_shell(temporal_data): + """Shell output for a registered vector map reports the map metadata""" + tools = Tools(session=temporal_data.session) + info = shell_info(tools, type="vector", input="vect_1") + + assert list(info.keys()) == VECTOR_SHELL_KEYS + assert stable_items(info) == { + "id": f"vect_1@{temporal_data.mapset}", + "name": "vect_1", + "mapset": temporal_data.mapset, + "temporal_type": "absolute", + "start_time": "'2001-01-01 00:00:00'", + "end_time": "'2001-02-01 00:00:00'", + "top": "0.0", + "bottom": "0.0", + "is_3d": "0", + "points": "20", + "lines": "0", + "boundaries": "0", + "centroids": "0", + "faces": "0", + "kernels": "0", + "primitives": "20", + "nodes": "0", + "areas": "0", + "islands": "0", + "holes": "0", + "volumes": "0", + "registered_datasets": f"vect_abs1@{temporal_data.mapset}", + } + + +def test_vector_map_plain(temporal_data): + """Plain output for a registered vector map reports the map metadata""" + tools = Tools(session=temporal_data.session) + text = tools.t_info(type="vector", input="vect_1").text + + assert "Vector Dataset" in text + assert "Number of points ........... 20" in text + assert f"Registered datasets ........ vect_abs1@{temporal_data.mapset}" in text diff --git a/temporal/t.info/testsuite/test.t.info.sh b/temporal/t.info/testsuite/test.t.info.sh deleted file mode 100755 index f3b0396e340..00000000000 --- a/temporal/t.info/testsuite/test.t.info.sh +++ /dev/null @@ -1,84 +0,0 @@ -#!/bin/sh -# Test the info output - -# We need to set a specific region in the -# @preprocess step of this test. -g.region s=0 n=80 w=0 e=120 b=0 t=50 res=10 res3=10 -p3 - -export GRASS_OVERWRITE=1 - -r.mapcalc expr="prec_1 = rand(0, 550)" -r.mapcalc expr="prec_2 = rand(0, 450)" - -r3.mapcalc expr="prec_1 = rand(0, 120)" -r3.mapcalc expr="prec_2 = rand(0, 320)" - -v.random -z output=lidar_abs_1 n=20 zmin=0 zmax=100 column=height -v.random -z output=lidar_abs_2 n=20 zmin=0 zmax=100 column=height - -# The first @test -t.info -d -t.info -dg - -t.create type=strds temporaltype=absolute output=precip_abs1 title="A test" descr="A test" -t.create type=strds temporaltype=absolute output=precip_abs2 title="A test" descr="A test" - -t.create type=str3ds temporaltype=absolute output=precip_abs1 title="A test" descr="A test" -t.create type=str3ds temporaltype=absolute output=precip_abs2 title="A test" descr="A test" - -t.create type=stvds temporaltype=absolute output=lidar_abs_ds1 title="A test" descr="A test" -t.create type=stvds temporaltype=absolute output=lidar_abs_ds2 title="A test" descr="A test" - -LC="fr_BE@UTF-8" t.create output=pluies_nc semantictype=sum title="précipitation_mois" description="Précipitation totale mensuelle NC" - -t.register type=raster -i input=precip_abs1 maps=prec_1,prec_2 start="2001-01-01" increment="20 years" -t.info type=strds input=precip_abs1 -t.info -g type=strds input=precip_abs1 - -t.register type=raster -i input=precip_abs2 maps=prec_1,prec_2 start="2001-01-01" increment="20 years" -t.info type=strds input=precip_abs2 -t.info -g type=strds input=precip_abs2 -t.info -h type=strds input=precip_abs2 -t.info type=raster input=prec_1 -t.info -g type=raster input=prec_1 -t.info type=raster input=prec_2 -t.info -g type=raster input=prec_2 - - -t.register type=raster_3d -i input=precip_abs1 maps=prec_1,prec_2 start="2001-01-01" increment="20 years" -t.info type=str3ds input=precip_abs1 -t.info -g type=str3ds input=precip_abs1 - -t.register type=raster_3d -i input=precip_abs2 maps=prec_1,prec_2 start="2001-01-01" increment="20 years" -t.info type=str3ds input=precip_abs2 -t.info -g type=str3ds input=precip_abs2 -t.info -h type=str3ds input=precip_abs2 -t.info type=raster_3d input=prec_1 -t.info -g type=raster_3d input=prec_1 -t.info type=raster_3d input=prec_2 -t.info -g type=raster_3d input=prec_2 - -t.register type=vector --v -i input=lidar_abs_ds1 maps=lidar_abs_1,lidar_abs_2 start="2001-01-01" increment="20 years" -t.info type=stvds input=lidar_abs_ds1 -t.info -g type=stvds input=lidar_abs_ds1 - -t.register type=vector --v -i input=lidar_abs_ds2 maps=lidar_abs_1,lidar_abs_2 start="2001-01-01" increment="20 years" -t.info type=stvds input=lidar_abs_ds2 -t.info -g type=stvds input=lidar_abs_ds2 -t.info type=vector input=lidar_abs_1 -t.info -g type=vector input=lidar_abs_1 -t.info type=vector input=lidar_abs_2 -t.info -g type=vector input=lidar_abs_2 - - -t.register type=raster -i input=pluies_nc maps=prec_1,prec_2 start="2001-01-01" increment="20 years" -t.info type=strds input=pluies_nc - -t.unregister type=vector maps=lidar_abs_1,lidar_abs_2 -t.remove type=stvds input=lidar_abs_ds1,lidar_abs_ds2 - -t.unregister type=raster maps=prec_1,prec_2,pluies_nc -t.remove type=strds input=precip_abs1,precip_abs2 - -t.unregister type=raster_3d maps=prec_1,prec_2 -t.remove type=str3ds input=precip_abs1,precip_abs2 From 37c05c4ebe8a60efa27832a5992c605ec0ed2e37 Mon Sep 17 00:00:00 2001 From: Anna Petrasova Date: Tue, 14 Jul 2026 03:46:17 -0400 Subject: [PATCH 2/2] t.info: Make command history assertions platform independent The temporal history records the command name with the .py extension on Windows, so assert the name and its parameters separately. --- temporal/t.info/tests/test_t_info_strds.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/temporal/t.info/tests/test_t_info_strds.py b/temporal/t.info/tests/test_t_info_strds.py index 819db49f732..d0cb6518b40 100644 --- a/temporal/t.info/tests/test_t_info_strds.py +++ b/temporal/t.info/tests/test_t_info_strds.py @@ -166,7 +166,11 @@ def test_plain_reports_title_description_and_history(temporal_data): assert "Title:" in plain assert "Description:" in plain assert "Command history:" in plain - assert 't.create type="strds"' in plain + # The history records the command name as t.create.py on Windows, + # so the name and its parameters are checked separately. + assert "t.create" in plain + assert 'type="strds"' in plain + assert 'title="A test"' in plain assert "Title:" not in shell assert "Description:" not in shell @@ -181,7 +185,8 @@ def test_history_flag(temporal_data): assert "# Title:" in text assert "# Description:" in text assert "# Command history:" in text - assert 't.create type="strds"' in text + assert "t.create" in text + assert 'output="precip_abs1"' in text assert "t.register" in text assert 'maps="prec_1,prec_2,prec_3"' in text