Coverage for tests / test_blockUtils.py: 27%
123 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-22 09:41 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-22 09:41 +0000
1# This file is part of summit_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/>.
22"""Test cases for utils."""
24import asyncio
25import json
26import os
27import unittest
29import pandas as pd
30from utils import getVcr
32import lsst.utils.tests
33from lsst.summit.utils.blockUtils import BlockParser
34from lsst.summit.utils.efdUtils import makeEfdClient
36__all__ = ("writeNewBlockInfoTestTruthValues",)
38HAS_EFD_CLIENT = True
39try:
40 import lsst_efd_client # noqa: F401 just need to check this is available
41except ImportError:
42 HAS_EFD_CLIENT = False
44vcr = getVcr()
46DELIMITER = "||" # don't use a comma, as str(list) will naturally contain commas
47TESTDIR = os.path.abspath(os.path.dirname(__file__))
50def getBlockInfoTestTruthValues(dayObs: int) -> dict[tuple[str, int], str]:
51 """Get the current truth values for the block information.
53 Parameters
54 ----------
55 dayObs : `int`, optional
56 The dayObs to get the truth values for.
58 Returns
59 -------
60 data : `dict` [`tuple` [`int`, `int`], `str`]
61 The block info truth data.
62 """
63 dataFilename = os.path.join(TESTDIR, "data", f"blockInfoData_{dayObs}.json")
65 with open(dataFilename, "r") as f:
66 loaded = json.loads(f.read())
68 data = {}
69 for dayObsSeqNumStr, line in loaded.items():
70 blockNum = str(dayObsSeqNumStr.split(f"{DELIMITER}")[0])
71 blockSeqNum = int(dayObsSeqNumStr.split(f"{DELIMITER}")[1])
72 data[blockNum, blockSeqNum] = line
73 return data
76def writeNewBlockInfoTestTruthValues(dayObs: int) -> None:
77 """This function is used to write out the truth values for the test cases.
79 If bugs are found in the parsing, it's possible these values could change,
80 and would need to be updated. If that happens, run this function, and check
81 the new values into git.
82 """
83 blockParser = BlockParser(dayObs)
85 data = {}
86 for block in (blockParser).getBlockNums():
87 seqNums = blockParser.getSeqNums(block)
88 for seqNum in seqNums:
89 blockInfo = blockParser.getBlockInfo(block, seqNum)
90 assert blockInfo is not None
91 line = (
92 f"{blockInfo.blockId}{DELIMITER}"
93 f"{blockInfo.begin}{DELIMITER}"
94 f"{blockInfo.end}{DELIMITER}"
95 f"{blockInfo.salIndices}{DELIMITER}"
96 f"{blockInfo.tickets}{DELIMITER}"
97 f"{len(blockInfo.states)}"
98 )
99 # must store as string not tuple for json serialization
100 data[f"{block}{DELIMITER}{seqNum}"] = line
102 dataFilename = os.path.join(TESTDIR, "data", f"blockInfoData_{dayObs}.json")
103 with open(dataFilename, "w") as f:
104 json.dump(data, f)
107@unittest.skipIf(not HAS_EFD_CLIENT, "No EFD client available")
108@vcr.use_cassette()
109class BlockParserTestCase(lsst.utils.tests.TestCase):
110 @classmethod
111 @vcr.use_cassette()
112 def setUpClass(cls):
113 try:
114 cls.client = makeEfdClient(testing=True)
115 except RuntimeError:
116 raise unittest.SkipTest("Could not instantiate an EFD client")
118 cls.dayObsNoTestCases = 20230615
119 cls.dayObsWithCases = 20250420 # blocks = ['365', 'T282', 'T3', 'T379', 'T380', 'T4', 'T454']
120 cls.dayObsNoBlocks = 20230531 # contains data but no blocks
121 cls.blockParser = BlockParser(dayObs=cls.dayObsNoTestCases, client=cls.client)
122 cls.blockNums = cls.blockParser.getBlockNums()
123 cls.blockDict = {}
124 for block in cls.blockNums:
125 cls.blockDict[block] = cls.blockParser.getSeqNums(block)
127 @vcr.use_cassette()
128 def tearDown(self):
129 loop = asyncio.get_event_loop()
130 if self.client.influx_client is not None:
131 loop.run_until_complete(self.client.influx_client.close())
133 @vcr.use_cassette()
134 def test_parsing(self):
135 blockNums = self.blockParser.getBlockNums()
136 self.assertTrue(all(isinstance(n, str)) for n in blockNums)
137 self.assertEqual(blockNums, list(self.blockDict.keys()))
139 for block, seqNums in self.blockDict.items():
140 self.assertTrue(isinstance(block, str))
141 self.assertIsInstance(seqNums, list)
142 self.assertTrue(all(isinstance(s, int)) for s in seqNums)
144 found = self.blockParser.getSeqNums(block)
145 self.assertTrue(all(isinstance(s, int) for s in found))
146 self.assertEqual(found, seqNums)
147 self.blockParser.printBlockEvolution(block)
149 for seqNum in seqNums:
150 data = self.blockParser.getRows(block, seqNum)
151 self.assertIsInstance(data, pd.DataFrame)
152 self.assertGreater(len(data), 0)
153 self.blockParser.getBlockInfo(block=block, seqNum=seqNum)
154 self.blockParser.printBlockEvolution(block, seqNum=seqNum)
156 @vcr.use_cassette()
157 def test_notFoundBehavior(self):
158 # no block data on this day so check init doesn't raise
159 blockParser = BlockParser(dayObs=self.dayObsNoBlocks, client=self.client)
160 self.assertIsInstance(blockParser, BlockParser)
162 # check the queries which return nothing give nothing back gracefully
163 blocks = blockParser.getBlockNums()
164 self.assertIsInstance(blocks, list)
165 self.assertEqual(len(blocks), 0)
167 seqNums = blockParser.getSeqNums(block=123)
168 self.assertIsInstance(seqNums, list)
169 self.assertEqual(len(seqNums), 0)
171 # just check this doesn't raise
172 blockParser.getBlockInfo(block=1, seqNum=1)
174 # now switch back to one with data, and make sure the same is true
175 # when there is data present
176 blockParser = self.blockParser
177 seqNums = blockParser.getSeqNums(block=9999999)
178 self.assertIsInstance(seqNums, list)
179 self.assertEqual(len(seqNums), 0)
181 # just check this doesn't raise
182 blockParser.getBlockInfo(block=9999999, seqNum=9999999)
184 @vcr.use_cassette()
185 def test_actualValues(self):
186 for dayObs in [self.dayObsNoTestCases, self.dayObsWithCases]:
187 data = getBlockInfoTestTruthValues(dayObs)
188 blockParser = BlockParser(dayObs, client=self.client)
190 for block in blockParser.getBlockNums():
191 seqNums = blockParser.getSeqNums(block)
192 for seqNum in seqNums:
193 blockInfo = blockParser.getBlockInfo(block, seqNum)
194 line = data[blockInfo.blockNumber, blockInfo.seqNum]
195 items = line.split(f"{DELIMITER}")
196 self.assertEqual(items[0], blockInfo.blockId)
197 self.assertEqual(items[1], str(blockInfo.begin.value))
198 self.assertEqual(items[2], str(blockInfo.end.value))
199 self.assertEqual(items[3], str(blockInfo.salIndices))
200 self.assertEqual(items[4], str(blockInfo.tickets))
201 self.assertEqual(items[5], str(len(blockInfo.states)))
204class TestMemory(lsst.utils.tests.MemoryTestCase):
205 pass
208def setup_module(module):
209 lsst.utils.tests.init()
212if __name__ == "__main__": 212 ↛ 213line 212 didn't jump to line 213 because the condition on line 212 was never true
213 lsst.utils.tests.init()
214 unittest.main()