Coverage for tests/test_classes.py: 44%
84 statements
« prev ^ index » next coverage.py v7.4.3, created at 2024-03-01 11:57 +0000
« prev ^ index » next coverage.py v7.4.3, created at 2024-03-01 11:57 +0000
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# 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/>.
22import copy
23import functools
24import logging
25import pickle
26import unittest
28from lsst.utils.classes import Singleton, cached_getter, immutable
30log = logging.getLogger("test_classes")
33class SingletonTestCase(unittest.TestCase):
34 """Tests of the Singleton metaclass."""
36 class IsSingleton(metaclass=Singleton):
37 """A singleton."""
39 def __init__(self):
40 self.data = {}
41 self.id = 0
43 class IsBadSingleton(IsSingleton):
44 """A single that can not accept any arguments."""
46 def __init__(self, arg):
47 self.arg = arg
49 class IsSingletonSubclass(IsSingleton):
50 """A subclass of a singleton."""
52 def __init__(self):
53 super().__init__()
55 def testSingleton(self):
56 one = SingletonTestCase.IsSingleton()
57 two = SingletonTestCase.IsSingleton()
59 # Now update the first one and check the second
60 one.data["test"] = 52
61 self.assertEqual(one.data, two.data)
62 two.id += 1
63 self.assertEqual(one.id, two.id)
65 three = SingletonTestCase.IsSingletonSubclass()
66 self.assertNotEqual(one.id, three.id)
68 with self.assertRaises(TypeError):
69 SingletonTestCase.IsBadSingleton(52)
72class ImmutabilityTestCase(unittest.TestCase):
73 """Test immutable classes."""
75 @immutable
76 class Immutable:
77 """An immutable test class."""
79 def __init__(self, name: str, number: int):
80 self.name = name
81 self.number = number
83 def __hash__(self) -> int:
84 return hash((self.name, self.number))
86 def testImmutable(self):
87 im1 = ImmutabilityTestCase.Immutable("name", 42)
88 im2 = ImmutabilityTestCase.Immutable("another", 0)
89 self.assertEqual((im1.name, im1.number), ("name", 42))
90 test_set = {im1, im2}
91 self.assertIn(im2, test_set)
93 with self.assertRaises(AttributeError):
94 im1.name = "no"
96 self.assertIs(copy.copy(im1), im1)
98 # Pickling does not work without help and this tests that it
99 # does not work.
100 pickled = pickle.dumps(im1)
101 im3 = pickle.loads(pickled)
102 self.assertEqual(im3.__dict__, {})
105class CacheTestCase(unittest.TestCase):
106 """Test the caching code."""
108 class Cached1:
109 """Cached getter using cached_getter. This can use slots."""
111 __slots__ = ("value", "_cached_cache_value")
113 def __init__(self, value: int):
114 self.value = value
116 @property
117 @cached_getter
118 def cache_value(self) -> int:
119 log.info("Calculating cached value.")
120 return self.value + 1
122 class Cached2:
123 """Cached getter using functools. This can not use slots."""
125 def __init__(self, value: int):
126 self.value = value
128 @functools.cached_property
129 def cache_value(self) -> int:
130 log.info("Calculating cached value.")
131 return self.value + 1
133 def assertCache(self, cls):
134 v1 = cls(42)
135 self.assertEqual(v1.value, 42)
137 with self.assertLogs(level=logging.INFO) as cm:
138 cached_value = v1.cache_value
139 self.assertEqual(cached_value, 43)
140 self.assertEqual(cm.output, ["INFO:test_classes:Calculating cached value."])
142 v1.value = 50
143 self.assertEqual(v1.value, 50)
144 with self.assertLogs(level=logging.INFO) as cm:
145 cached_value = v1.cache_value
146 log.info("Used cache.")
147 self.assertEqual(cached_value, 43)
148 self.assertEqual(cm.output, ["INFO:test_classes:Used cache."])
150 def testCachedGetter(self):
151 self.assertCache(CacheTestCase.Cached1)
153 def testFunctoolsCachedProperty(self):
154 self.assertCache(CacheTestCase.Cached2)
157if __name__ == "__main__":
158 unittest.main()