1   /* Copyright 2002-2025 CS GROUP
2    * Licensed to CS GROUP (CS) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * CS licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *   http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.orekit.time;
19  
20  import static org.junit.jupiter.api.Assertions.assertNotNull;
21  import static org.junit.jupiter.api.Assertions.fail;
22  
23  import java.util.ConcurrentModificationException;
24  import java.util.concurrent.ExecutionException;
25  import java.util.concurrent.ExecutorService;
26  import java.util.concurrent.Executors;
27  import java.util.concurrent.Future;
28  import org.junit.jupiter.api.Assertions;
29  import org.junit.jupiter.api.BeforeEach;
30  import org.junit.jupiter.api.RepeatedTest;
31  import org.junit.jupiter.api.Test;
32  import org.orekit.Utils;
33  import org.orekit.data.LazyLoadedDataContext;
34  import org.orekit.utils.IERSConventions;
35  
36  import java.util.ArrayList;
37  import java.util.List;
38  
39  /**
40   * Unit tests for {@link LazyLoadedTimeScales}.
41   *
42   * @author Evan Ward
43   */
44  public class LazyLoadedTimeScalesTest {
45  
46      /** Subject under test. */
47      private LazyLoadedTimeScales timeScales;
48  
49      /** Create subject under test. */
50      @BeforeEach
51      public void setUp() {
52          LazyLoadedDataContext defaultContext =
53                  (LazyLoadedDataContext) Utils.setDataRoot("regular-data");
54          timeScales = defaultContext.getTimeScales();
55      }
56  
57      /**
58       * Check {@link LazyLoadedTimeScales#getGMST(IERSConventions, boolean)} uses it's
59       * parameters. See issue #627.
60       */
61      @Test
62      public void testGetGMST() {
63          DateTimeComponents reference = new DateTimeComponents(2000, 1, 1, 12, 0, 0.0);
64          List<TimeScale> scales = new ArrayList<>();
65  
66          for (IERSConventions conventions : IERSConventions.values()) {
67              for (boolean simpleEop : new boolean[]{true, false}) {
68                  // setup
69                  GMSTScale gmst = timeScales.getGMST(conventions, simpleEop);
70                  UT1Scale ut1 = timeScales.getUT1(conventions, simpleEop);
71                  AbsoluteDate date = new AbsoluteDate(reference, ut1);
72                  String message = conventions + " " + simpleEop;
73  
74                  // verify
75                  Assertions.assertSame(gmst, timeScales.getGMST(conventions, simpleEop), message);
76                  double expected = 24110.54841 + ut1.offsetFromTAI(date).toDouble();
77                  Assertions.assertEquals(expected, gmst.offsetFromTAI(date).toDouble(), 0, message);
78                  Assertions.assertTrue(!scales.contains(gmst), message + " " + scales);
79                  scales.add(gmst);
80              }
81          }
82      }
83  
84      /** Check {@link LazyLoadedTimeScales#getUT1(IERSConventions, boolean)}. */
85      @Test
86      public void testGetUt1() {
87          UTCScale utc = timeScales.getUTC();
88          DateTimeComponents reference = new DateTimeComponents(2004, 2, 1);
89          AbsoluteDate date = new AbsoluteDate(reference, utc);
90          List<TimeScale> scales = new ArrayList<>();
91  
92          for (IERSConventions conventions : IERSConventions.values()) {
93              for (boolean simpleEop : new boolean[]{true, false}) {
94                  // setup
95                  UT1Scale ut1 = timeScales.getUT1(conventions, simpleEop);
96                  String message = conventions + " " + simpleEop;
97  
98                  // verify
99                  Assertions.assertSame(ut1, timeScales.getUT1(conventions, simpleEop), message);
100                 Assertions.assertSame(ut1.getEOPHistory().getConventions(), conventions);
101                 double expected = utc.offsetFromTAI(date).toDouble();
102                 if (conventions != IERSConventions.IERS_1996) {
103                     expected += -0.4051590;
104                 }
105                 if (!simpleEop) {
106                     expected += conventions.getEOPTidalCorrection(timeScales).value(date)[2];
107                 }
108                 Assertions.assertEquals(expected, ut1.offsetFromTAI(date).toDouble(), 0, message);
109                 Assertions.assertTrue(!scales.contains(ut1), message + " " + scales);
110                 scales.add(ut1);
111             }
112         }
113     }
114 
115     /**
116      * Tests fix for issue-1296
117      */
118     @RepeatedTest(10)
119     void testGetUtcLazyInit() throws InterruptedException, ExecutionException {
120         LazyLoadedDataContext defaultContext =
121             (LazyLoadedDataContext) Utils.setDataRoot("regular-data");
122 
123         LazyLoadedTimeScales ts = defaultContext.getTimeScales();
124 
125         ExecutorService executorService = Executors.newFixedThreadPool(100);
126         try {
127             List<Future<UTCScale>> futures = new ArrayList<>();
128             for (int i = 0; i < 100; i++) {
129                 futures.add(executorService.submit(ts::getUTC));
130             }
131 
132             for (Future<?> f : futures) {
133                 try {
134                     assertNotNull(f.get());
135                 } catch (ExecutionException e) {
136                     if (e.getCause() instanceof ConcurrentModificationException) {
137                         fail("ConcurrentModificationException detected, not thread safe");
138                     }
139                     throw e;
140                 }
141             }
142 
143         } finally {
144             executorService.shutdown();
145         }
146     }
147 
148 }