Coverage for tests/test_progress.py : 30%

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/>.
23from contextlib import contextmanager
24import logging
25import unittest
27import click
29from lsst.daf.butler.core.progress import Progress, ProgressHandler
30from lsst.daf.butler.cli.utils import clickResultMsg
31from lsst.daf.butler.cli.progress import ClickProgressHandler
34class MockProgressBar:
35 """Mock implementation of `ProgressBar` that remembers the status it
36 would report in a list.
38 Both the initial 0 and the end-of-iterable size are reported.
40 Parameters
41 ----------
42 iterable : `Iterable`, optional
43 Iterable to wrap, or `None`.
44 """
45 def __init__(self, iterable):
46 self._iterable = iterable
47 self._current = 0
48 self.reported = [self._current]
49 MockProgressBar.last = self
51 last = None
52 """Last instance of this class that was constructed, for test code that
53 cannot access it directly via other means.
54 """
56 def __iter__(self):
57 for element in self._iterable:
58 yield element
59 self._current += 1
60 self.reported.append(self._current)
62 def update(self, n: int = 1) -> None:
63 self._current += n
64 self.reported.append(self._current)
67class MockProgressHandler(ProgressHandler):
68 """A `ProgressHandler` implementation that returns `MockProgressBar`
69 instances.
70 """
71 @contextmanager
72 def get_progress_bar(self, iterable, desc, total, level):
73 yield MockProgressBar(iterable)
76class ClickProgressHandlerTestCase(unittest.TestCase):
77 """Test enabling and disabling progress in click commands.
79 It looks like click's testing harness doesn't ever actually let its
80 progress bar generate output, so the best we can do is check that using it
81 doesn't raise exceptions, and see if it looks like we're doing something
82 based on what our own progress-object state is.
83 """
85 def setUp(self):
86 # Set up a mock handler by default. Tests of click behavior will
87 # rely on this when they check that inside a click command we never
88 # end up with that mock.
89 self.logger = logging.getLogger("test_progress")
90 self.logger.setLevel(logging.INFO)
91 Progress.set_handler(MockProgressHandler())
92 self.runner = click.testing.CliRunner()
94 def tearDown(self):
95 MockProgressHandler.last = None
96 Progress.set_handler(None)
97 self.logger.setLevel(logging.NOTSET)
99 def get_cmd(self, level, enabled):
100 """Return a click command that uses a progress bar and tests that it
101 is or not enabled, as given.
102 """
104 @click.command()
105 @ClickProgressHandler.option
106 def cmd(progress):
107 p = Progress("test_progress", level=level)
108 with p.bar(range(5), desc="testing!") as bar:
109 self.assertFalse(isinstance(bar, MockProgressBar))
110 r = list(bar)
111 self.assertEqual(r, list(range(5)))
112 self.assertEqual(enabled, p.is_enabled())
114 return cmd
116 def test_click_disabled_by_default(self):
117 """Test that progress is disabled by default in click commands.
118 """
119 result = self.runner.invoke(
120 self.get_cmd(logging.INFO, enabled=False),
121 [],
122 )
123 self.assertEqual(result.exit_code, 0, clickResultMsg(result))
125 def test_click_enabled(self):
126 """Test turning on progress in click commands.
127 """
128 result = self.runner.invoke(
129 self.get_cmd(logging.INFO, enabled=True),
130 ["--progress"],
131 )
132 self.assertEqual(result.exit_code, 0, clickResultMsg(result))
134 def test_click_disabled_globally(self):
135 """Test turning on progress in click commands.
136 """
137 result = self.runner.invoke(
138 self.get_cmd(logging.INFO, enabled=False),
139 ["--no-progress"],
140 )
141 self.assertEqual(result.exit_code, 0, clickResultMsg(result))
143 def test_click_disabled_by_log_level(self):
144 """Test that progress reports below the current log level are disabled,
145 even if progress is globally enabled.
146 """
147 result = self.runner.invoke(
148 self.get_cmd(logging.DEBUG, enabled=False),
149 ["--progress"],
150 )
151 self.assertEqual(result.exit_code, 0, clickResultMsg(result))
154class MockedProgressHandlerTestCase(unittest.TestCase):
155 """Test that the interface layer for progress reporting works by using
156 mock handler and progress bar objects.
157 """
159 def setUp(self):
160 self.logger = logging.getLogger("test_progress")
161 self.logger.setLevel(logging.INFO)
162 Progress.set_handler(MockProgressHandler())
163 self.progress = Progress("test_progress")
165 def tearDown(self):
166 MockProgressHandler.last = None
167 Progress.set_handler(None)
168 self.logger.setLevel(logging.NOTSET)
170 def test_bar_iterable(self):
171 """Test using `Progress.bar` to wrap an iterable.
172 """
173 iterable = list(range(5))
174 with self.progress.bar(iterable) as bar:
175 r = list(bar)
176 self.assertEqual(r, iterable)
177 self.assertEqual(iterable + [len(iterable)], bar.reported)
179 def test_bar_update(self):
180 """Test using `Progress.bar` with manual updates.
181 """
182 with self.progress.bar(total=10) as bar:
183 for i in range(5):
184 bar.update(2)
185 self.assertEqual(list(range(0, 12, 2)), bar.reported)
187 def test_iter_chunks(self):
188 """Test using `Progress.iter_chunks`.
189 """
190 iterable = [list(range(2)), list(range(3))]
191 seen = []
192 for chunk in self.progress.iter_chunks(iterable):
193 seen.extend(chunk)
194 self.assertEqual(seen, iterable[0] + iterable[1])
195 self.assertEqual(MockProgressBar.last.reported, [0, 2, 5])
197 def test_iter_item_chunks(self):
198 """Test using `Progress.iter_item_chunks`.
199 """
200 mapping = {"x": list(range(2)), "y": list(range(3))}
201 seen = {}
202 for key, chunk in self.progress.iter_item_chunks(mapping.items()):
203 seen[key] = chunk
204 self.assertEqual(seen, mapping)
205 self.assertEqual(MockProgressBar.last.reported, [0, 2, 5])
208if __name__ == "__main__": 208 ↛ 209line 208 didn't jump to line 209, because the condition on line 208 was never true
209 unittest.main()