Coverage for tests/test_classes.py: 48%

86 statements  

« prev     ^ index     » next       coverage.py v6.4, created at 2022-05-25 10:15 +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 def __init__(self): 

28 self.data = {} 

29 self.id = 0 

30 

31 class IsBadSingleton(IsSingleton): 

32 def __init__(self, arg): 

33 """A singleton can not accept any arguments.""" 

34 self.arg = arg 

35 

36 class IsSingletonSubclass(IsSingleton): 

37 def __init__(self): 

38 super().__init__() 

39 

40 def testSingleton(self): 

41 one = SingletonTestCase.IsSingleton() 

42 two = SingletonTestCase.IsSingleton() 

43 

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) 

49 

50 three = SingletonTestCase.IsSingletonSubclass() 

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

52 

53 with self.assertRaises(TypeError): 

54 SingletonTestCase.IsBadSingleton(52) 

55 

56 

57class ImmutabilityTestCase(unittest.TestCase): 

58 @immutable 

59 class Immutable: 

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

61 self.name = name 

62 self.number = number 

63 

64 def __hash__(self) -> int: 

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

66 

67 def testImmutable(self): 

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

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

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

71 test_set = {im1, im2} 

72 self.assertIn(im2, test_set) 

73 

74 with self.assertRaises(AttributeError): 

75 im1.name = "no" 

76 

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

78 

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

80 # does not work. 

81 pickled = pickle.dumps(im1) 

82 im3 = pickle.loads(pickled) 

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

84 

85 

86class CacheTestCase(unittest.TestCase): 

87 class Cached1: 

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

89 

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

91 

92 def __init__(self, value: int): 

93 self.value = value 

94 

95 @property 

96 @cached_getter 

97 def cache_value(self) -> int: 

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

99 return self.value + 1 

100 

101 class Cached2: 

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

103 

104 def __init__(self, value: int): 

105 self.value = value 

106 

107 @functools.cached_property 

108 def cache_value(self) -> int: 

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

110 return self.value + 1 

111 

112 def assertCache(self, cls): 

113 v1 = cls(42) 

114 self.assertEqual(v1.value, 42) 

115 

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

117 cached_value = v1.cache_value 

118 self.assertEqual(cached_value, 43) 

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

120 

121 v1.value = 50 

122 self.assertEqual(v1.value, 50) 

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

124 cached_value = v1.cache_value 

125 log.info("Used cache.") 

126 self.assertEqual(cached_value, 43) 

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

128 

129 def testCachedGetter(self): 

130 self.assertCache(CacheTestCase.Cached1) 

131 

132 def testFunctoolsCachedProperty(self): 

133 self.assertCache(CacheTestCase.Cached2) 

134 

135 

136if __name__ == "__main__": 136 ↛ 137line 136 didn't jump to line 137, because the condition on line 136 was never true

137 unittest.main()