Commit 8cd7bd65 authored by vertighel's avatar vertighel
Browse files

Using 'with' context manager and hdu[0].copy() in load_fits, to allow...

Using 'with' context manager and hdu[0].copy() in load_fits, to allow subsequent I/O errors while calling write_noctis_header. Updated unit tests
parent 3f2e89e8
Loading
Loading
Loading
Loading
Loading
+17 −9
Original line number Diff line number Diff line
@@ -131,10 +131,10 @@ class Noche:
    def fill_keyword(self, k, val, silent=False):
        """
        Fill new header keyword with its value.
        Why use this function instea of simply self.header[keyword] = value?
        Why use this function instead of simply self.header[keyword] = value?
        Because it also takes care of the format, rounding,
        and and significant digits
        taken from header tempalte.
        taken from header template.

        Parameters
        ----------
@@ -152,7 +152,12 @@ class Noche:
        
        if typ.strip() == "float":
            with custom_float():
                if val:
                    val = header_round(val, int(fmt))
                elif val == 0.0:
                    val = header_round(val, int(fmt))                
                else:
                    val = None

        self.header[k] = val
        
@@ -215,8 +220,11 @@ class Noche:

            val = loc[k]

            if val is None:
                log.warning(f"{k:<8} : fixed value missing! Leave blank")
            if val == None:
                log.warning(f"{k:<8} : fixed value None! Leave blank")
                pass
            if val == "":
                log.warning(f"{k:<8} : fixed value empty! Leave blank")
                pass
            else:
                log.info(f"{k:<11} : fixed to the value : {val:<28}")
@@ -582,11 +590,11 @@ class Noche:
        log.debug(sys._getframe().f_code.co_name)

        filename = fits_file
        hdul = fits.open(filename)
        self.hdu = hdul[0]

        with fits.open(filename) as hdul:
            self.hdu = hdul[0].copy() # to avoid I/O issues with writing

    def write_noctis_fits(self, filename=None, overwrite=True):

        data = self.hdu.data # Original data
        original_hdu = fits.ImageHDU(header=self.hdu.header, name="ORIGINAL")
        noche_hdu = fits.PrimaryHDU(data=data, header=self.header)
+130 −46
Original line number Diff line number Diff line
# import unittest
# import os
# import argparse
# import sys
# from glob import glob
# from noche import Noche

# class TestNoche(unittest.TestCase):

#     def test_process_all_observatory_files(self):
#         print(f"\n--- Avvio Test: Processazione file Noche ---")

#         for obs in os.listdir("../examples/"):
#             print(obs)
#             for f in filter(lambda x: x[0] != '~', glob(f'../examples/{obs}/*')):
#                 print(f)
#                 try:
#                     print("Instanciate Noche")
#                     n = Noche()
#                     print("Process")
#                     n.load_noctis_observatory(obs, f)
#                     print("Write")
#                     filename = os.path.basename(f)
#                     n.write_noctis_fits(f"./examples-tested/processed_{filename}")
#                     print("Done")
#                     del n
                    
#                 except Exception as e:
#                     print(f"Il test è fallito per {obs}/{f} con l'errore: {e}")
#                     raise
        

# if __name__ == '__main__':    
#     unittest.main(argv=sys.argv, exit=False)

import unittest
from astropy.time import Time
import os
import sys
from glob import glob
from noche import Noche 
from astropy.io.fits import Header
import argparse 

class TestNoche(unittest.TestCase):
# --- Configurazione e Rilevamento File ---

# Directory radice per gli esempi
EXAMPLES_ROOT = "../examples"
output_dir = "./examples-tested"

# Lista per contenere le tuple (nome_osservatorio, percorso_file)
TEST_FILES = []

# Rilevamento dinamico dei file di test
if os.path.exists(EXAMPLES_ROOT):
    # Itera attraverso i contenuti della directory degli esempi (i nomi degli osservatori)
    for obs in os.listdir(EXAMPLES_ROOT):
        obs_path = os.path.join(EXAMPLES_ROOT, obs)
        
        # Assicurati che sia una directory (l'osservatorio)
        if os.path.isdir(obs_path): 
            # Cerca tutti i file all'interno della directory dell'osservatorio
            for f in glob(os.path.join(obs_path, '*')):
                filename = os.path.basename(f)
                
                # Applica il filtro originale: escludi i file che iniziano con '~'
                if not filename.startswith('~'):
                    TEST_FILES.append((obs, f))

# --- Funzione per la Creazione Dinamica dei Test ---

def create_test_for_file(observatory_name, file_path):
    """Genera un metodo unittest per un file specifico."""
    
    def generated_test_method(self):
        filename = os.path.basename(file_path)
        print(f"\n--- Avvio Test: {observatory_name}/{filename} ---")
        
    def setUp(self):
        self.noche = Noche("noche/headers/header_base.ini")
        self.noche.load_observatory("noche/observatories/oarpaf.ini")

    def test_init_creates_header(self):
        self.assertIsInstance(self.noche.header, Header)

    def test_set_location(self):
        self.noche.set_location(10.0, 45.0, 100.0)
        self.assertEqual(self.noche.header['OBS-LONG'], 10.0)
        self.assertEqual(self.noche.header['OBS-LAT'], 45.0)
        self.assertEqual(self.noche.header['OBS-ELEV'], 100.0)

    def test_set_obstime(self):
        now = Time.now()
        self.noche.set_obstime(now)
        self.assertEqual(self.noche.header['DATE-OBS'], now.isot)
        self.assertAlmostEqual(self.noche.header['MJD-OBS'], now.mjd, places=5)

    def test_set_coordinates(self):
        self.noche.set_coordinates(ra=10.0, dec=20.0)
        self.assertIn('RA', self.noche.header)
        self.assertIn('DEC', self.noche.header)
        self.assertAlmostEqual(self.noche.header['RA_DEG'], 150.0, places=4)  # 10h -> 150deg
        self.assertAlmostEqual(self.noche.header['DEC_DEG'], 20.0, places=4)

    def test_set_object_and_resolve(self):
        self.noche.set_object("Vega")
        self.assertEqual(self.noche.header['OBJECT'], "Vega")

    def test_check_empty_does_not_raise(self):
        # Should not raise even if empty fields exist
        try:
            self.noche.check_empty()
            # 1. Instanzia Noche
            n = Noche() 
            
            # 2. Processa il file
            n.load_noctis_observatory(observatory_name, file_path)
            
            # 3. Scrivi l'output (Crea la directory di output se non esiste)
            os.makedirs(output_dir, exist_ok=True)
            
            output_path = os.path.join(output_dir, f"processed_{filename}")
            n.write_noctis_fits(output_path)
            
            print(f"Successo: {observatory_name}/{filename}")
            
            # 4. Asserzione: verifica che il file di output sia stato creato
            self.assertTrue(os.path.exists(output_path), 
                            f"Il file di output non è stato creato: {output_path}")
            
            # # 5. Pulizia
            # del n
            
        except Exception as e:
            self.fail(f"check_empty() raised an exception: {e}")
            # Cattura l'errore, stampa un messaggio descrittivo e fallisce il test
            print(f"Il test è fallito per {observatory_name}/{filename} con l'errore: {e}")
            raise # Ri-lancia l'eccezione per far fallire il singolo test

    # Crea un nome di metodo sicuro e descrittivo per unittest
    # Sostituisce caratteri problematici (come . e -) con _
    safe_name = os.path.basename(file_path).replace('.', '_').replace('-', '_')
    test_method_name = f"test_process_{observatory_name}_{safe_name}"
    generated_test_method.__name__ = test_method_name
    
    return generated_test_method

# --- Definizione TestCase ---

class TestNoche(unittest.TestCase):
    """
    TestCase popolato dinamicamente con un metodo di test per ogni file 
    fits trovato nelle directory degli esempi.
    """
    pass

# Popolamento dinamico della classe TestNoche con i metodi di test
for obs_name, file_path in TEST_FILES:
    test_func = create_test_for_file(obs_name, file_path)
    # Assegna il metodo di test alla classe TestCase
    setattr(TestNoche, test_func.__name__, test_func)

# Se non vengono trovati file, aggiungi un test segnaposto per evitare 'no tests found'
if not TEST_FILES:
    def test_no_files_found(self):
        print(f"\n[ATTENZIONE] Nessun file di esempio trovato in {EXAMPLES_ROOT}. Test ignorato.")
        self.skipTest(f"Nessun file di esempio trovato in {EXAMPLES_ROOT}.")
    setattr(TestNoche, 'test_no_files_found', test_no_files_found)
    
    def test_parse_various_types(self):
        self.assertEqual(self.noche._parse("42"), 42)
        self.assertEqual(self.noche._parse("3.14"), 3.14)
        self.assertEqual(self.noche._parse("True"), True)
        self.assertEqual(self.noche._parse("False"), False)
        self.assertEqual(self.noche._parse("some string"), "some string")
        self.assertIsNone(self.noche._parse("   "))
# --- Esecuzione ---

if __name__ == '__main__':
    unittest.main()
    # Esegue tutti i test trovati nella classe TestNoche
    unittest.main(argv=sys.argv, exit=False)