Skip to content

Commit 80a63d2

Browse files
committed
Split tests related to virtualfiles into multiple test files
1 parent c2e429c commit 80a63d2

7 files changed

+590
-513
lines changed
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
"""
2+
Test the Session.inquire_virtualfile method.
3+
"""
4+
5+
from pygmt import clib
6+
7+
8+
def test_inquire_virtualfile():
9+
"""
10+
Test that the inquire_virtualfile method returns the correct family.
11+
12+
Currently, only output virtual files are tested.
13+
"""
14+
with clib.Session() as lib:
15+
for family in [
16+
"GMT_IS_DATASET",
17+
"GMT_IS_DATASET|GMT_VIA_MATRIX",
18+
"GMT_IS_DATASET|GMT_VIA_VECTOR",
19+
]:
20+
with lib.open_virtualfile(
21+
family, "GMT_IS_PLP", "GMT_OUT|GMT_IS_REFERENCE", None
22+
) as vfile:
23+
assert lib.inquire_virtualfile(vfile) == lib["GMT_IS_DATASET"]
24+
25+
for family, geometry in [
26+
("GMT_IS_GRID", "GMT_IS_SURFACE"),
27+
("GMT_IS_IMAGE", "GMT_IS_SURFACE"),
28+
("GMT_IS_CUBE", "GMT_IS_VOLUME"),
29+
("GMT_IS_PALETTE", "GMT_IS_NONE"),
30+
("GMT_IS_POSTSCRIPT", "GMT_IS_NONE"),
31+
]:
32+
with lib.open_virtualfile(family, geometry, "GMT_OUT", None) as vfile:
33+
assert lib.inquire_virtualfile(vfile) == lib[family]
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
"""
2+
Test the Session.open_virtualfile method.
3+
"""
4+
5+
from importlib.util import find_spec
6+
from pathlib import Path
7+
8+
import numpy as np
9+
import pytest
10+
from pygmt import clib
11+
from pygmt.exceptions import GMTCLibError, GMTInvalidInput
12+
from pygmt.helpers import GMTTempFile
13+
from pygmt.tests.test_clib import mock
14+
15+
POINTS_DATA = Path(__file__).parent / "data" / "points.txt"
16+
17+
18+
@pytest.fixture(scope="module", name="data")
19+
def fixture_data():
20+
"""
21+
Load the point data from the test file.
22+
"""
23+
return np.loadtxt(POINTS_DATA)
24+
25+
26+
@pytest.fixture(scope="module", name="dtypes")
27+
def fixture_dtypes():
28+
"""
29+
List of supported numpy dtypes.
30+
"""
31+
return "int8 int16 int32 int64 uint8 uint16 uint32 uint64 float32 float64".split()
32+
33+
34+
@pytest.fixture(scope="module", name="dtypes_pandas")
35+
def fixture_dtypes_pandas(dtypes):
36+
"""
37+
List of supported pandas dtypes.
38+
"""
39+
dtypes_pandas = dtypes.copy()
40+
41+
if find_spec("pyarrow") is not None:
42+
dtypes_pandas.extend([f"{dtype}[pyarrow]" for dtype in dtypes_pandas])
43+
44+
return tuple(dtypes_pandas)
45+
46+
47+
@pytest.mark.benchmark
48+
def test_open_virtualfile(dtypes):
49+
"""
50+
Test passing in data via a virtual file with a Dataset.
51+
"""
52+
shape = (5, 3)
53+
for dtype in dtypes:
54+
with clib.Session() as lib:
55+
family = "GMT_IS_DATASET|GMT_VIA_MATRIX"
56+
geometry = "GMT_IS_POINT"
57+
dataset = lib.create_data(
58+
family=family,
59+
geometry=geometry,
60+
mode="GMT_CONTAINER_ONLY",
61+
dim=[shape[1], shape[0], 1, 0], # columns, rows, layers, dtype
62+
)
63+
data = np.arange(shape[0] * shape[1], dtype=dtype).reshape(shape)
64+
lib.put_matrix(dataset, matrix=data)
65+
# Add the dataset to a virtual file and pass it along to gmt info
66+
vfargs = (family, geometry, "GMT_IN|GMT_IS_REFERENCE", dataset)
67+
with lib.open_virtualfile(*vfargs) as vfile:
68+
with GMTTempFile() as outfile:
69+
lib.call_module("info", [vfile, f"->{outfile.name}"])
70+
output = outfile.read(keep_tabs=True)
71+
bounds = "\t".join([f"<{col.min():.0f}/{col.max():.0f}>" for col in data.T])
72+
expected = f"<matrix memory>: N = {shape[0]}\t{bounds}\n"
73+
assert output == expected
74+
75+
76+
def test_open_virtualfile_fails():
77+
"""
78+
Check that opening and closing virtual files raises an exception for non- zero
79+
return codes.
80+
"""
81+
vfargs = (
82+
"GMT_IS_DATASET|GMT_VIA_MATRIX",
83+
"GMT_IS_POINT",
84+
"GMT_IN|GMT_IS_REFERENCE",
85+
None,
86+
)
87+
88+
# Mock Open_VirtualFile to test the status check when entering the context.
89+
# If the exception is raised, the code won't get to the closing of the
90+
# virtual file.
91+
with clib.Session() as lib, mock(lib, "GMT_Open_VirtualFile", returns=1):
92+
with pytest.raises(GMTCLibError):
93+
with lib.open_virtualfile(*vfargs):
94+
pass
95+
96+
# Test the status check when closing the virtual file
97+
# Mock the opening to return 0 (success) so that we don't open a file that
98+
# we won't close later.
99+
with (
100+
clib.Session() as lib,
101+
mock(lib, "GMT_Open_VirtualFile", returns=0),
102+
mock(lib, "GMT_Close_VirtualFile", returns=1),
103+
):
104+
with pytest.raises(GMTCLibError):
105+
with lib.open_virtualfile(*vfargs):
106+
pass
107+
108+
109+
def test_open_virtualfile_bad_direction():
110+
"""
111+
Test passing an invalid direction argument.
112+
"""
113+
with clib.Session() as lib:
114+
vfargs = (
115+
"GMT_IS_DATASET|GMT_VIA_MATRIX",
116+
"GMT_IS_POINT",
117+
"GMT_IS_GRID", # The invalid direction argument
118+
0,
119+
)
120+
with pytest.raises(GMTInvalidInput):
121+
with lib.open_virtualfile(*vfargs):
122+
pass
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
"""
2+
Test the Session.virtualfile_from_matrix method.
3+
"""
4+
5+
import numpy as np
6+
import pytest
7+
from pygmt import clib
8+
from pygmt.helpers import GMTTempFile
9+
10+
11+
@pytest.fixture(scope="module", name="dtypes")
12+
def fixture_dtypes():
13+
"""
14+
List of supported numpy dtypes.
15+
"""
16+
return "int8 int16 int32 int64 uint8 uint16 uint32 uint64 float32 float64".split()
17+
18+
19+
@pytest.mark.benchmark
20+
def test_virtualfile_from_matrix(dtypes):
21+
"""
22+
Test transforming a matrix to virtual file dataset.
23+
"""
24+
shape = (7, 5)
25+
for dtype in dtypes:
26+
data = np.arange(shape[0] * shape[1], dtype=dtype).reshape(shape)
27+
with clib.Session() as lib:
28+
with lib.virtualfile_from_matrix(data) as vfile:
29+
with GMTTempFile() as outfile:
30+
lib.call_module("info", [vfile, f"->{outfile.name}"])
31+
output = outfile.read(keep_tabs=True)
32+
bounds = "\t".join([f"<{col.min():.0f}/{col.max():.0f}>" for col in data.T])
33+
expected = f"<matrix memory>: N = {shape[0]}\t{bounds}\n"
34+
assert output == expected
35+
36+
37+
def test_virtualfile_from_matrix_slice(dtypes):
38+
"""
39+
Test transforming a slice of a larger array to virtual file dataset.
40+
"""
41+
shape = (10, 6)
42+
for dtype in dtypes:
43+
full_data = np.arange(shape[0] * shape[1], dtype=dtype).reshape(shape)
44+
rows = 5
45+
cols = 3
46+
data = full_data[:rows, :cols]
47+
with clib.Session() as lib:
48+
with lib.virtualfile_from_matrix(data) as vfile:
49+
with GMTTempFile() as outfile:
50+
lib.call_module("info", [vfile, f"->{outfile.name}"])
51+
output = outfile.read(keep_tabs=True)
52+
bounds = "\t".join([f"<{col.min():.0f}/{col.max():.0f}>" for col in data.T])
53+
expected = f"<matrix memory>: N = {rows}\t{bounds}\n"
54+
assert output == expected
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
"""
2+
Test the Session.virtualfile_from_stringio method.
3+
"""
4+
5+
import io
6+
7+
import numpy as np
8+
from pygmt import clib
9+
10+
11+
def _stringio_to_dataset(data: io.StringIO):
12+
"""
13+
A helper function for check the virtualfile_from_stringio method.
14+
15+
The function does the following:
16+
17+
1. Creates a virtual file from the input StringIO object.
18+
2. Pass the virtual file to the ``read`` module, which reads the virtual file and
19+
writes it to another virtual file.
20+
3. Reads the output virtual file as a GMT_DATASET object.
21+
4. Extracts the header and the trailing text from the dataset and returns it as a
22+
string.
23+
"""
24+
with clib.Session() as lib:
25+
with (
26+
lib.virtualfile_from_stringio(data) as vintbl,
27+
lib.virtualfile_out(kind="dataset") as vouttbl,
28+
):
29+
lib.call_module("read", args=[vintbl, vouttbl, "-Td"])
30+
ds = lib.read_virtualfile(vouttbl, kind="dataset").contents
31+
32+
output = []
33+
table = ds.table[0].contents
34+
for segment in table.segment[: table.n_segments]:
35+
seg = segment.contents
36+
output.append(f"> {seg.header.decode()}" if seg.header else ">")
37+
output.extend(np.char.decode(seg.text[: seg.n_rows]))
38+
return "\n".join(output) + "\n"
39+
40+
41+
def test_virtualfile_from_stringio():
42+
"""
43+
Test the virtualfile_from_stringio method.
44+
"""
45+
data = io.StringIO(
46+
"# Comment\n"
47+
"H 24p Legend\n"
48+
"N 2\n"
49+
"S 0.1i c 0.15i p300/12 0.25p 0.3i My circle\n"
50+
)
51+
expected = (
52+
">\n" "H 24p Legend\n" "N 2\n" "S 0.1i c 0.15i p300/12 0.25p 0.3i My circle\n"
53+
)
54+
assert _stringio_to_dataset(data) == expected
55+
56+
57+
def test_one_segment():
58+
"""
59+
Test the virtualfile_from_stringio method with one segment.
60+
"""
61+
data = io.StringIO(
62+
"# Comment\n"
63+
"> Segment 1\n"
64+
"1 2 3 ABC\n"
65+
"4 5 DE\n"
66+
"6 7 8 9 FGHIJK LMN OPQ\n"
67+
"RSTUVWXYZ\n"
68+
)
69+
expected = (
70+
"> Segment 1\n"
71+
"1 2 3 ABC\n"
72+
"4 5 DE\n"
73+
"6 7 8 9 FGHIJK LMN OPQ\n"
74+
"RSTUVWXYZ\n"
75+
)
76+
assert _stringio_to_dataset(data) == expected
77+
78+
79+
def test_multiple_segments():
80+
"""
81+
Test the virtualfile_from_stringio method with multiple segments.
82+
"""
83+
data = io.StringIO(
84+
"# Comment line 1\n"
85+
"# Comment line 2\n"
86+
"> Segment 1\n"
87+
"1 2 3 ABC\n"
88+
"4 5 DE\n"
89+
"6 7 8 9 FG\n"
90+
"# Comment line 3\n"
91+
"> Segment 2\n"
92+
"1 2 3 ABC\n"
93+
"4 5 DE\n"
94+
"6 7 8 9 FG\n"
95+
)
96+
expected = (
97+
"> Segment 1\n"
98+
"1 2 3 ABC\n"
99+
"4 5 DE\n"
100+
"6 7 8 9 FG\n"
101+
"> Segment 2\n"
102+
"1 2 3 ABC\n"
103+
"4 5 DE\n"
104+
"6 7 8 9 FG\n"
105+
)
106+
assert _stringio_to_dataset(data) == expected

0 commit comments

Comments
 (0)