# Licensed under a 3-clause BSD style license - see LICENSE.rst from concurrent.futures import ThreadPoolExecutor from datetime import datetime, timedelta from pathlib import Path import erfa import pytest import astropy.time.core from astropy.time import Time, update_leap_seconds from astropy.utils import iers from astropy.utils.exceptions import AstropyWarning class TestUpdateLeapSeconds: def setup_method(self): self.built_in = iers.LeapSeconds.from_iers_leap_seconds() self.erfa_ls = iers.LeapSeconds.from_erfa() now = datetime.now() self.good_enough = now + timedelta(150) def teardown_method(self): self.erfa_ls.update_erfa_leap_seconds(initialize_erfa=True) def test_auto_update_leap_seconds(self): # Sanity check. assert erfa.dat(2018, 1, 1, 0.0) == 37.0 # Set expired leap seconds expired = self.erfa_ls[self.erfa_ls["year"] < 2017] expired.update_erfa_leap_seconds(initialize_erfa="empty") # Check the 2017 leap second is indeed missing. assert erfa.dat(2018, 1, 1, 0.0) == 36.0 # Update with missing leap seconds. n_update = update_leap_seconds([iers.IERS_LEAP_SECOND_FILE]) assert n_update >= 1 assert erfa.leap_seconds.expires == self.built_in.expires assert erfa.dat(2018, 1, 1, 0.0) == 37.0 # Doing it again does not change anything n_update2 = update_leap_seconds([iers.IERS_LEAP_SECOND_FILE]) assert n_update2 == 0 assert erfa.dat(2018, 1, 1, 0.0) == 37.0 @pytest.mark.remote_data def test_never_expired_if_connected(self): assert self.erfa_ls.expires > datetime.now() assert self.erfa_ls.expires >= self.good_enough @pytest.mark.remote_data def test_auto_update_always_good(self): self.erfa_ls.update_erfa_leap_seconds(initialize_erfa="only") update_leap_seconds() assert not erfa.leap_seconds.expired assert erfa.leap_seconds.expires > self.good_enough def test_auto_update_bad_file(self): with pytest.warns(AstropyWarning, match="FileNotFound"): update_leap_seconds(["nonsense"]) def test_auto_update_corrupt_file(self, tmp_path): bad_file = tmp_path / "no_expiration" lines = Path(iers.IERS_LEAP_SECOND_FILE).read_text().splitlines() bad_file.write_text( "\n".join(line for line in lines if not line.startswith("#")) ) with pytest.warns(AstropyWarning, match="ValueError.*did not find expiration"): update_leap_seconds([bad_file]) def test_auto_update_expired_file(self, tmp_path): # Set up expired ERFA leap seconds. expired = self.erfa_ls[self.erfa_ls["year"] < 2017] expired.update_erfa_leap_seconds(initialize_erfa="empty") # Create similarly expired file. expired_file = str(tmp_path / "expired.dat") Path(expired_file).write_text( "\n".join( ["# File expires on 28 June 2010"] + [str(item) for item in expired] ) ) with pytest.warns(iers.IERSStaleWarning): update_leap_seconds(["erfa", expired_file]) def test_init_thread_safety(self, monkeypatch): # Set up expired ERFA leap seconds. expired = self.erfa_ls[self.erfa_ls["year"] < 2017] expired.update_erfa_leap_seconds(initialize_erfa="empty") # Force re-initialization, even if another test already did it monkeypatch.setattr( astropy.time.core, "_LEAP_SECONDS_CHECK", astropy.time.core._LeapSecondsCheck.NOT_STARTED, ) workers = 4 with ThreadPoolExecutor(max_workers=workers) as executor: futures = [ executor.submit(lambda: str(Time("2019-01-01 00:00:00.000").tai)) for i in range(workers) ] results = [future.result() for future in futures] assert results == ["2019-01-01 00:00:37.000"] * workers