Skip to content

Commit e493473

Browse files
committed
Split tests related to virtualfiles into multiple test files (#3512)
1 parent f60d407 commit e493473

6 files changed

+472
-395
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: 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
Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
"""
2+
Test the Session.virtualfile_from_vectors method.
3+
"""
4+
5+
from importlib.util import find_spec
6+
7+
import numpy as np
8+
import pandas as pd
9+
import pytest
10+
from pygmt import clib
11+
from pygmt.exceptions import GMTInvalidInput
12+
from pygmt.helpers import GMTTempFile
13+
14+
15+
@pytest.fixture(scope="module", name="dtypes")
16+
def fixture_dtypes():
17+
"""
18+
List of supported numpy dtypes.
19+
"""
20+
return "int8 int16 int32 int64 uint8 uint16 uint32 uint64 float32 float64".split()
21+
22+
23+
@pytest.fixture(scope="module", name="dtypes_pandas")
24+
def fixture_dtypes_pandas(dtypes):
25+
"""
26+
List of supported pandas dtypes.
27+
"""
28+
dtypes_pandas = dtypes.copy()
29+
30+
if find_spec("pyarrow") is not None:
31+
dtypes_pandas.extend([f"{dtype}[pyarrow]" for dtype in dtypes_pandas])
32+
33+
return tuple(dtypes_pandas)
34+
35+
36+
@pytest.mark.benchmark
37+
def test_virtualfile_from_vectors(dtypes):
38+
"""
39+
Test the automation for transforming vectors to virtual file dataset.
40+
"""
41+
size = 10
42+
for dtype in dtypes:
43+
x = np.arange(size, dtype=dtype)
44+
y = np.arange(size, size * 2, 1, dtype=dtype)
45+
z = np.arange(size * 2, size * 3, 1, dtype=dtype)
46+
with clib.Session() as lib:
47+
with lib.virtualfile_from_vectors(x, y, z) as vfile:
48+
with GMTTempFile() as outfile:
49+
lib.call_module("info", [vfile, f"->{outfile.name}"])
50+
output = outfile.read(keep_tabs=True)
51+
bounds = "\t".join([f"<{i.min():.0f}/{i.max():.0f}>" for i in (x, y, z)])
52+
expected = f"<vector memory>: N = {size}\t{bounds}\n"
53+
assert output == expected
54+
55+
56+
@pytest.mark.benchmark
57+
@pytest.mark.parametrize("dtype", [str, object])
58+
def test_virtualfile_from_vectors_one_string_or_object_column(dtype):
59+
"""
60+
Test passing in one column with string or object dtype into virtual file dataset.
61+
"""
62+
size = 5
63+
x = np.arange(size, dtype=np.int32)
64+
y = np.arange(size, size * 2, 1, dtype=np.int32)
65+
strings = np.array(["a", "bc", "defg", "hijklmn", "opqrst"], dtype=dtype)
66+
with clib.Session() as lib:
67+
with lib.virtualfile_from_vectors(x, y, strings) as vfile:
68+
with GMTTempFile() as outfile:
69+
lib.call_module("convert", [vfile, f"->{outfile.name}"])
70+
output = outfile.read(keep_tabs=True)
71+
expected = "".join(
72+
f"{i}\t{j}\t{k}\n" for i, j, k in zip(x, y, strings, strict=True)
73+
)
74+
assert output == expected
75+
76+
77+
@pytest.mark.parametrize("dtype", [str, object])
78+
def test_virtualfile_from_vectors_two_string_or_object_columns(dtype):
79+
"""
80+
Test passing in two columns of string or object dtype into virtual file dataset.
81+
"""
82+
size = 5
83+
x = np.arange(size, dtype=np.int32)
84+
y = np.arange(size, size * 2, 1, dtype=np.int32)
85+
# Catch bug in https://github.com/GenericMappingTools/pygmt/pull/2719
86+
strings1 = np.array(["a", "bc", "def", "ghij", "klmnolooong"], dtype=dtype)
87+
strings2 = np.array(["pqrst", "uvwx", "yz!", "@#", "$"], dtype=dtype)
88+
with clib.Session() as lib:
89+
with lib.virtualfile_from_vectors(x, y, strings1, strings2) as vfile:
90+
with GMTTempFile() as outfile:
91+
lib.call_module("convert", [vfile, f"->{outfile.name}"])
92+
output = outfile.read(keep_tabs=True)
93+
expected = "".join(
94+
f"{h}\t{i}\t{j} {k}\n"
95+
for h, i, j, k in zip(x, y, strings1, strings2, strict=True)
96+
)
97+
assert output == expected
98+
99+
100+
def test_virtualfile_from_vectors_transpose(dtypes):
101+
"""
102+
Test transforming matrix columns to virtual file dataset.
103+
"""
104+
shape = (7, 5)
105+
for dtype in dtypes:
106+
data = np.arange(shape[0] * shape[1], dtype=dtype).reshape(shape)
107+
with clib.Session() as lib:
108+
with lib.virtualfile_from_vectors(*data.T) as vfile:
109+
with GMTTempFile() as outfile:
110+
lib.call_module("info", [vfile, "-C", f"->{outfile.name}"])
111+
output = outfile.read(keep_tabs=True)
112+
bounds = "\t".join([f"{col.min():.0f}\t{col.max():.0f}" for col in data.T])
113+
expected = f"{bounds}\n"
114+
assert output == expected
115+
116+
117+
def test_virtualfile_from_vectors_diff_size():
118+
"""
119+
Test the function fails for arrays of different sizes.
120+
"""
121+
x = np.arange(5)
122+
y = np.arange(6)
123+
with clib.Session() as lib:
124+
with pytest.raises(GMTInvalidInput):
125+
with lib.virtualfile_from_vectors(x, y):
126+
pass
127+
128+
129+
def test_virtualfile_from_vectors_pandas(dtypes_pandas):
130+
"""
131+
Pass vectors to a dataset using pandas.Series, checking both numpy and pyarrow
132+
dtypes.
133+
"""
134+
size = 13
135+
136+
for dtype in dtypes_pandas:
137+
data = pd.DataFrame(
138+
data={
139+
"x": np.arange(size),
140+
"y": np.arange(size, size * 2, 1),
141+
"z": np.arange(size * 2, size * 3, 1),
142+
},
143+
dtype=dtype,
144+
)
145+
with clib.Session() as lib:
146+
with lib.virtualfile_from_vectors(data.x, data.y, data.z) as vfile:
147+
with GMTTempFile() as outfile:
148+
lib.call_module("info", [vfile, f"->{outfile.name}"])
149+
output = outfile.read(keep_tabs=True)
150+
bounds = "\t".join(
151+
[f"<{i.min():.0f}/{i.max():.0f}>" for i in (data.x, data.y, data.z)]
152+
)
153+
expected = f"<vector memory>: N = {size}\t{bounds}\n"
154+
assert output == expected
155+
156+
157+
def test_virtualfile_from_vectors_arraylike():
158+
"""
159+
Pass array-like vectors to a dataset.
160+
"""
161+
size = 13
162+
x = list(range(0, size, 1))
163+
y = tuple(range(size, size * 2, 1))
164+
z = range(size * 2, size * 3, 1)
165+
with clib.Session() as lib:
166+
with lib.virtualfile_from_vectors(x, y, z) as vfile:
167+
with GMTTempFile() as outfile:
168+
lib.call_module("info", [vfile, f"->{outfile.name}"])
169+
output = outfile.read(keep_tabs=True)
170+
bounds = "\t".join([f"<{min(i):.0f}/{max(i):.0f}>" for i in (x, y, z)])
171+
expected = f"<vector memory>: N = {size}\t{bounds}\n"
172+
assert output == expected

0 commit comments

Comments
 (0)