Skip to content

Commit 83de5f9

Browse files
committed
tests for docs_generator
1 parent b0cea21 commit 83de5f9

4 files changed

Lines changed: 156 additions & 4 deletions

File tree

‎paths_cli/compiling/_gendocs/README.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# _docgen
1+
# _gendocs
22

33
Tools for generating documentation for the tools import `paths_cli.compiling`.
44
Note that this entire directory is considered outside the API, so nothing in

‎paths_cli/compiling/_gendocs/docs_generator.py‎

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,16 @@
55

66
PARAMETER_RST = """* **{p.name}**{type_str} - {p.description}{required}\n"""
77

8+
# TODO: add templates here instead of adding up strings in the code
89

910
class DocsGenerator:
1011
"""This generates the RST to describe options for compile input files.
12+
13+
Parameters
14+
----------
15+
config : Dict[str, DocCategoryInfo]
16+
mapping of category name to DocCategoryInfo for that category;
17+
usually generated by :method:`.load_config`
1118
"""
1219

1320
parameter_template = PARAMETER_RST
@@ -16,6 +23,8 @@ def __init__(self, config):
1623
self.config = config
1724

1825
def format_parameter(self, parameter, type_str=None):
26+
"""Format a single :class:`.paths_cli.compiling.Parameter` in RST
27+
"""
1928
required = " (required)" if parameter.required else ""
2029
return self.parameter_template.format(
2130
p=parameter, type_str=type_str, required=required
@@ -40,9 +49,9 @@ def generate_category_rst(self, category_plugin, type_required=True):
4049
)
4150
return rst
4251

43-
def generate_plugin_rst(self, plugin, strategy_name,
52+
def generate_plugin_rst(self, plugin, category_name,
4453
type_required=True):
45-
rst_anchor = f".. _{strategy_name}{self._ANCHOR_SEP}{plugin.name}:"
54+
rst_anchor = f".. _{category_name}{self._ANCHOR_SEP}{plugin.name}:"
4655
rst = f"{rst_anchor}\n\n{plugin.name}\n{'-' * len(plugin.name)}\n\n"
4756
if plugin.description:
4857
rst += plugin.description + "\n\n"

‎paths_cli/compiling/gendocs.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
@click.command()
77
@click.argument("config_file")
88
@click.option("--stdout", type=bool, is_flag=True, default=False)
9-
def main(config_file, stdout): # -no-cov-
9+
def main(config_file, stdout):
1010
register_installed_plugins()
1111
config = load_config(config_file)
1212
generator = DocsGenerator(config)
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
import pytest
2+
from unittest import mock
3+
import io
4+
5+
from paths_cli.compiling._gendocs.docs_generator import *
6+
from paths_cli.compiling._gendocs import DocCategoryInfo
7+
8+
from paths_cli.compiling import Parameter, InstanceCompilerPlugin
9+
from paths_cli.compiling.core import CategoryCompiler
10+
11+
class TestDocsGenerator:
12+
def setup(self):
13+
self.required_parameter = Parameter(
14+
name="req_param",
15+
loader=None,
16+
json_type="string",
17+
description="req_desc",
18+
)
19+
self.optional_parameter = Parameter(
20+
name="opt_param",
21+
loader=None,
22+
json_type="string",
23+
description="opt_desc",
24+
default="foo",
25+
)
26+
self.plugin = InstanceCompilerPlugin(
27+
builder=None,
28+
parameters=[self.required_parameter,
29+
self.optional_parameter],
30+
name="object_plugin",
31+
description="object_plugin_desc",
32+
)
33+
self.category = CategoryCompiler(
34+
type_dispatch=None,
35+
label="category",
36+
)
37+
self.category.register_builder(self.plugin, self.plugin.name)
38+
39+
self.generator = DocsGenerator(
40+
{'category': DocCategoryInfo(header="Header",
41+
description="category_desc")}
42+
)
43+
self.param_strs = ['req_param', 'req_desc', 'opt_param', 'opt_desc']
44+
self.obj_plugin_strs = [
45+
".. _category--object_plugin:",
46+
"object_plugin\n-------------\n",
47+
"object_plugin_desc",
48+
]
49+
self.category_strs = [
50+
".. _compiling--category:",
51+
"Header\n======\n",
52+
".. contents:: :local:",
53+
"\ncategory_desc\n",
54+
]
55+
56+
@pytest.mark.parametrize('param_type', ['req', 'opt'])
57+
def test_format_parameter(self, param_type):
58+
# when formatting individual parameters, we should obtain the
59+
# correct RST string
60+
param = {'req': self.required_parameter,
61+
'opt': self.optional_parameter}[param_type]
62+
expected = { # NOTE: these can change without breaking API
63+
'req': "* **req_param** (string) - req_desc (required)\n",
64+
'opt': "* **opt_param** (string) - opt_desc\n",
65+
}[param_type]
66+
type_str = f" ({param.json_type})"
67+
result = self.generator.format_parameter(param, type_str=type_str)
68+
assert result == expected
69+
70+
@pytest.mark.parametrize('label', ['category', 'unknown'])
71+
def test_get_cat_info(self, label):
72+
# getting a DocCategoryInfo should either give the result from the
73+
# config or give the best-guess default
74+
category = {
75+
'category': self.category,
76+
'unknown': CategoryCompiler(type_dispatch=None,
77+
label="unknown"),
78+
}[label]
79+
expected = {
80+
'category': DocCategoryInfo(header="Header",
81+
description="category_desc"),
82+
'unknown': DocCategoryInfo(header='unknown')
83+
}[label]
84+
assert self.generator._get_cat_info(category) == expected
85+
86+
def test_generate_category_rst(self):
87+
# generating RST for a category plugin should include the expected
88+
# RST strings
89+
result = self.generator.generate_category_rst(self.category)
90+
expected_strs = (self.param_strs + self.obj_plugin_strs
91+
+ self.category_strs)
92+
for string in expected_strs:
93+
assert string in result
94+
95+
@pytest.mark.parametrize('type_required', [True, False])
96+
def test_generate_plugin_rst(self, type_required):
97+
# generating RST for an object plugin should include the expected
98+
# RST strings
99+
result = self.generator.generate_plugin_rst(
100+
self.plugin,
101+
self.category.label,
102+
type_required=type_required
103+
)
104+
for string in self.param_strs + self.obj_plugin_strs:
105+
assert string in result
106+
107+
type_str = "**type** - type identifier; must exactly match"
108+
if type_required:
109+
assert type_str in result
110+
else:
111+
assert type_str not in result
112+
113+
@pytest.mark.parametrize('inputs,expected', [
114+
("Collective Variables", "collective_variables.rst"),
115+
])
116+
def test_get_filename(self, inputs, expected):
117+
# filenames creating from a DocCategoryInfo (using _get_filename)
118+
# should give expected filenames
119+
cat_info = DocCategoryInfo(inputs)
120+
assert self.generator._get_filename(cat_info) == expected
121+
122+
@pytest.mark.parametrize('stdout', [True, False])
123+
def test_generate(self, stdout):
124+
base_loc = "paths_cli.compiling._gendocs.docs_generator."
125+
if stdout:
126+
mock_loc = base_loc + "sys.stdout"
127+
mock_obj = mock.Mock()
128+
else:
129+
mock_loc = base_loc + "open"
130+
mock_obj = mock.mock_open()
131+
132+
mock_generate_category = mock.Mock(return_value="foo")
133+
self.generator.generate_category_rst =mock_generate_category
134+
135+
with mock.patch(mock_loc, mock_obj):
136+
self.generator.generate([self.category], stdout=stdout)
137+
138+
mock_generate_category.assert_called_once()
139+
if not stdout:
140+
mock_obj.assert_called_once_with('header.rst', 'w')
141+
142+
write = mock_obj.write if stdout else mock_obj().write
143+
write.assert_called_once_with("foo")

0 commit comments

Comments
 (0)