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