Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1# This file is part of daf_butler. 

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 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 <http://www.gnu.org/licenses/>. 

21 

22"""Unit tests for the daf_butler CliLog utility. Code is implemented in 

23daf_butler but some only runs if lsst.log.Log can be imported so these parts of 

24it can't be tested there because daf_butler does not directly depend on 

25lsst.log, and only uses it if it has been setup by another package.""" 

26 

27import click 

28from collections import namedtuple 

29from functools import partial 

30import logging 

31import unittest 

32 

33from lsst.daf.butler.cli.butler import cli as butlerCli 

34from lsst.daf.butler.cli.cliLog import CliLog 

35from lsst.daf.butler.cli.utils import clickResultMsg, command_test_env, LogCliRunner 

36try: 

37 import lsst.log as lsstLog 

38except ModuleNotFoundError: 

39 lsstLog = None 

40 

41 

42def hasLsstLogHandler(logger): 

43 """Check if a python logger has an lsst.log.LogHandler installed. 

44 

45 Parameters 

46 ---------- 

47 logger : `logging.logger` 

48 A python logger. 

49 

50 Returns 

51 ------ 

52 `bool` 

53 True if the logger has an lsst.log.LogHander installed, else False. 

54 """ 

55 if lsstLog is None: 

56 return False 

57 for handler in logging.getLogger().handlers: 

58 if isinstance(handler, lsstLog.LogHandler): 

59 return True 

60 

61 

62@click.command() 

63@click.option("--expected-pyroot-level") 

64@click.option("--expected-pybutler-level") 

65@click.option("--expected-lsstroot-level") 

66@click.option("--expected-lsstbutler-level") 

67def command_log_settings_test(expected_pyroot_level, 

68 expected_pybutler_level, 

69 expected_lsstroot_level, 

70 expected_lsstbutler_level): 

71 if lsstLog is not None and not hasLsstLogHandler(logging.getLogger()): 

72 raise click.ClickException("Expected to find an lsst.log handler in the python root logger's " 

73 "handlers.") 

74 

75 LogLevel = namedtuple("LogLevel", ("expected", "actual", "name")) 

76 

77 logLevels = [LogLevel(expected_pyroot_level, 

78 logging.getLogger().level, 

79 "pyRoot"), 

80 LogLevel(expected_pybutler_level, 

81 logging.getLogger("lsst.daf.butler").level, 

82 "pyButler")] 

83 if lsstLog is not None: 

84 logLevels.extend([LogLevel(expected_lsstroot_level, 

85 lsstLog.getLogger("").getLevel(), 

86 "lsstRoot"), 

87 LogLevel(expected_lsstbutler_level, 

88 lsstLog.getLogger("lsst.daf.butler").getLevel(), 

89 "lsstButler")]) 

90 for expected, actual, name in logLevels: 

91 if expected != actual: 

92 raise(click.ClickException(f"expected {name} level to be {expected}, actual:{actual}")) 

93 

94 

95class CliLogTestBase(): 

96 """Tests log initialization, reset, and setting log levels.""" 

97 

98 lsstLogHandlerId = None 

99 

100 def setUp(self): 

101 self.runner = LogCliRunner() 

102 

103 def tearDown(self): 

104 self.lsstLogHandlerId = None 

105 

106 class PythonLogger: 

107 """Keeps track of log level of a component and number of handlers 

108 attached to it at the time this object was initialized.""" 

109 

110 def __init__(self, component): 

111 self.logger = logging.getLogger(component) 

112 self.initialLevel = self.logger.level 

113 

114 class LsstLogger: 

115 """Keeps track of log level for a component at the time this object was 

116 initialized.""" 

117 def __init__(self, component): 

118 self.logger = lsstLog.getLogger(component) if lsstLog else None 

119 self.initialLevel = self.logger.getLevel() if lsstLog else None 

120 

121 def runTest(self, cmd): 

122 """Test that the log context manager works with the butler cli to 

123 initialize the logging system according to cli inputs for the duration 

124 of the command execution and resets the logging system to its previous 

125 state or expected state when command execution finishes.""" 

126 pyRoot = self.PythonLogger(None) 

127 pyButler = self.PythonLogger("lsst.daf.butler") 

128 lsstRoot = self.LsstLogger("") 

129 lsstButler = self.LsstLogger("lsst.daf.butler") 

130 

131 with command_test_env(self.runner, "lsst.daf.butler.tests.cliLogTestBase", 

132 "command-log-settings-test"): 

133 result = cmd() 

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

135 

136 if lsstLog is not None: 

137 self.assertFalse(hasLsstLogHandler(logging.getLogger()), 

138 msg="CliLog should remove the lsst.log handler it added to the root logger.") 

139 self.assertEqual(pyRoot.logger.level, logging.INFO) 

140 self.assertEqual(pyButler.logger.level, pyButler.initialLevel) 

141 if lsstLog is not None: 

142 self.assertEqual(lsstRoot.logger.getLevel(), lsstLog.INFO) 

143 # lsstLogLevel can either be the inital level, or uninitialized or 

144 # the defined default value. 

145 expectedLsstLogLevel = ((lsstButler.initialLevel, ) if lsstButler.initialLevel != -1 

146 else(-1, CliLog.defaultLsstLogLevel)) 

147 self.assertIn(lsstButler.logger.getLevel(), expectedLsstLogLevel) 

148 

149 def test_butlerCliLog(self): 

150 """Test that the log context manager works with the butler cli to 

151 initialize the logging system according to cli inputs for the duration 

152 of the command execution and resets the logging system to its previous 

153 state or expected state when command execution finishes.""" 

154 

155 self.runTest(partial(self.runner.invoke, 

156 butlerCli, 

157 ["--log-level", "WARNING", 

158 "--log-level", "lsst.daf.butler=DEBUG", 

159 "command-log-settings-test", 

160 "--expected-pyroot-level", logging.WARNING, 

161 "--expected-pybutler-level", logging.DEBUG, 

162 "--expected-lsstroot-level", lsstLog.WARN if lsstLog else 0, 

163 "--expected-lsstbutler-level", lsstLog.DEBUG if lsstLog else 0])) 

164 

165 def test_helpLogReset(self): 

166 """Verify that when a command does not execute, like when the help menu 

167 is printed instead, that CliLog is still reset.""" 

168 

169 self.runTest(partial(self.runner.invoke, butlerCli, ["command-log-settings-test", "--help"])) 

170 

171 

172if __name__ == "__main__": 172 ↛ 173line 172 didn't jump to line 173, because the condition on line 172 was never true

173 unittest.main()