Coverage for tests/test_qgraph.py: 25%

53 statements  

« prev     ^ index     » next       coverage.py v7.10.6, created at 2025-10-09 03:40 -0700

1# This file is part of ctrl_mpexec. 

2# 

3# Developed for the LSST Data Management System. 

4# This product includes software developed by the LSST Project 

5# (http://www.lsst.org). 

6# See the COPYRIGHT file at the top-level directory of this distribution 

7# for details of code ownership. 

8# 

9# This software is dual licensed under the GNU General Public License and also 

10# under a 3-clause BSD license. Recipients may choose which of these licenses 

11# to use; please see the files gpl-3.0.txt and/or bsd_license.txt, 

12# respectively. If you choose the GPL option then the following text applies 

13# (but note that there is still no warranty even if you opt for BSD instead): 

14# 

15# This program is free software: you can redistribute it and/or modify 

16# it under the terms of the GNU General Public License as published by 

17# the Free Software Foundation, either version 3 of the License, or 

18# (at your option) any later version. 

19# 

20# This program is distributed in the hope that it will be useful, 

21# but WITHOUT ANY WARRANTY; without even the implied warranty of 

22# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

23# GNU General Public License for more details. 

24# 

25# You should have received a copy of the GNU General Public License 

26# along with this program. If not, see <http://www.gnu.org/licenses/>. 

27 

28"""Unit tests for ctrl_mpexec CLI qgraph subcommand.""" 

29 

30import os 

31import textwrap 

32import unittest 

33 

34import click.testing 

35 

36from lsst.ctrl.mpexec.cli import opt, script 

37from lsst.ctrl.mpexec.cli.pipetask import cli as pipetask_cli 

38from lsst.daf.butler.cli.utils import LogCliRunner, clickResultMsg 

39from lsst.pipe.base.quantum_reports import Report 

40from lsst.pipe.base.tests.mocks import DirectButlerRepo 

41 

42 

43class QgraphTest(unittest.TestCase): 

44 """Tests for the "pipetask qgraph" command. 

45 

46 Note that significant coverage for the qgraph command is also provided by 

47 test_run.py, since those tests generally build a quantum graph and then run 

48 it. 

49 """ 

50 

51 def test_qgraph_summary(self): 

52 """Test reading a saved graph and writing a summary.""" 

53 with DirectButlerRepo.make_temporary() as (helper, root): 

54 helper.add_task() 

55 helper.add_task() 

56 qgc = helper.make_quantum_graph_builder().finish(attach_datastore_records=False) 

57 graph_uri = os.path.join(root, "graph.qg") 

58 qgc.write(graph_uri) 

59 test_filename = os.path.join(root, "summary.json") 

60 runner = LogCliRunner() 

61 result = runner.invoke( 

62 pipetask_cli, 

63 ["qgraph", "--butler-config", root, "--qgraph", graph_uri, "--summary", test_filename], 

64 input="no", 

65 ) 

66 # Check that we can read from the command line 

67 self.assertEqual(result.exit_code, 0, clickResultMsg(result)) 

68 # Check that we can open and read the file produced by make_reports 

69 with open(test_filename) as f: 

70 summary = Report.model_validate_json(f.read()) 

71 self.assertEqual(summary.qgraphSummary.outputRun, "output_run") 

72 self.assertEqual(len(summary.qgraphSummary.qgraphTaskSummaries), 2) 

73 

74 def test_qgraph_show(self): 

75 with DirectButlerRepo.make_temporary() as (helper, root): 

76 helper.add_task() 

77 helper.add_task() 

78 qgc = helper.make_quantum_graph_builder().finish(attach_datastore_records=False) 

79 graph_uri = os.path.join(root, "graph.qg") 

80 qgc.write(graph_uri) 

81 runner = LogCliRunner() 

82 result = runner.invoke( 

83 pipetask_cli, 

84 ["qgraph", "--butler-config", root, "--qgraph", graph_uri, "--show", "graph"], 

85 input="no", 

86 ) 

87 self.assertIn( 

88 "task_auto1 (lsst.pipe.base.tests.mocks.DynamicTestPipelineTask)", 

89 result.output, 

90 ) 

91 self.assertIn( 

92 "task_auto2 (lsst.pipe.base.tests.mocks.DynamicTestPipelineTask)", 

93 result.output, 

94 ) 

95 self.assertIn("DatasetType('dataset_auto0', {}, _mock_StructuredDataDict)", result.output) 

96 self.assertIn("DatasetType('dataset_auto1', {}, _mock_StructuredDataDict)", result.output) 

97 self.assertIn("DatasetType('dataset_auto2', {}, _mock_StructuredDataDict)", result.output) 

98 result = runner.invoke( 

99 pipetask_cli, 

100 ["qgraph", "--butler-config", root, "--qgraph", graph_uri, "--show", "workflow"], 

101 input="no", 

102 ) 

103 id1, id2 = qgc.quantum_datasets.keys() 

104 self.assertEqual( 

105 result.output.strip(), 

106 textwrap.dedent(f""" 

107 Quantum {id1}: lsst.pipe.base.tests.mocks.DynamicTestPipelineTask 

108 Quantum {id2}: lsst.pipe.base.tests.mocks.DynamicTestPipelineTask 

109 Parent Quantum {id1} - Child Quantum {id2} 

110 """).strip(), 

111 ) 

112 result = runner.invoke( 

113 pipetask_cli, 

114 ["qgraph", "--butler-config", root, "--qgraph", graph_uri, "--show", "uri"], 

115 input="no", 

116 ) 

117 self.assertIn( 

118 f"Quantum {id1}: lsst.pipe.base.tests.mocks.DynamicTestPipelineTask", 

119 result.output, 

120 ) 

121 self.assertIn( 

122 f"Quantum {id2}: lsst.pipe.base.tests.mocks.DynamicTestPipelineTask", 

123 result.output, 

124 ) 

125 

126 def test_missing_option(self): 

127 """Test that if options for the qgraph script are missing that it 

128 fails. 

129 """ 

130 

131 @click.command() 

132 @opt.qgraph_options() 

133 def cli(**kwargs): 

134 script.qgraph(**kwargs) 

135 

136 runner = click.testing.CliRunner() 

137 result = runner.invoke(cli) 

138 # The cli call should fail, because qgraph takes more options than are 

139 # defined by qgraph_options. 

140 self.assertNotEqual(result.exit_code, 0) 

141 

142 

143if __name__ == "__main__": 

144 unittest.main()