Coverage for tests/test_mask_scoping.py: 30%

71 statements  

« prev     ^ index     » next       coverage.py v7.14.1, created at 2026-05-30 08:49 +0000

1# 

2# LSST Data Management System 

3# Copyright 2026 LSST Corporation. 

4# 

5# This product includes software developed by the 

6# LSST Project (http://www.lsst.org/). 

7# 

8# This program is free software: you can redistribute it and/or modify 

9# it under the terms of the GNU General Public License as published by 

10# the Free Software Foundation, either version 3 of the License, or 

11# (at your option) any later version. 

12# 

13# This program is distributed in the hope that it will be useful, 

14# but WITHOUT ANY WARRANTY; without even the implied warranty of 

15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

16# GNU General Public License for more details. 

17# 

18# You should have received a copy of the LSST License Statement and 

19# the GNU General Public License along with this program. If not, 

20# see <http://www.lsstcorp.org/LegalNotices/>. 

21# 

22 

23"""Tests that mask overlays sent to the Firefly server are keyed by both 

24frame and plane name, so that multiple frames carrying same-named planes 

25(e.g. ``DETECTED`` on three different exposures) do not overwrite each 

26other on the server. 

27""" 

28 

29import unittest 

30from types import SimpleNamespace 

31from unittest import mock 

32 

33import lsst.utils.tests 

34from lsst.display.firefly import firefly as firefly_mod 

35 

36 

37def _make_impl(frame, mask_ids=None, mask_dict=None, mask_plane_colors=None, 

38 default_mask_plane_color=None, mask_transparencies=None): 

39 """Construct a ``DisplayImpl`` without running ``__init__``. 

40 

41 ``DisplayImpl.__init__`` requires a live Firefly server; we sidestep it 

42 via ``__new__`` and inject only the attributes the methods under test 

43 read. ``display`` is stubbed as a SimpleNamespace exposing the small 

44 surface those methods touch (``frame``, ``getMaskPlaneColor``, 

45 ``_defaultMaskPlaneColor``). 

46 """ 

47 impl = firefly_mod.DisplayImpl.__new__(firefly_mod.DisplayImpl) 

48 impl.display = SimpleNamespace( 

49 frame=frame, 

50 getMaskPlaneColor=lambda name: (mask_plane_colors or {}).get(name, "red"), 

51 _defaultMaskPlaneColor=default_mask_plane_color or {}, 

52 ) 

53 impl._maskIds = list(mask_ids) if mask_ids is not None else [] 

54 impl._maskDict = dict(mask_dict) if mask_dict is not None else {} 

55 impl._maskPlaneColors = dict(mask_plane_colors) if mask_plane_colors is not None else {} 

56 impl._maskTransparencies = dict(mask_transparencies) if mask_transparencies is not None else {} 

57 impl._fireflyFitsID = "fits-id-stub" 

58 # ``__del__`` -> ``_close()`` reads these attributes; satisfy it 

59 # since we are bypassing ``__init__``. 

60 impl.verbose = False 

61 impl._client = None 

62 return impl 

63 

64 

65class ScopedMaskIdTest(unittest.TestCase): 

66 """The helper itself is pure -- verify it produces distinct ids per 

67 frame while remaining human-readable in the layer panel.""" 

68 

69 def test_distinct_per_frame(self): 

70 self.assertNotEqual(firefly_mod.DisplayImpl._scoped_mask_id(0, "DETECTED"), 

71 firefly_mod.DisplayImpl._scoped_mask_id(1, "DETECTED")) 

72 

73 def test_includes_plane_name(self): 

74 self.assertIn("DETECTED", firefly_mod.DisplayImpl._scoped_mask_id(0, "DETECTED")) 

75 self.assertIn("0", firefly_mod.DisplayImpl._scoped_mask_id(0, "DETECTED")) 

76 

77 

78class RemoveMasksTest(unittest.TestCase): 

79 """``_remove_masks`` is invoked when a new image is loaded into a 

80 frame; it must only clear *that* frame's overlays.""" 

81 

82 def test_only_removes_current_frame(self): 

83 impl = _make_impl( 

84 frame=1, 

85 mask_ids=[(0, "DETECTED"), (1, "DETECTED"), (1, "BAD"), (2, "SAT")], 

86 ) 

87 with mock.patch.object(firefly_mod, "_fireflyClient") as client: 

88 impl._remove_masks() 

89 removed = [(c.kwargs["plot_id"], c.kwargs["mask_id"]) 

90 for c in client.remove_mask.call_args_list] 

91 # Frame 1 layers removed, frames 0 and 2 left alone. 

92 self.assertEqual(set(removed), 

93 {("1", "f1__DETECTED"), ("1", "f1__BAD")}) 

94 self.assertEqual(set(impl._maskIds), {(0, "DETECTED"), (2, "SAT")}) 

95 

96 

97class SetMaskPlaneColorTest(unittest.TestCase): 

98 """``setMaskPlaneColor`` should retarget only the current frame's 

99 layer, leaving sibling frames' layers in place.""" 

100 

101 def test_scopes_mask_id(self): 

102 impl = _make_impl( 

103 frame=2, 

104 mask_dict={"DETECTED": 5}, 

105 mask_plane_colors={"DETECTED": "red"}, 

106 ) 

107 with mock.patch.object(firefly_mod, "_fireflyClient") as client: 

108 impl._setMaskPlaneColor("DETECTED", "cyan") 

109 (remove_call,) = client.remove_mask.call_args_list 

110 (add_call,) = client.add_mask.call_args_list 

111 self.assertEqual(remove_call.kwargs["plot_id"], "2") 

112 self.assertEqual(remove_call.kwargs["mask_id"], "f2__DETECTED") 

113 self.assertEqual(add_call.kwargs["plot_id"], "2") 

114 self.assertEqual(add_call.kwargs["mask_id"], "f2__DETECTED") 

115 self.assertEqual(impl._maskPlaneColors["DETECTED"], "cyan") 

116 

117 def test_ignore_color_skips_add(self): 

118 impl = _make_impl( 

119 frame=0, 

120 mask_dict={"DETECTED": 5}, 

121 mask_plane_colors={"DETECTED": "red"}, 

122 ) 

123 with mock.patch.object(firefly_mod, "_fireflyClient") as client: 

124 impl._setMaskPlaneColor("DETECTED", "ignore") 

125 self.assertEqual(client.remove_mask.call_count, 1) 

126 self.assertEqual(client.add_mask.call_count, 0) 

127 

128 

129class SetMaskTransparencyTest(unittest.TestCase): 

130 """``setMaskTransparency`` dispatches per-layer attribute changes; 

131 the dispatched ``imageOverlayId`` must be the frame-scoped id.""" 

132 

133 def test_named_plane_uses_scoped_overlay_id(self): 

134 impl = _make_impl(frame=3) 

135 with mock.patch.object(firefly_mod, "_fireflyClient") as client: 

136 impl._setMaskTransparency(40, "DETECTED") 

137 (call,) = client.dispatch.call_args_list 

138 payload = call.kwargs["payload"] 

139 self.assertEqual(payload["plotId"], "3") 

140 self.assertEqual(payload["imageOverlayId"], "f3__DETECTED") 

141 self.assertAlmostEqual(payload["attributes"]["opacity"], 0.6) 

142 

143 def test_all_planes_filters_by_frame(self): 

144 # ``maskName=None`` means "all of this frame's planes". Layers 

145 # registered against other frames must not be touched. 

146 impl = _make_impl( 

147 frame=1, 

148 mask_ids=[(0, "DETECTED"), (1, "DETECTED"), (1, "BAD")], 

149 default_mask_plane_color={}, 

150 ) 

151 with mock.patch.object(firefly_mod, "_fireflyClient") as client: 

152 impl._setMaskTransparency(0, None) 

153 ids = {c.kwargs["payload"]["imageOverlayId"] 

154 for c in client.dispatch.call_args_list} 

155 self.assertEqual(ids, {"f1__DETECTED", "f1__BAD"}) 

156 

157 

158class TestMemory(lsst.utils.tests.MemoryTestCase): 

159 pass 

160 

161 

162def setup_module(module): 

163 lsst.utils.tests.init() 

164 

165 

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

167 lsst.utils.tests.init() 

168 unittest.main()