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}