Coverage for tests/test_timer.py : 26%

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 utils.
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# Use of this source code is governed by a 3-clause BSD-style
10# license that can be found in the LICENSE file.
12import unittest
13import logging
14import time
15import datetime
16import os.path
17from dataclasses import dataclass
19from lsst.utils.timer import timeMethod, logPairs, time_this
21log = logging.getLogger("test_timer")
23THIS_FILE = os.path.basename(__file__)
25# Only use this in a single test but needs to be associated
26# with the function up front.
27test_metadata = {}
30@dataclass
31class Example1:
32 log: logging.Logger
33 metadata: dict
35 @timeMethod
36 def sleeper(self, duration: float) -> None:
37 """Sleep for some time."""
38 time.sleep(duration)
41@timeMethod
42def decorated_sleeper_nothing(self, duration: float) -> None:
43 time.sleep(duration)
46@timeMethod(logger=log)
47def decorated_sleeper_logger(self, duration: float) -> None:
48 time.sleep(duration)
51@timeMethod(logger=log, logLevel=logging.INFO)
52def decorated_sleeper_logger_level(self, duration: float) -> None:
53 time.sleep(duration)
56@timeMethod(metadata=test_metadata)
57def decorated_sleeper_metadata(self, duration: float) -> None:
58 time.sleep(duration)
61class TestTimeMethod(unittest.TestCase):
63 def testLogPairs(self):
64 # Test the non-obj case.
65 logger = logging.getLogger("test")
66 pairs = (("name1", 0), ("name2", 1))
67 metadata = {}
68 with self.assertLogs(level=logging.INFO) as cm:
69 logPairs(None, pairs, logLevel=logging.INFO, logger=logger, metadata=metadata)
70 self.assertEqual(len(cm.output), 1, cm.output)
71 self.assertTrue(cm.output[0].endswith("name1=0; name2=1"), cm.output)
72 self.assertEqual(cm.records[0].filename, THIS_FILE, "log message should originate from here")
73 self.assertEqual(metadata, {"name1": [0], "name2": [1]})
75 def assertTimer(self, duration, task):
76 # Call it twice to test the "add" functionality.
77 task.sleeper(duration)
78 task.sleeper(duration)
79 counter = 2
81 has_logger = getattr(task, "log", None) is not None \
82 and task.log is not None
83 has_metadata = getattr(task, "metadata", None) is not None \
84 and task.metadata is not None
86 if has_logger:
87 counter += 1
88 with self.assertLogs("timer.task", level=logging.DEBUG) as cm:
89 task.sleeper(duration)
90 self.assertEqual(cm.records[0].filename, THIS_FILE, "log message should originate from here")
92 if has_metadata:
93 self.assertEqual(len(task.metadata["sleeperStartUserTime"]), counter)
95 start = datetime.datetime.fromisoformat(task.metadata["sleeperStartUtc"][1])
96 end = datetime.datetime.fromisoformat(task.metadata["sleeperEndUtc"][1])
97 delta = end - start
98 delta_sec = delta.seconds + (delta.microseconds / 1e6)
99 self.assertGreaterEqual(delta_sec, duration)
101 def testTaskLike(self):
102 """Test timer on something that looks like a Task."""
104 # Call with different parameters.
105 parameters = ((logging.getLogger("task"), {}),
106 (logging.getLogger("task"), None),
107 (None, {}),
108 (None, None))
110 duration = 0.1
111 for log, metadata in parameters:
112 with self.subTest(log=log, metadata=metadata):
113 task = Example1(log=log, metadata=metadata)
114 self.assertTimer(duration, task)
116 def testDecorated(self):
117 """Test timeMethod on non-Task like instances."""
118 duration = 0.1
120 # The "self" object shouldn't be usable but this should do nothing
121 # and not crash.
122 decorated_sleeper_nothing(self, duration)
124 # Use a function decorated for logging.
125 with self.assertLogs("timer.test_timer", level=logging.DEBUG) as cm:
126 decorated_sleeper_logger(self, duration)
127 self.assertEqual(cm.records[0].filename, THIS_FILE, "log message should originate from here")
129 # And adjust the log level
130 with self.assertLogs("timer.test_timer", level=logging.INFO):
131 decorated_sleeper_logger_level(self, duration)
133 # Use a function decorated for metadata.
134 self.assertEqual(len(test_metadata), 0)
135 with self.assertLogs("timer.test_timer", level=logging.DEBUG) as cm:
136 # Check that we only get a single log message and nothing from
137 # timeMethod itself.
138 decorated_sleeper_metadata(self, duration)
139 logging.getLogger("timer.test_timer").debug("sentinel")
140 self.assertEqual(len(cm.output), 1)
141 self.assertIn("decorated_sleeper_metadataStartUserTime", test_metadata)
144class TimerTestCase(unittest.TestCase):
146 def testTimer(self):
147 with self.assertLogs(level="DEBUG") as cm:
148 with time_this():
149 pass
150 self.assertEqual(cm.records[0].name, "timer")
151 self.assertEqual(cm.records[0].levelname, "DEBUG")
152 self.assertEqual(cm.records[0].filename, THIS_FILE)
154 with self.assertLogs(level="DEBUG") as cm:
155 with time_this(prefix=None):
156 pass
157 self.assertEqual(cm.records[0].name, "root")
158 self.assertEqual(cm.records[0].levelname, "DEBUG")
159 self.assertIn("Took", cm.output[0])
160 self.assertEqual(cm.records[0].filename, THIS_FILE)
162 # Change logging level
163 with self.assertLogs(level="INFO") as cm:
164 with time_this(level=logging.INFO, prefix=None):
165 pass
166 self.assertEqual(cm.records[0].name, "root")
167 self.assertIn("Took", cm.output[0])
168 self.assertIn("seconds", cm.output[0])
170 # Use a new logger with a message.
171 msg = "Test message %d"
172 test_num = 42
173 logname = "test"
174 with self.assertLogs(level="DEBUG") as cm:
175 with time_this(log=logging.getLogger(logname),
176 msg=msg, args=(42,), prefix=None):
177 pass
178 self.assertEqual(cm.records[0].name, logname)
179 self.assertIn("Took", cm.output[0])
180 self.assertIn(msg % test_num, cm.output[0])
182 # Prefix the logger.
183 prefix = "prefix"
184 with self.assertLogs(level="DEBUG") as cm:
185 with time_this(prefix=prefix):
186 pass
187 self.assertEqual(cm.records[0].name, prefix)
188 self.assertIn("Took", cm.output[0])
190 # Prefix explicit logger.
191 with self.assertLogs(level="DEBUG") as cm:
192 with time_this(log=logging.getLogger(logname),
193 prefix=prefix):
194 pass
195 self.assertEqual(cm.records[0].name, f"{prefix}.{logname}")
197 # Trigger a problem.
198 with self.assertLogs(level="ERROR") as cm:
199 with self.assertRaises(RuntimeError):
200 with time_this(log=logging.getLogger(logname),
201 prefix=prefix):
202 raise RuntimeError("A problem")
203 self.assertEqual(cm.records[0].name, f"{prefix}.{logname}")
204 self.assertEqual(cm.records[0].levelname, "ERROR")
207if __name__ == "__main__": 207 ↛ 208line 207 didn't jump to line 208, because the condition on line 207 was never true
208 unittest.main()