Coverage for tests/test_spectraction.py: 98%
391 statements
« prev ^ index » next coverage.py v7.16.2, created at 2026-10-10 09:55 +0000
« prev ^ index » next coverage.py v7.16.2, created at 2026-10-10 09:55 +0000
1#!/usr/bin/env python
2# This file is part of atmospec.
3#
4# Developed for the LSST Data Management System.
5# This product includes software developed by the LSST Project
6# (https://www.lsst.org).
7# See the COPYRIGHT file at the top-level directory of this distribution
8# for details of code ownership.
9#
10# This program is free software: you can redistribute it and/or modify
11# it under the terms of the GNU General Public License as published by
12# the Free Software Foundation, either version 3 of the License, or
13# (at your option) any later version.
14#
15# This program is distributed in the hope that it will be useful,
16# but WITHOUT ANY WARRANTY; without even the implied warranty of
17# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18# GNU General Public License for more details.
19#
20# You should have received a copy of the GNU General Public License
21# along with this program. If not, see <https://www.gnu.org/licenses/>.
22"""Test cases for the atmospec SpectractorShim."""
24import importlib.resources
25import os
26import tempfile
27import unittest
28from unittest import mock
30import numpy as np
32import lsst.utils
33import lsst.utils.tests
34import lsst.afw.coord as afwCoord
35import lsst.afw.image as afwImage
36import lsst.geom as geom
37from lsst.daf.base import DateTime
39from spectractor import parameters
40from spectractor.extractor.images import Image
41import lsst.atmospec.spectraction as spectractionModule
42from lsst.atmospec.spectraction import SpectractorShim, Spectraction
45def _makeExposure(width=20, height=24, physicalFilter="empty~holo4_003",
46 weather=None, withMetadata=True):
47 """Build a small ExposureF with a populated VisitInfo and filter."""
48 exp = afwImage.ExposureF(width, height)
49 exp.image.array[:] = np.random.rand(height, width).astype(np.float32) + 10.0
50 exp.variance.array[:] = 1.0
52 if weather is None:
53 weather = afwCoord.Weather(10.0, 74300.0, 40.0)
55 vi = afwImage.VisitInfo(
56 exposureTime=30.0,
57 date=DateTime("2021-01-01T00:00:00Z", DateTime.UTC),
58 boresightRaDec=geom.SpherePoint(30.0 * geom.degrees, -30.0 * geom.degrees),
59 boresightAzAlt=geom.SpherePoint(0.0 * geom.degrees, 80.0 * geom.degrees),
60 boresightAirmass=1.1,
61 boresightRotAngle=45.0 * geom.degrees,
62 weather=weather,
63 )
64 exp.getInfo().setVisitInfo(vi)
65 exp.setFilter(afwImage.FilterLabel(physical=physicalFilter, band="white"))
66 if withMetadata: 66 ↛ 71line 66 didn't jump to line 71 because the condition on line 66 was always true
67 md = exp.getMetadata()
68 md['GRATING'] = 'holo4_003'
69 md['AIRMASS'] = 1.1
70 md['DATE'] = '2021-01-01T00:00:00'
71 return exp
74def _makeSpectractorImage(shape=(10, 12)):
75 """Make a real (blank) Spectractor Image usable by helper methods."""
76 image = Image(file_name='', target_label='', disperser_label='', filter_label='')
77 image.data = np.ones(shape)
78 return image
81class SpectractionStaticTestCase(lsst.utils.tests.TestCase):
82 """Tests for the pure/static helper methods."""
84 def testImport(self):
85 import lsst.atmospec.spectraction as s # noqa: F401
87 def test_flipImageLeftRight(self):
88 image = mock.MagicMock()
89 data = np.arange(12).reshape(3, 4)
90 image.data = data.copy()
91 outImage, xpos, ypos = SpectractorShim.flipImageLeftRight(image, 1.0, 2.0)
92 # data flipped along axis 1
93 np.testing.assert_array_equal(outImage.data, np.flip(data, 1))
94 self.assertEqual(xpos, data.shape[1] - 1.0)
95 self.assertEqual(ypos, 2.0)
97 def test_transposeCentroid(self):
98 image = mock.MagicMock()
99 image.data = np.ones((5, 7)) # xSize, ySize = 5, 7
100 newX, newY = SpectractorShim.transposeCentroid(2.0, 3.0, image)
101 self.assertEqual(newX, 3.0) # dmYpos
102 self.assertEqual(newY, 5 - 2.0) # xSize - dmXpos
104 def test_dumpParameters(self):
105 # Should simply print without error.
106 SpectractorShim.dumpParameters()
109class SpectractionParameterTestCase(lsst.utils.tests.TestCase):
110 """Tests for the parameter (over/supplement/reset) machinery."""
112 def setUp(self):
113 self.shim = SpectractorShim()
115 def test_overrideParametersValid(self):
116 original = parameters.VERBOSE
117 try:
118 self.shim.overrideParameters({'VERBOSE': True})
119 self.assertTrue(parameters.VERBOSE)
120 finally:
121 parameters.VERBOSE = original
123 def test_overrideParametersInvalidRaises(self):
124 with self.assertRaises(RuntimeError):
125 self.shim.overrideParameters({'THIS_PARAM_DOES_NOT_EXIST_XYZ': 1})
127 def test_supplementParametersNew(self):
128 name = 'MY_SUPPLEMENTARY_PARAM_XYZ'
129 self.assertNotIn(name, dir(parameters))
130 try:
131 self.shim.supplementParameters({name: 42})
132 self.assertEqual(getattr(parameters, name), 42)
133 finally:
134 if hasattr(parameters, name): 134 ↛ exitline 134 didn't return from function 'test_supplementParametersNew' because the condition on line 134 was always true
135 delattr(parameters, name)
137 def test_supplementParametersExistingWarns(self):
138 # Existing key should NOT be modified, only a warning emitted.
139 original = parameters.VERBOSE
140 try:
141 self.shim.supplementParameters({'VERBOSE': not original})
142 self.assertEqual(parameters.VERBOSE, original)
143 finally:
144 parameters.VERBOSE = original
146 def test_resetParameters(self):
147 name = 'MY_RESET_PARAM_XYZ'
148 try:
149 self.shim.resetParameters({name: 7})
150 self.assertEqual(getattr(parameters, name), 7)
151 # reset also works for existing attributes
152 original = parameters.VERBOSE
153 self.shim.resetParameters({'VERBOSE': not original})
154 self.assertEqual(parameters.VERBOSE, not original)
155 parameters.VERBOSE = original
156 finally:
157 if hasattr(parameters, name): 157 ↛ exitline 157 didn't return from function 'test_resetParameters' because the condition on line 157 was always true
158 delattr(parameters, name)
161class SpectractionInitTestCase(lsst.utils.tests.TestCase):
162 """Tests for the SpectractorShim constructor."""
164 @classmethod
165 def setUpClass(cls):
166 with importlib.resources.path("lsst.atmospec", "resources/config/auxtel.ini") as cfg:
167 cls.configFile = str(cfg)
169 def test_initDefault(self):
170 shim = SpectractorShim()
171 self.assertIsNotNone(shim.log)
173 def test_initWithConfigFile(self):
174 shim = SpectractorShim(configFile=self.configFile)
175 self.assertIsNotNone(shim.log)
177 def test_initWithParamDicts(self):
178 supName = 'INIT_SUPP_PARAM_XYZ'
179 resName = 'INIT_RESET_PARAM_XYZ'
180 original = parameters.VERBOSE
181 try:
182 shim = SpectractorShim(
183 paramOverrides={'VERBOSE': True},
184 supplementaryParameters={supName: 1},
185 resetParameters={resName: 2},
186 )
187 self.assertIsNotNone(shim.log)
188 self.assertTrue(parameters.VERBOSE)
189 self.assertEqual(getattr(parameters, supName), 1)
190 self.assertEqual(getattr(parameters, resName), 2)
191 finally:
192 parameters.VERBOSE = original
193 for name in (supName, resName):
194 if hasattr(parameters, name): 194 ↛ 193line 194 didn't jump to line 193 because the condition on line 194 was always true
195 delattr(parameters, name)
197 def test_initWithDebug(self):
198 original = parameters.DEBUG
199 try:
200 parameters.DEBUG = True
201 shim = SpectractorShim()
202 self.assertIsNotNone(shim.log)
203 finally:
204 parameters.DEBUG = original
207class SpectractionHelperTestCase(lsst.utils.tests.TestCase):
208 """Tests for the small IO / image helper methods."""
210 def setUp(self):
211 self.shim = SpectractorShim()
213 def test_makePathPlotting(self):
214 with tempfile.TemporaryDirectory() as tmp:
215 target = os.path.join(tmp, 'sub')
216 self.shim._makePath(target, plotting=True)
217 self.assertTrue(os.path.exists(os.path.join(target, 'plots')))
219 def test_makePathNoPlotting(self):
220 with tempfile.TemporaryDirectory() as tmp:
221 target = os.path.join(tmp, 'sub2')
222 self.shim._makePath(target, plotting=False)
223 self.assertTrue(os.path.exists(target))
224 # Calling again when it already exists should be a no-op.
225 self.shim._makePath(target, plotting=False)
226 self.assertTrue(os.path.exists(target))
228 def test_ensureFitsHeaderAdds(self):
229 obj = _makeSpectractorImage()
230 if 'SIMPLE' in obj.header: 230 ↛ 231line 230 didn't jump to line 231 because the condition on line 230 was never true
231 del obj.header['SIMPLE']
232 self.shim._ensureFitsHeader(obj)
233 self.assertIn('SIMPLE', obj.header)
235 def test_ensureFitsHeaderAlreadyPresent(self):
236 obj = _makeSpectractorImage()
237 obj.header['SIMPLE'] = True
238 self.shim._ensureFitsHeader(obj)
239 self.assertIn('SIMPLE', obj.header)
241 def test_getImageData(self):
242 exp = _makeExposure()
243 data = self.shim._getImageData(exp)
244 # transposed relative to exp.image.array
245 self.assertEqual(data.shape, exp.image.array.T.shape)
247 def test_getImageDataTrimToSquare(self):
248 exp = _makeExposure()
249 data = self.shim._getImageData(exp, trimToSquare=True)
250 # image is smaller than 4000, so trimming leaves it unchanged (then .T)
251 self.assertEqual(data.shape, exp.image.array.T.shape)
253 def test_setReadNoiseFromExpConst(self):
254 image = _makeSpectractorImage()
255 exp = _makeExposure()
256 self.shim._setReadNoiseFromExp(image, exp, constValue=3.0)
257 self.assertEqual(image.read_out_noise.shape, image.data.shape)
258 self.assertTrue(np.all(image.read_out_noise == 3.0))
260 def test_setReadNoiseFromExpNoneRaises(self):
261 image = _makeSpectractorImage()
262 exp = _makeExposure()
263 with self.assertRaises(NotImplementedError):
264 self.shim._setReadNoiseFromExp(image, exp, constValue=None)
266 def test_setReadNoiseToNone(self):
267 image = _makeSpectractorImage()
268 self.shim._setReadNoiseToNone(image)
269 self.assertIsNone(image.read_out_noise)
271 def test_setGainFromExpConst(self):
272 image = _makeSpectractorImage()
273 exp = _makeExposure()
274 gain = self.shim._setGainFromExp(image, exp, constValue=0.85)
275 self.assertEqual(gain.shape, image.data.shape)
276 self.assertTrue(np.all(gain == 0.85))
278 def test_setGainFromExpDefault(self):
279 image = _makeSpectractorImage()
280 exp = _makeExposure()
281 gain = self.shim._setGainFromExp(image, exp, constValue=None)
282 self.assertTrue(np.all(gain == 1.0))
284 def test_setStatErrorInImageComputed(self):
285 image = _makeSpectractorImage()
286 exp = _makeExposure()
287 image.read_out_noise = np.ones_like(image.data)
288 image.gain = np.ones_like(image.data)
289 self.shim._setStatErrorInImage(image, exp, useExpVariance=False)
290 self.assertIsNotNone(image.err)
292 def test_setStatErrorInImageFromVariance(self):
293 # useExpVariance=True must write to Image.err (renamed from the old
294 # Image.stat_errors, which no longer exists in Spectractor).
295 image = _makeSpectractorImage(shape=(24, 20))
296 exp = _makeExposure()
297 self.assertFalse(hasattr(Image, "stat_errors"))
298 self.shim._setStatErrorInImage(image, exp, useExpVariance=True)
299 np.testing.assert_array_equal(image.err, exp.maskedImage.variance.array)
301 def test_debugPrintTargetCentroidValue(self):
302 image = mock.MagicMock()
303 image.target_guess = (2.0, 3.0)
304 image.data = np.ones((10, 10))
305 # Should run without error.
306 self.shim.debugPrintTargetCentroidValue(image)
308 def test_setImageAndHeaderInfoVisitInfo(self):
309 image = _makeSpectractorImage()
310 exp = _makeExposure()
311 self.shim._setImageAndHeaderInfo(image, exp, useVisitInfo=True)
312 self.assertEqual(image.expo, 30.0)
313 self.assertEqual(image.header.filter, 'empty')
315 def test_setImageAndHeaderInfoMetadata(self):
316 image = _makeSpectractorImage()
317 exp = _makeExposure()
318 self.shim._setImageAndHeaderInfo(image, exp, useVisitInfo=False)
319 self.assertEqual(image.airmass, 1.1)
320 self.assertEqual(image.date_obs, '2021-01-01T00:00:00')
322 def test_setImageAndHeaderInfoException(self):
323 # Missing AIRMASS in the metadata triggers the exception branch,
324 # falling back to a default airmass of 1.
325 image = _makeSpectractorImage()
326 exp = _makeExposure()
327 exp.getMetadata().remove('AIRMASS')
328 self.shim._setImageAndHeaderInfo(image, exp, useVisitInfo=False)
329 self.assertEqual(image.header.airmass, 1.)
331 def test_displayImage(self):
332 image = mock.MagicMock()
333 image.data = np.ones((5, 6))
334 with mock.patch("lsst.afw.display.Display") as mockDisp:
335 self.shim.displayImage(image, centroid=(1, 2))
336 mockDisp.assert_called_once()
337 self.assertTrue(mockDisp.return_value.mtv.called)
338 self.assertTrue(mockDisp.return_value.dot.called)
340 def test_displayImageNoCentroid(self):
341 image = mock.MagicMock()
342 image.data = np.ones((5, 6))
343 with mock.patch("lsst.afw.display.Display") as mockDisp:
344 self.shim.displayImage(image, centroid=None)
345 self.assertFalse(mockDisp.return_value.dot.called)
348class SpectractionAdrTestCase(lsst.utils.tests.TestCase):
349 """Tests for setAdrParameters."""
351 def setUp(self):
352 self.shim = SpectractorShim()
354 def test_setAdrParametersValidWeather(self):
355 exp = _makeExposure(weather=afwCoord.Weather(10.0, 74300.0, 40.0))
356 spectrum = mock.MagicMock()
357 self.shim.setAdrParameters(spectrum, exp)
358 # pressure converted Pa -> hPa
359 self.assertAlmostEqual(spectrum.pressure, 743.0)
360 self.assertEqual(spectrum.temperature, 10.0)
361 self.assertEqual(spectrum.humidity, 40.0)
362 self.assertEqual(len(spectrum.adr_params), 6)
364 def test_setAdrParametersNanWeather(self):
365 nan = float('nan')
366 exp = _makeExposure(weather=afwCoord.Weather(nan, nan, nan))
367 spectrum = mock.MagicMock()
368 self.shim.setAdrParameters(spectrum, exp)
369 # falls back to nominal values
370 self.assertEqual(spectrum.temperature, 10)
371 self.assertEqual(spectrum.pressure, 743)
372 self.assertIsNone(spectrum.humidity)
374 def test_setAdrParametersLowPressure(self):
375 # pressure already in hPa (< 10000) should not be divided.
376 exp = _makeExposure(weather=afwCoord.Weather(10.0, 743.0, 40.0))
377 spectrum = mock.MagicMock()
378 self.shim.setAdrParameters(spectrum, exp)
379 self.assertAlmostEqual(spectrum.pressure, 743.0)
382class SpectractionImageBuildTestCase(lsst.utils.tests.TestCase):
383 """Tests for spectractorImageFromLsstExposure."""
385 @classmethod
386 def setUpClass(cls):
387 with importlib.resources.path("lsst.atmospec", "resources/config/auxtel.ini") as cfg:
388 cls.shim = SpectractorShim(configFile=str(cfg))
390 def test_spectractorImageFromLsstExposure(self):
391 exp = _makeExposure()
392 with mock.patch.object(spectractionModule, "Hologram"), \
393 mock.patch.object(Image, "compute_parallactic_angle"):
394 image = self.shim.spectractorImageFromLsstExposure(
395 exp, 5.0, 6.0, target_label='HD1',
396 disperser_label='holo4_003', filter_label='empty')
397 self.assertEqual(image.units, "ADU/s")
398 self.assertEqual(image.expo, 30.0)
399 # target_guess is the (y, x) translation of the input centroid
400 self.assertEqual(tuple(image.target_guess), (6.0, 5.0))
402 def test_hologramConstructedWithoutDeadKwargs(self):
403 # Spectractor's Disperser/Hologram no longer accept ``D`` or
404 # ``verbose``; the shim must call Hologram with only the supported
405 # kwargs (currently just data_dir).
406 exp = _makeExposure()
407 with mock.patch.object(spectractionModule, "Hologram") as mockHologram, \
408 mock.patch.object(Image, "compute_parallactic_angle"):
409 self.shim.spectractorImageFromLsstExposure(
410 exp, 5.0, 6.0, target_label='HD1',
411 disperser_label='holo4_003', filter_label='empty')
412 mockHologram.assert_called_once()
413 _, kwargs = mockHologram.call_args
414 self.assertNotIn('D', kwargs)
415 self.assertNotIn('verbose', kwargs)
417 def test_hologramSignatureRejectsDeadKwargs(self):
418 # Guard against the upstream signature regressing: the real Hologram
419 # must not accept ``D`` or ``verbose``.
420 import inspect
421 from spectractor.extractor.dispersers import Disperser, Hologram
422 params = set(inspect.signature(Hologram.__init__).parameters)
423 params |= set(inspect.signature(Disperser.__init__).parameters)
424 self.assertNotIn('D', params)
425 self.assertNotIn('verbose', params)
427 def test_spectractorImageFromLsstExposureDebug(self):
428 exp = _makeExposure()
429 original = parameters.DEBUG
430 try:
431 parameters.DEBUG = True
432 with mock.patch.object(spectractionModule, "Hologram"), \
433 mock.patch.object(Image, "compute_parallactic_angle"):
434 image = self.shim.spectractorImageFromLsstExposure(
435 exp, 5.0, 6.0, target_label='HD1',
436 disperser_label='holo4_003', filter_label='empty')
437 self.assertIsNotNone(image)
438 finally:
439 parameters.DEBUG = original
442class SpectractionRunTestCase(lsst.utils.tests.TestCase):
443 """Tests for the run() pipeline, with heavy mocking of Spectractor."""
445 @classmethod
446 def setUpClass(cls):
447 with importlib.resources.path("lsst.atmospec", "resources/config/auxtel.ini") as cfg:
448 cls.shim = SpectractorShim(configFile=str(cfg))
450 def setUp(self):
451 self._saved = {name: getattr(parameters, name) for name in (
452 'DEBUG', 'VERBOSE', 'CCD_REBIN', 'SPECTRACTOR_DECONVOLUTION_PSF2D',
453 'SPECTRACTOR_DECONVOLUTION_FFM', 'OBS_OBJECT_TYPE', 'DISPLAY')}
455 def tearDown(self):
456 for name, value in self._saved.items():
457 setattr(parameters, name, value)
459 def _runWithMocks(self, **runKwargs):
460 exp = _makeExposure()
461 spectrum = mock.MagicMock()
462 spectrum.order = 1
463 spectrum.lambdas = np.zeros(10)
464 image = mock.MagicMock()
465 image.data = np.ones((20, 24))
466 image.target_guess = (5.0, 6.0)
468 patches = {
469 "spectractorImageFromLsstExposure": mock.patch.object(
470 SpectractorShim, "spectractorImageFromLsstExposure", return_value=image),
471 "setAdrParameters": mock.patch.object(SpectractorShim, "setAdrParameters"),
472 "find_target": mock.patch.object(spectractionModule, "find_target"),
473 "turn_image": mock.patch.object(spectractionModule, "turn_image"),
474 "Spectrum": mock.patch.object(spectractionModule, "Spectrum", return_value=spectrum),
475 "extract": mock.patch.object(
476 spectractionModule, "extract_spectrum_from_image",
477 return_value=(mock.MagicMock(), mock.MagicMock())),
478 "psf2d": mock.patch.object(spectractionModule, "run_spectrogram_deconvolution_psf2d"),
479 "calibrate": mock.patch.object(spectractionModule, "calibrate_spectrum"),
480 "ffmWs": mock.patch.object(spectractionModule, "FullForwardModelFitWorkspace"),
481 "ffmRun": mock.patch.object(
482 spectractionModule, "run_ffm_minimisation", return_value=spectrum),
483 "specWs": mock.patch.object(spectractionModule, "SpectrumFitWorkspace"),
484 "specRun": mock.patch.object(spectractionModule, "run_spectrum_minimisation"),
485 "sgramWs": mock.patch.object(spectractionModule, "SpectrogramFitWorkspace"),
486 "sgramRun": mock.patch.object(spectractionModule, "run_spectrogram_minimisation"),
487 "rebin": mock.patch.object(spectractionModule, "apply_rebinning_to_parameters"),
488 }
489 started = {k: p.start() for k, p in patches.items()}
490 self.addCleanup(mock.patch.stopall)
491 result = self.shim.run(exp, 5.0, 6.0, "HD1", **runKwargs)
492 return result, spectrum, image, started
494 def test_runAllBranchesOn(self):
495 parameters.CCD_REBIN = 1
496 parameters.VERBOSE = False
497 parameters.DEBUG = False
498 parameters.SPECTRACTOR_DECONVOLUTION_PSF2D = True
499 parameters.SPECTRACTOR_DECONVOLUTION_FFM = True
500 parameters.OBS_OBJECT_TYPE = "STAR"
501 with tempfile.TemporaryDirectory() as tmp:
502 result, spectrum, image, started = self._runWithMocks(
503 doFitAtmosphere=True, doFitAtmosphereOnSpectrogram=True,
504 outputRoot=tmp, plotting=True)
505 self.assertIsInstance(result, Spectraction)
506 self.assertIs(result.spectrum, spectrum)
507 self.assertIs(result.image, image)
508 self.assertIsNotNone(result.spectrumForwardModelFitParameters)
509 self.assertIsNotNone(result.spectrumLibradtranFitParameters)
510 self.assertIsNotNone(result.spectrogramLibradtranFitParameters)
511 self.assertTrue(started["psf2d"].called)
512 self.assertTrue(started["specRun"].called)
513 self.assertTrue(started["sgramRun"].called)
515 def test_runAllBranchesOff(self):
516 parameters.CCD_REBIN = 1
517 parameters.VERBOSE = False
518 parameters.DEBUG = False
519 parameters.SPECTRACTOR_DECONVOLUTION_PSF2D = False
520 parameters.SPECTRACTOR_DECONVOLUTION_FFM = False
521 parameters.OBS_OBJECT_TYPE = "HG-AR" # triggers with_adr = False
522 result, spectrum, image, started = self._runWithMocks(
523 doFitAtmosphere=False, doFitAtmosphereOnSpectrogram=False,
524 outputRoot=None, plotting=True)
525 self.assertIsInstance(result, Spectraction)
526 self.assertIsNone(result.spectrumForwardModelFitParameters)
527 self.assertIsNone(result.spectrumLibradtranFitParameters)
528 self.assertIsNone(result.spectrogramLibradtranFitParameters)
529 self.assertFalse(started["psf2d"].called)
530 self.assertFalse(started["ffmRun"].called)
531 self.assertFalse(started["specRun"].called)
532 self.assertFalse(started["sgramRun"].called)
534 def test_extractSpectrumCalledWithoutRightEdge(self):
535 # Spectractor's extract_spectrum_from_image no longer accepts
536 # ``right_edge`` (it computes it internally from data_rotated), so the
537 # shim must not pass it.
538 parameters.CCD_REBIN = 1
539 parameters.VERBOSE = False
540 parameters.DEBUG = False
541 parameters.SPECTRACTOR_DECONVOLUTION_PSF2D = False
542 parameters.SPECTRACTOR_DECONVOLUTION_FFM = False
543 parameters.OBS_OBJECT_TYPE = "STAR"
544 result, spectrum, image, started = self._runWithMocks(
545 doFitAtmosphere=False, doFitAtmosphereOnSpectrogram=False,
546 outputRoot=None, plotting=False)
547 self.assertTrue(started["extract"].called)
548 _, kwargs = started["extract"].call_args
549 self.assertNotIn('right_edge', kwargs)
551 def test_extractSpectrumSignatureRejectsRightEdge(self):
552 # Guard against the upstream signature regressing: the real
553 # extract_spectrum_from_image must not accept ``right_edge``.
554 import inspect
555 from spectractor.extractor.extractor import extract_spectrum_from_image
556 params = inspect.signature(extract_spectrum_from_image).parameters
557 self.assertNotIn('right_edge', params)
559 def test_runDebugAndRebin(self):
560 parameters.CCD_REBIN = 2
561 parameters.VERBOSE = True
562 parameters.DEBUG = True
563 parameters.SPECTRACTOR_DECONVOLUTION_PSF2D = True
564 parameters.SPECTRACTOR_DECONVOLUTION_FFM = True
565 parameters.OBS_OBJECT_TYPE = "STAR"
566 result, spectrum, image, started = self._runWithMocks(
567 doFitAtmosphere=True, doFitAtmosphereOnSpectrogram=True,
568 outputRoot=None, plotting=True)
569 self.assertIsInstance(result, Spectraction)
570 # rebinning path was exercised
571 self.assertTrue(started["rebin"].called)
572 self.assertTrue(image.rebin.called)
575class SpectractionContainerTestCase(lsst.utils.tests.TestCase):
576 """Tests for the Spectraction data container."""
578 def test_instantiate(self):
579 result = Spectraction()
580 result.spectrum = "spectrum"
581 result.image = "image"
582 self.assertEqual(result.spectrum, "spectrum")
583 self.assertEqual(result.image, "image")
586class TestMemory(lsst.utils.tests.MemoryTestCase):
587 pass
590def setup_module(module):
591 lsst.utils.tests.init()
594if __name__ == "__main__": 594 ↛ 595line 594 didn't jump to line 595 because the condition on line 594 was never true
595 lsst.utils.tests.init()
596 unittest.main()