Skip to content

Commit

Permalink
Merge pull request #16156 from aeslaughter/generate-16155
Browse files Browse the repository at this point in the history
Improve stub generate command and associated testing
  • Loading branch information
loganharbour authored Nov 23, 2020
2 parents 79d62e3 + a29f881 commit ee00f87
Show file tree
Hide file tree
Showing 12 changed files with 335 additions and 91 deletions.
36 changes: 36 additions & 0 deletions framework/doc/content/source/mesh/MeshGeneratorMesh.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# MeshGeneratorMesh

!alert! construction title=Undocumented Class
The MeshGeneratorMesh has not been documented. The content listed below should be used as a starting point for
documenting the class, which includes the typical automatic documentation associated with a
MooseObject; however, what is contained is ultimately determined by what is necessary to make the
documentation clear for users.

```markdown
# MeshGeneratorMesh

!syntax description /Mesh/MeshGeneratorMesh

## Overview

!! Replace these lines with information regarding the MeshGeneratorMesh object.

## Example Input File Syntax

!! Describe and include an example of how to use the MeshGeneratorMesh object.

!syntax parameters /Mesh/MeshGeneratorMesh

!syntax inputs /Mesh/MeshGeneratorMesh

!syntax children /Mesh/MeshGeneratorMesh
```
!alert-end!

!syntax description /Mesh/MeshGeneratorMesh

!syntax parameters /Mesh/MeshGeneratorMesh

!syntax inputs /Mesh/MeshGeneratorMesh

!syntax children /Mesh/MeshGeneratorMesh
19 changes: 6 additions & 13 deletions python/MooseDocs/commands/check.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,8 @@ def command_line_options(subparser, parent):
parser.add_argument('--show-warnings', action='store_true',
help='Display all report warnings.')

parser.add_argument('--generate', nargs='+', default=None,
help='Generate/update stub pages for syntax and objects for the applications names listed (e.g., MooseApp). The type(s) provided here must match with the types provided in the configuration of the Application(s).')

parser.add_argument('--dump', nargs='+', default=None,
help='Dump the syntax for the specified application reports (e.g. --dump navier_stokes')
parser.add_argument('--generate', nargs='+', default=None, help='Deprecated')
parser.add_argument('--dump', nargs='+', default=None, help='Deprecated')

parser.add_argument('--app-reports', nargs='+', default=None,
help='Limit to the following application reports (e.g. --app-reports navier_stokes')
Expand Down Expand Up @@ -91,16 +88,12 @@ def main(opt):
req_reports = [report for report in req_reports if report.title in opt.req_reports]

# Apply --generate option
if app_reports and opt.generate:
for report in app_reports:
if (set(report.app_types) == set(opt.generate)):
report.generate_stubs = True
if opt.generate:
print("The --generate option has been replaced by./moosedocs.py generate.")

# Apply --dump option
if app_reports and opt.dump:
for report in app_reports:
if report.title in opt.dump:
report.dump_syntax = True
if opt.dump:
print("The --dump option has been replaced by./moosedocs.py syntax.")

# Apply 'show_warnings' option
if opt.show_warnings:
Expand Down
105 changes: 105 additions & 0 deletions python/MooseDocs/commands/generate.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
#* This file is part of the MOOSE framework
#* https://www.mooseframework.org
#*
#* All rights reserved, see COPYRIGHT for full restrictions
#* https://github.com/idaholab/moose/blob/master/COPYRIGHT
#*
#* Licensed under LGPL 2.1, please see LICENSE for details
#* https://www.gnu.org/licenses/lgpl-2.1.html

"""Developer tools for MooseDocs."""
import argparse
import os
import re
import collections
import logging

import MooseDocs
import moosesqa
import moosetree
import mooseutils
import moosesyntax

from .. import common
from ..extensions import template

LOG = logging.getLogger(__name__)

def command_line_options(subparser, parent):
"""Define the 'generate' command."""
parser = subparser.add_parser('generate',
parents=[parent],
help="Tool for creating/updating documentation stub pages.")
parser.add_argument('app_types', nargs='+', type=str,
help="A list of app types to use when generate stub pages (e.g., StochasticToolsApp).")
parser.add_argument('--config', type=str, default='sqa_reports.yml',
help="The YAML config file for performing SQA checks.")

def main(opt):
"""./moosedocs generate"""

# Setup logging
logger = logging.getLogger('MooseDocs')
logger.handlers = list()
logger.addHandler(moosesqa.SilentRecordHandler())

# Get the report objects for the applications
_, _, app_reports = moosesqa.get_sqa_reports(opt.config, app_report=True, doc_report=False, req_report=False)

# Loop through all reports and generate the stub pages
for report in app_reports:
report.app_types = opt.app_types
report.getReport() # this is needed to generate the app syntax
for node in moosetree.iterate(report.app_syntax, lambda n: _shouldCreateStub(report, n)):
_createStubPage(report, node)

logger.handlers[0].clear() # don't report errors, that is the job for check command
return 0

def _shouldCreateStub(report, n):
return (not n.removed) \
and ('_md_file' in n) \
and ((n['_md_file'] is None) or n['_is_stub']) \
and ((n.group in report.app_types) \
or (n.groups() == set(report.app_types)))

def _createStubPage(report, node):
"""Copy template content to expected document location."""

# Determine the correct markdown filename
filename = node['_md_path']
if isinstance(node, moosesyntax.ObjectNodeBase):
filename = os.path.join(report.working_dir, node['_md_path'])
elif isinstance(node, moosesyntax.SyntaxNode):
action = moosetree.find(node, lambda n: isinstance(n, moosesyntax.ActionNode))
filename = os.path.join(report.working_dir,os.path.dirname(node['_md_path']), 'index.md')

# Determine the source template
tname = None
if isinstance(node, moosesyntax.SyntaxNode):
tname = 'moose_system.md.template'
elif isinstance(node, moosesyntax.MooseObjectNode):
tname = 'moose_object.md.template'
elif isinstance(node, moosesyntax.ActionNode):
tname = 'moose_action.md.template'
else:
raise Exception("Unexpected syntax node type.")

# Template file
tname = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', '..', 'framework',
'doc', 'content','templates', 'stubs', tname))

# Read template and apply node content
with open(tname, 'r') as fid:
content = fid.read()
content = mooseutils.apply_template_arguments(content, name=node.name, syntax=node.fullpath())

# Write the content to the desired destination
print("Creating/updating stub page: {}".format(filename))
_writeFile(filename, content)

def _writeFile(filename, content):
"""A helper function that is easy to mock in tests"""
os.makedirs(os.path.dirname(filename), exist_ok=True)
with open(filename, 'w') as fid:
fid.write(content)
55 changes: 55 additions & 0 deletions python/MooseDocs/commands/syntax.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
#* This file is part of the MOOSE framework
#* https://www.mooseframework.org
#*
#* All rights reserved, see COPYRIGHT for full restrictions
#* https://github.com/idaholab/moose/blob/master/COPYRIGHT
#*
#* Licensed under LGPL 2.1, please see LICENSE for details
#* https://www.gnu.org/licenses/lgpl-2.1.html

"""Developer tools for MooseDocs."""
import argparse
import os
import re
import collections
import logging

import MooseDocs
import moosesqa
import moosetree
import mooseutils
import moosesyntax

from .. import common
#from ..common import exceptions
#from ..tree import syntax
from ..extensions import template

LOG = logging.getLogger(__name__)

def command_line_options(subparser, parent):
"""Define the 'syntax' command."""
parser = subparser.add_parser('syntax',
parents=[parent],
help="Tool for dumping application syntax to screen.")
parser.add_argument('--config', type=str, default='sqa_reports.yml',
help="The YAML config file for performing SQA checks.")

def main(opt):
"""./moosedocs syntax"""

# Setup logging
logger = logging.getLogger('MooseDocs')
logger.handlers = list()
logger.addHandler(moosesqa.SilentRecordHandler())

# Get the report objects for the applications
_, _, app_reports = moosesqa.get_sqa_reports(opt.config, app_report=True, doc_report=False, req_report=False)

# Loop through all reports and generate the stub pages
for report in app_reports:
report.getReport() # this is needed to generate the app syntax
print(report.app_syntax)

logger.handlers[0].clear() # don't report errors, that is the job for check command
return 0
9 changes: 8 additions & 1 deletion python/MooseDocs/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
import logging
import mooseutils

from .commands import build, verify, check
from .commands import build, verify, check, generate, syntax
from .common import log

def command_line_options():
Expand All @@ -40,6 +40,9 @@ def command_line_options():
build.command_line_options(subparser, parent)
check.command_line_options(subparser, parent)
verify.command_line_options(subparser, parent)
generate.command_line_options(subparser, parent)
syntax.command_line_options(subparser, parent)

return parser.parse_args()

def run():
Expand All @@ -55,6 +58,10 @@ def run():
errno = check.main(options)
elif options.command == 'verify':
errno = verify.main(options)
elif options.command == 'generate':
errno = generate.main(options)
elif options.command == 'syntax':
errno = syntax.main(options)

handler = logging.getLogger('MooseDocs').handlers[0]
critical = handler.getCount(logging.CRITICAL)
Expand Down
20 changes: 1 addition & 19 deletions python/MooseDocs/test/commands/test_check.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,31 +37,13 @@ def tearDown(self):
# Restore the working directory
os.chdir(self._working_dir)

@mock.patch('moosesqa.SQAMooseAppReport._writeFile')
def testCheck(self, writeFile):

# Store the filenames to be created
filenames = list()
writeFile.side_effect = lambda fn, *args: filenames.append(fn)
def testCheck(self):

# Create command-line arguments in
opt = types.SimpleNamespace(config='sqa_test_reports.yml', reports=['app'], dump=None,
app_reports=None, req_reports=None,
generate=['MooseTestApp'], show_warnings=False)

# --generate
status = check.main(opt)
self.assertEqual(status, 0)
self.assertTrue(len(filenames) > 0)

# --dump
opt.dump = ['moose_test']
opt.generate = None
with mock.patch('sys.stdout', new=io.StringIO()) as stdout:
status = check.main(opt)
self.assertIn('/Kernels/Diffusion', stdout.getvalue())
self.assertEqual(status, 0)

# --reports
opt.reports = ['app']
opt.config = 'sqa_reports.yml'
Expand Down
60 changes: 60 additions & 0 deletions python/MooseDocs/test/commands/test_generate.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
#!/usr/bin/env python3
#* This file is part of the MOOSE framework
#* https://www.mooseframework.org
#*
#* All rights reserved, see COPYRIGHT for full restrictions
#* https://github.com/idaholab/moose/blob/master/COPYRIGHT
#*
#* Licensed under LGPL 2.1, please see LICENSE for details
#* https://www.gnu.org/licenses/lgpl-2.1.html
import os
import unittest
import mock
import types
import io
import mooseutils
import moosesqa
from MooseDocs.commands import generate

@unittest.skipIf(mooseutils.git_version() < (2,11,4), "Git version must at least 2.11.4")
class TestGenerate(unittest.TestCase):
def setUp(self):
# Change to the test/doc directory
self._working_dir = os.getcwd()
moose_test_doc_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', '..', '..', 'test', 'doc'))
os.chdir(moose_test_doc_dir)

def tearDown(self):
# Restore the working directory
os.chdir(self._working_dir)

@mock.patch('MooseDocs.commands.generate._shouldCreateStub')
@mock.patch('MooseDocs.commands.generate._writeFile')
def testGenerate(self, writeFile, shouldCreateStub):

# Store the filenames to be created
filenames = list()
writeFile.side_effect = lambda fn, *args: filenames.append(fn)

# Create custom function for determining if a stub should be created
shouldCreateStub.side_effect = self._shouldCreateStub

# Run the generate command
opt = types.SimpleNamespace(app_types=['MooseApp'], config='sqa_test_reports.yml')
status = generate.main(opt)

self.assertEqual(status, 0)
self.assertEqual(len(filenames), 3)
self.assertTrue(filenames[0].endswith('moose/test/doc/content/syntax/Kernels/index.md'))
self.assertTrue(filenames[1].endswith('moose/test/doc/content/source/actions/AddKernelAction.md'))
self.assertTrue(filenames[2].endswith('moose/test/doc/content/source/kernels/Diffusion.md'))

@staticmethod
def _shouldCreateStub(report, n):
# Test for stub on Action, Object, and Syntax
if n.fullpath() in ('/Kernels/AddKernelAction', '/Kernels/Diffusion', '/Kernels'):
return True
return False

if __name__ == '__main__':
unittest.main(verbosity=2)
Loading

0 comments on commit ee00f87

Please sign in to comment.