Coverage for tests/test_classes.py : 43%

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 functools
15import copy
16import pickle
18from lsst.utils.classes import Singleton, immutable, cached_getter
20log = logging.getLogger("test_classes")
23class SingletonTestCase(unittest.TestCase):
24 """Tests of the Singleton metaclass"""
26 class IsSingleton(metaclass=Singleton):
27 def __init__(self):
28 self.data = {}
29 self.id = 0
31 class IsBadSingleton(IsSingleton):
32 def __init__(self, arg):
33 """A singleton can not accept any arguments."""
34 self.arg = arg
36 class IsSingletonSubclass(IsSingleton):
37 def __init__(self):
38 super().__init__()
40 def testSingleton(self):
41 one = SingletonTestCase.IsSingleton()
42 two = SingletonTestCase.IsSingleton()
44 # Now update the first one and check the second
45 one.data["test"] = 52
46 self.assertEqual(one.data, two.data)
47 two.id += 1
48 self.assertEqual(one.id, two.id)
50 three = SingletonTestCase.IsSingletonSubclass()
51 self.assertNotEqual(one.id, three.id)
53 with self.assertRaises(TypeError):
54 SingletonTestCase.IsBadSingleton(52)
57class ImmutabilityTestCase(unittest.TestCase):
59 @immutable
60 class Immutable:
62 def __init__(self, name: str, number: int):
63 self.name = name
64 self.number = number
66 def __hash__(self) -> int:
67 return hash((self.name, self.number))
69 def testImmutable(self):
70 im1 = ImmutabilityTestCase.Immutable("name", 42)
71 im2 = ImmutabilityTestCase.Immutable("another", 0)
72 self.assertEqual((im1.name, im1.number), ("name", 42))
73 test_set = {im1, im2}
74 self.assertIn(im2, test_set)
76 with self.assertRaises(AttributeError):
77 im1.name = "no"
79 self.assertIs(copy.copy(im1), im1)
81 # Pickling does not work without help and this tests that it
82 # does not work.
83 pickled = pickle.dumps(im1)
84 im3 = pickle.loads(pickled)
85 self.assertEqual(im3.__dict__, {})
88class CacheTestCase(unittest.TestCase):
90 class Cached1:
91 """Cached getter using cached_getter. This can use slots."""
92 __slots__ = ("value", "_cached_cache_value")
94 def __init__(self, value: int):
95 self.value = value
97 @property
98 @cached_getter
99 def cache_value(self) -> int:
100 log.info("Calculating cached value.")
101 return self.value + 1
103 class Cached2:
104 """Cached getter using functools. This can not use slots."""
106 def __init__(self, value: int):
107 self.value = value
109 @functools.cached_property
110 def cache_value(self) -> int:
111 log.info("Calculating cached value.")
112 return self.value + 1
114 def assertCache(self, cls):
115 v1 = cls(42)
116 self.assertEqual(v1.value, 42)
118 with self.assertLogs(level=logging.INFO) as cm:
119 cached_value = v1.cache_value
120 self.assertEqual(cached_value, 43)
121 self.assertEqual(cm.output, ["INFO:test_classes:Calculating cached value."])
123 v1.value = 50
124 self.assertEqual(v1.value, 50)
125 with self.assertLogs(level=logging.INFO) as cm:
126 cached_value = v1.cache_value
127 log.info("Used cache.")
128 self.assertEqual(cached_value, 43)
129 self.assertEqual(cm.output, ["INFO:test_classes:Used cache."])
131 def testCachedGetter(self):
132 self.assertCache(CacheTestCase.Cached1)
134 def testFunctoolsCachedProperty(self):
135 self.assertCache(CacheTestCase.Cached2)
138if __name__ == "__main__": 138 ↛ 139line 138 didn't jump to line 139, because the condition on line 138 was never true
139 unittest.main()