001package ca.uhn.fhir.util;
002
003/*
004 * #%L
005 * HAPI FHIR - Core Library
006 * %%
007 * Copyright (C) 2014 - 2022 Smile CDR, Inc.
008 * %%
009 * Licensed under the Apache License, Version 2.0 (the "License");
010 * you may not use this file except in compliance with the License.
011 * You may obtain a copy of the License at
012 *
013 * http://www.apache.org/licenses/LICENSE-2.0
014 *
015 * Unless required by applicable law or agreed to in writing, software
016 * distributed under the License is distributed on an "AS IS" BASIS,
017 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
018 * See the License for the specific language governing permissions and
019 * limitations under the License.
020 * #L%
021 */
022
023import ca.uhn.fhir.i18n.HapiLocalizer;
024import ca.uhn.fhir.i18n.Msg;
025
026import java.util.Locale;
027import java.util.TimeZone;
028import java.util.concurrent.Callable;
029import java.util.concurrent.atomic.AtomicInteger;
030
031import static org.apache.commons.lang3.StringUtils.defaultString;
032
033public class TestUtil {
034        private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(TestUtil.class);
035        private static boolean ourShouldRandomizeTimezones = true;
036
037        public static void setShouldRandomizeTimezones(boolean theShouldRandomizeTimezones) {
038                ourShouldRandomizeTimezones = theShouldRandomizeTimezones;
039        }
040
041        /**
042         * <b>THIS IS FOR UNIT TESTS ONLY - DO NOT CALL THIS METHOD FROM USER CODE</b>
043         * <p>
044         * When we run the unit tests in cobertura, JUnit doesn't seem to clean up static fields which leads to
045         * tons of memory being used by the end and the JVM crashes in Travis. Manually clearing all of the
046         * static fields seems to solve this.
047         */
048        public static void randomizeLocaleAndTimezone() {
049                HapiLocalizer.setOurFailOnMissingMessage(true);
050
051                doRandomizeLocaleAndTimezone();
052        }
053
054        /**
055         * Set some system properties randomly after each test.. this is kind of hackish,
056         * but it helps us make sure we don't have any tests that depend on a particular
057         * environment
058         */
059        public static void doRandomizeLocaleAndTimezone() {
060//              Locale[] availableLocales = {Locale.CANADA, Locale.GERMANY, Locale.TAIWAN};
061                Locale[] availableLocales = {Locale.US};
062                Locale.setDefault(availableLocales[(int) (Math.random() * availableLocales.length)]);
063                ourLog.info("Tests are running in locale: " + Locale.getDefault().getDisplayName());
064                if (Math.random() < 0.5) {
065                        ourLog.info("Tests are using WINDOWS line endings and ISO-8851-1");
066                        System.setProperty("file.encoding", "ISO-8859-1");
067                        System.setProperty("line.separator", "\r\n");
068                } else {
069                        ourLog.info("Tests are using UNIX line endings and UTF-8");
070                        System.setProperty("file.encoding", "UTF-8");
071                        System.setProperty("line.separator", "\n");
072                }
073
074                if (ourShouldRandomizeTimezones) {
075                        String availableTimeZones[] = {"GMT+08:00", "GMT-05:00", "GMT+00:00", "GMT+03:30"};
076                        String timeZone = availableTimeZones[(int) (Math.random() * availableTimeZones.length)];
077                        TimeZone.setDefault(TimeZone.getTimeZone(timeZone));
078                }
079
080                ourLog.info("Tests are using time zone: {}", TimeZone.getDefault().getID());
081        }
082
083
084        /**
085         * <b>THIS IS FOR UNIT TESTS ONLY - DO NOT CALL THIS METHOD FROM USER CODE</b>
086         * <p>
087         * Wait for an atomicinteger to hit a given site and fail if it never does
088         */
089        public static void waitForSize(int theTarget, AtomicInteger theInteger) {
090                long start = System.currentTimeMillis();
091                while (theInteger.get() != theTarget && (System.currentTimeMillis() - start) <= 15000) {
092                        try {
093                                Thread.sleep(50);
094                        } catch (InterruptedException theE) {
095                                throw new Error(Msg.code(1778) + theE);
096                        }
097                }
098                if ((System.currentTimeMillis() - start) >= 15000) {
099                        throw new IllegalStateException(Msg.code(1779) + "Size " + theInteger.get() + " is != target " + theTarget);
100                }
101        }
102
103        /**
104         * <b>THIS IS FOR UNIT TESTS ONLY - DO NOT CALL THIS METHOD FROM USER CODE</b>
105         * <p>
106         * Wait for an atomicinteger to hit a given site and fail if it never does
107         */
108        public static void waitForSize(int theTarget, Callable<Integer> theSource) throws Exception {
109                long start = System.currentTimeMillis();
110                while (theSource.call() != theTarget && (System.currentTimeMillis() - start) <= 15000) {
111                        try {
112                                Thread.sleep(50);
113                        } catch (InterruptedException theE) {
114                                throw new Error(Msg.code(1780) + theE);
115                        }
116                }
117                if ((System.currentTimeMillis() - start) >= 15000) {
118                        throw new IllegalStateException(Msg.code(1781) + "Size " + theSource.call() + " is != target " + theTarget);
119                }
120        }
121
122        /**
123         * <b>THIS IS FOR UNIT TESTS ONLY - DO NOT CALL THIS METHOD FROM USER CODE</b>
124         * <p>
125         * Strip \r chars from a string to account for line ending platform differences
126         */
127        public static String stripReturns(String theString) {
128                return defaultString(theString).replace("\r", "");
129        }
130
131        /**
132         * <b>THIS IS FOR UNIT TESTS ONLY - DO NOT CALL THIS METHOD FROM USER CODE</b>
133         * <p>
134         * Strip \r chars from a string to account for line ending platform differences
135         */
136        public static String stripWhitespace(String theString) {
137                return stripReturns(theString).replace(" ", "");
138        }
139
140        public static void sleepAtLeast(long theMillis) {
141                sleepAtLeast(theMillis, true);
142        }
143
144
145        @SuppressWarnings("BusyWait")
146        public static void sleepAtLeast(long theMillis, boolean theLogProgress) {
147                long start = System.currentTimeMillis();
148                while (System.currentTimeMillis() <= start + theMillis) {
149                        try {
150                                long timeSinceStarted = System.currentTimeMillis() - start;
151                                long timeToSleep = Math.max(0, theMillis - timeSinceStarted);
152                                if (theLogProgress) {
153                                        ourLog.info("Sleeping for {}ms", timeToSleep);
154                                }
155                                Thread.sleep(timeToSleep);
156                        } catch (InterruptedException e) {
157                                Thread.currentThread().interrupt();
158                                ourLog.error("Interrupted", e);
159                        }
160                }
161        }
162}