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;
024
025import java.util.Locale;
026import java.util.TimeZone;
027import java.util.concurrent.Callable;
028import java.util.concurrent.atomic.AtomicInteger;
029
030import static org.apache.commons.lang3.StringUtils.defaultString;
031
032public class TestUtil {
033        private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(TestUtil.class);
034        private static boolean ourShouldRandomizeTimezones = true;
035
036        public static void setShouldRandomizeTimezones(boolean theShouldRandomizeTimezones) {
037                ourShouldRandomizeTimezones = theShouldRandomizeTimezones;
038        }
039
040        /**
041         * <b>THIS IS FOR UNIT TESTS ONLY - DO NOT CALL THIS METHOD FROM USER CODE</b>
042         * <p>
043         * When we run the unit tests in cobertura, JUnit doesn't seem to clean up static fields which leads to
044         * tons of memory being used by the end and the JVM crashes in Travis. Manually clearing all of the
045         * static fields seems to solve this.
046         */
047        public static void randomizeLocaleAndTimezone() {
048                HapiLocalizer.setOurFailOnMissingMessage(true);
049
050                doRandomizeLocaleAndTimezone();
051        }
052
053        /**
054         * Set some system properties randomly after each test.. this is kind of hackish,
055         * but it helps us make sure we don't have any tests that depend on a particular
056         * environment
057         */
058        public static void doRandomizeLocaleAndTimezone() {
059//              Locale[] availableLocales = {Locale.CANADA, Locale.GERMANY, Locale.TAIWAN};
060                Locale[] availableLocales = {Locale.US};
061                Locale.setDefault(availableLocales[(int) (Math.random() * availableLocales.length)]);
062                ourLog.info("Tests are running in locale: " + Locale.getDefault().getDisplayName());
063                if (Math.random() < 0.5) {
064                        ourLog.info("Tests are using WINDOWS line endings and ISO-8851-1");
065                        System.setProperty("file.encoding", "ISO-8859-1");
066                        System.setProperty("line.separator", "\r\n");
067                } else {
068                        ourLog.info("Tests are using UNIX line endings and UTF-8");
069                        System.setProperty("file.encoding", "UTF-8");
070                        System.setProperty("line.separator", "\n");
071                }
072
073                if (ourShouldRandomizeTimezones) {
074                        String availableTimeZones[] = {"GMT+08:00", "GMT-05:00", "GMT+00:00", "GMT+03:30"};
075                        String timeZone = availableTimeZones[(int) (Math.random() * availableTimeZones.length)];
076                        TimeZone.setDefault(TimeZone.getTimeZone(timeZone));
077                }
078
079                ourLog.info("Tests are using time zone: {}", TimeZone.getDefault().getID());
080        }
081
082
083        /**
084         * <b>THIS IS FOR UNIT TESTS ONLY - DO NOT CALL THIS METHOD FROM USER CODE</b>
085         * <p>
086         * Wait for an atomicinteger to hit a given site and fail if it never does
087         */
088        public static void waitForSize(int theTarget, AtomicInteger theInteger) {
089                long start = System.currentTimeMillis();
090                while (theInteger.get() != theTarget && (System.currentTimeMillis() - start) <= 15000) {
091                        try {
092                                Thread.sleep(50);
093                        } catch (InterruptedException theE) {
094                                throw new Error(theE);
095                        }
096                }
097                if ((System.currentTimeMillis() - start) >= 15000) {
098                        throw new IllegalStateException("Size " + theInteger.get() + " is != target " + theTarget);
099                }
100        }
101
102        /**
103         * <b>THIS IS FOR UNIT TESTS ONLY - DO NOT CALL THIS METHOD FROM USER CODE</b>
104         * <p>
105         * Wait for an atomicinteger to hit a given site and fail if it never does
106         */
107        public static void waitForSize(int theTarget, Callable<Integer> theSource) throws Exception {
108                long start = System.currentTimeMillis();
109                while (theSource.call() != theTarget && (System.currentTimeMillis() - start) <= 15000) {
110                        try {
111                                Thread.sleep(50);
112                        } catch (InterruptedException theE) {
113                                throw new Error(theE);
114                        }
115                }
116                if ((System.currentTimeMillis() - start) >= 15000) {
117                        throw new IllegalStateException("Size " + theSource.call() + " is != target " + theTarget);
118                }
119        }
120
121        /**
122         * <b>THIS IS FOR UNIT TESTS ONLY - DO NOT CALL THIS METHOD FROM USER CODE</b>
123         * <p>
124         * Strip \r chars from a string to account for line ending platform differences
125         */
126        public static String stripReturns(String theString) {
127                return defaultString(theString).replace("\r", "");
128        }
129
130        /**
131         * <b>THIS IS FOR UNIT TESTS ONLY - DO NOT CALL THIS METHOD FROM USER CODE</b>
132         * <p>
133         * Strip \r chars from a string to account for line ending platform differences
134         */
135        public static String stripWhitespace(String theString) {
136                return stripReturns(theString).replace(" ", "");
137        }
138
139        public static void sleepAtLeast(long theMillis) {
140                sleepAtLeast(theMillis, true);
141        }
142
143
144        @SuppressWarnings("BusyWait")
145        public static void sleepAtLeast(long theMillis, boolean theLogProgress) {
146                long start = System.currentTimeMillis();
147                while (System.currentTimeMillis() <= start + theMillis) {
148                        try {
149                                long timeSinceStarted = System.currentTimeMillis() - start;
150                                long timeToSleep = Math.max(0, theMillis - timeSinceStarted);
151                                if (theLogProgress) {
152                                        ourLog.info("Sleeping for {}ms", timeToSleep);
153                                }
154                                Thread.sleep(timeToSleep);
155                        } catch (InterruptedException e) {
156                                Thread.currentThread().interrupt();
157                                ourLog.error("Interrupted", e);
158                        }
159                }
160        }
161}