Coverage for tests/test_classes.py: 44%

84 statements  

« prev     ^ index     » next       coverage.py v7.2.7, created at 2023-07-25 09:27 +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# Use of this source code is governed by a 3-clause BSD-style 

10# license that can be found in the LICENSE file. 

11 

12import copy 

13import functools 

14import logging 

15import pickle 

16import unittest 

17 

18from lsst.utils.classes import Singleton, cached_getter, immutable 

19 

20log = logging.getLogger("test_classes") 

21 

22 

23class SingletonTestCase(unittest.TestCase): 

24 """Tests of the Singleton metaclass""" 

25 

26 class IsSingleton(metaclass=Singleton): 

27 """A singleton.""" 

28 

29 def __init__(self): 

30 self.data = {} 

31 self.id = 0 

32 

33 class IsBadSingleton(IsSingleton): 

34 """A single that can not accept any arguments.""" 

35 

36 def __init__(self, arg): 

37 self.arg = arg 

38 

39 class IsSingletonSubclass(IsSingleton): 

40 """A subclass of a singleton.""" 

41 

42 def __init__(self): 

43 super().__init__() 

44 

45 def testSingleton(self): 

46 one = SingletonTestCase.IsSingleton() 

47 two = SingletonTestCase.IsSingleton() 

48 

49 # Now update the first one and check the second 

50 one.data["test"] = 52 

51 self.assertEqual(one.data, two.data) 

52 two.id += 1 

53 self.assertEqual(one.id, two.id) 

54 

55 three = SingletonTestCase.IsSingletonSubclass() 

56 self.assertNotEqual(one.id, three.id) 

57 

58 with self.assertRaises(TypeError): 

59 SingletonTestCase.IsBadSingleton(52) 

60 

61 

62class ImmutabilityTestCase(unittest.TestCase): 

63 """Test immutable classes.""" 

64 

65 @immutable 

66 class Immutable: 

67 """An immutable test class.""" 

68 

69 def __init__(self, name: str, number: int): 

70 self.name = name 

71 self.number = number 

72 

73 def __hash__(self) -> int: 

74 return hash((self.name, self.number)) 

75 

76 def testImmutable(self): 

77 im1 = ImmutabilityTestCase.Immutable("name", 42) 

78 im2 = ImmutabilityTestCase.Immutable("another", 0) 

79 self.assertEqual((im1.name, im1.number), ("name", 42)) 

80 test_set = {im1, im2} 

81 self.assertIn(im2, test_set) 

82 

83 with self.assertRaises(AttributeError): 

84 im1.name = "no" 

85 

86 self.assertIs(copy.copy(im1), im1) 

87 

88 # Pickling does not work without help and this tests that it 

89 # does not work. 

90 pickled = pickle.dumps(im1) 

91 im3 = pickle.loads(pickled) 

92 self.assertEqual(im3.__dict__, {}) 

93 

94 

95class CacheTestCase(unittest.TestCase): 

96 """Test the caching code.""" 

97 

98 class Cached1: 

99 """Cached getter using cached_getter. This can use slots.""" 

100 

101 __slots__ = ("value", "_cached_cache_value") 

102 

103 def __init__(self, value: int): 

104 self.value = value 

105 

106 @property 

107 @cached_getter 

108 def cache_value(self) -> int: 

109 log.info("Calculating cached value.") 

110 return self.value + 1 

111 

112 class Cached2: 

113 """Cached getter using functools. This can not use slots.""" 

114 

115 def __init__(self, value: int): 

116 self.value = value 

117 

118 @functools.cached_property 

119 def cache_value(self) -> int: 

120 log.info("Calculating cached value.") 

121 return self.value + 1 

122 

123 def assertCache(self, cls): 

124 v1 = cls(42) 

125 self.assertEqual(v1.value, 42) 

126 

127 with self.assertLogs(level=logging.INFO) as cm: 

128 cached_value = v1.cache_value 

129 self.assertEqual(cached_value, 43) 

130 self.assertEqual(cm.output, ["INFO:test_classes:Calculating cached value."]) 

131 

132 v1.value = 50 

133 self.assertEqual(v1.value, 50) 

134 with self.assertLogs(level=logging.INFO) as cm: 

135 cached_value = v1.cache_value 

136 log.info("Used cache.") 

137 self.assertEqual(cached_value, 43) 

138 self.assertEqual(cm.output, ["INFO:test_classes:Used cache."]) 

139 

140 def testCachedGetter(self): 

141 self.assertCache(CacheTestCase.Cached1) 

142 

143 def testFunctoolsCachedProperty(self): 

144 self.assertCache(CacheTestCase.Cached2) 

145 

146 

147if __name__ == "__main__": 

148 unittest.main()