Coverage for python / lsst / obs / base / cli / butler_cmd_test.py: 50%

36 statements  

« prev     ^ index     » next       coverage.py v7.13.5, created at 2026-04-25 08:22 +0000

1# This file is part of obs_base. 

2# 

3# Developed for the LSST Data Management System. 

4# This product includes software developed by the LSST Project 

5# (https://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 program is free software: you can redistribute it and/or modify 

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

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

12# (at your option) any later version. 

13# 

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

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

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

17# GNU General Public License for more details. 

18# 

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

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

21 

22"""Base class for writing CLI butler command tests.""" 

23 

24from __future__ import annotations 

25 

26__all__ = ("ButlerCmdTestBase",) 

27 

28import abc 

29from typing import TYPE_CHECKING 

30 

31from lsst.daf.butler.cli import butler 

32from lsst.daf.butler.cli.utils import LogCliRunner 

33from lsst.utils import doImportType 

34 

35from .._instrument import Instrument 

36 

37if TYPE_CHECKING: 

38 from collections.abc import Callable 

39 

40 

41class ButlerCmdTestBase(metaclass=abc.ABCMeta): 

42 """Base class for tests of butler command line interface subcommands. 

43 Subclass from this, then `unittest.TestCase` to get a working test suite. 

44 """ 

45 

46 if TYPE_CHECKING: 

47 assertEqual: Callable 

48 

49 @property 

50 @abc.abstractmethod 

51 def instrumentClassName(self) -> str: 

52 """The fully qualified instrument class name. 

53 

54 Returns 

55 ------- 

56 `str` 

57 The fully qualified instrument class name. 

58 """ 

59 pass 

60 

61 @property 

62 def secondInstrumentClassName(self) -> str | None: 

63 """Optional; if provided the register-instrument test will try to 

64 register two instruments. 

65 

66 Returns 

67 ------- 

68 `str` or `None` 

69 The fully qualified instrument class name. 

70 """ 

71 return None 

72 

73 @property 

74 def instrument(self) -> type[Instrument]: 

75 """The instrument class.""" 

76 inst_class = doImportType(self.instrumentClassName) 

77 assert issubclass(inst_class, Instrument) 

78 return inst_class 

79 

80 @property 

81 def instrumentName(self) -> str: 

82 """The name of the instrument. 

83 

84 Returns 

85 ------- 

86 `str` 

87 The name of the instrument. 

88 """ 

89 return self.instrument.getName() 

90 

91 def test_cli(self) -> None: 

92 runner = LogCliRunner() 

93 with runner.isolated_filesystem(): 

94 result = runner.invoke(butler.cli, ["create", "here"]) 

95 self.assertEqual(result.exit_code, 0, f"output: {result.output} exception: {result.exception}") 

96 registerInstrumentArgs = ["register-instrument", "here", self.instrumentClassName] 

97 if self.secondInstrumentClassName is not None: 

98 registerInstrumentArgs.append(self.secondInstrumentClassName) 

99 result = runner.invoke(butler.cli, registerInstrumentArgs) 

100 self.assertEqual(result.exit_code, 0, f"output: {result.output} exception: {result.exception}") 

101 result = runner.invoke( 

102 butler.cli, 

103 ["write-curated-calibrations", "here", self.instrumentName, "--collection", "collection"], 

104 ) 

105 self.assertEqual(result.exit_code, 0, f"output: {result.output} exception: {result.exception}")