001package ca.uhn.fhir.util; 002 003/* 004 * #%L 005 * HAPI FHIR - Core Library 006 * %% 007 * Copyright (C) 2014 - 2021 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.context.FhirContext; 024import ca.uhn.fhir.i18n.HapiLocalizer; 025import ch.qos.logback.classic.Level; 026import ch.qos.logback.classic.Logger; 027import ch.qos.logback.classic.LoggerContext; 028import org.slf4j.LoggerFactory; 029 030import java.lang.reflect.Field; 031import java.lang.reflect.Modifier; 032import java.util.Arrays; 033import java.util.Locale; 034import java.util.TimeZone; 035import java.util.concurrent.Callable; 036import java.util.concurrent.atomic.AtomicInteger; 037 038import static org.apache.commons.lang3.StringUtils.defaultString; 039 040public class TestUtil { 041 private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(TestUtil.class); 042 private static boolean ourShouldRandomizeTimezones = true; 043 044 public static void setShouldRandomizeTimezones(boolean theShouldRandomizeTimezones) { 045 ourShouldRandomizeTimezones = theShouldRandomizeTimezones; 046 } 047 048 /** 049 * <b>THIS IS FOR UNIT TESTS ONLY - DO NOT CALL THIS METHOD FROM USER CODE</b> 050 * <p> 051 * When we run the unit tests in cobertura, JUnit doesn't seem to clean up static fields which leads to 052 * tons of memory being used by the end and the JVM crashes in Travis. Manually clearing all of the 053 * static fields seems to solve this. 054 */ 055 public static void clearAllStaticFieldsForUnitTest() { 056 HapiLocalizer.setOurFailOnMissingMessage(true); 057 058 Class<?> theType; 059 try { 060 throw new Exception(); 061 } catch (Exception e) { 062 StackTraceElement[] st = e.getStackTrace(); 063 StackTraceElement elem = st[1]; 064 String clazzName = elem.getClassName(); 065 try { 066 theType = Class.forName(clazzName); 067 } catch (ClassNotFoundException e1) { 068 throw new Error(e); 069 } 070 } 071 072 for (Field next : Arrays.asList(theType.getDeclaredFields())) { 073 if (Modifier.isStatic(next.getModifiers())) { 074 if (!Modifier.isFinal(next.getModifiers()) && !next.getType().isPrimitive()) { 075 ourLog.info("Clearing value of field: {}", next.toString()); 076 try { 077 next.setAccessible(true); 078 next.set(theType, null); 079 } catch (Exception e) { 080 throw new Error(e); 081 } 082 } 083 if (Modifier.isFinal(next.getModifiers())) { 084 if (next.getType().equals(FhirContext.class)) { 085 throw new Error("Test has final field of type FhirContext: " + next); 086 } 087 } 088 } 089 090 } 091 092 randomizeLocale(); 093 094 /* 095 * If we're running a CI build, set all loggers to TRACE level to ensure coverage 096 * on trace blocks 097 */ 098 try { 099 if ("true".equals(System.getProperty("ci"))) { 100 for (Logger next : ((LoggerContext) LoggerFactory.getILoggerFactory()).getLoggerList()) { 101 next.setLevel(Level.TRACE); 102 } 103 } 104 } catch (NoClassDefFoundError e) { 105 // ignore 106 } 107 } 108 109 /** 110 * Set some system properties randomly after each test.. this is kind of hackish, 111 * but it helps us make sure we don't have any tests that depend on a particular 112 * environment 113 */ 114 public static void randomizeLocale() { 115// Locale[] availableLocales = {Locale.CANADA, Locale.GERMANY, Locale.TAIWAN}; 116 Locale[] availableLocales = {Locale.US}; 117 Locale.setDefault(availableLocales[(int) (Math.random() * availableLocales.length)]); 118 ourLog.info("Tests are running in locale: " + Locale.getDefault().getDisplayName()); 119 if (Math.random() < 0.5) { 120 ourLog.info("Tests are using WINDOWS line endings and ISO-8851-1"); 121 System.setProperty("file.encoding", "ISO-8859-1"); 122 System.setProperty("line.separator", "\r\n"); 123 } else { 124 ourLog.info("Tests are using UNIX line endings and UTF-8"); 125 System.setProperty("file.encoding", "UTF-8"); 126 System.setProperty("line.separator", "\n"); 127 } 128 129 if (ourShouldRandomizeTimezones) { 130 String availableTimeZones[] = {"GMT+08:00", "GMT-05:00", "GMT+00:00", "GMT+03:30"}; 131 String timeZone = availableTimeZones[(int) (Math.random() * availableTimeZones.length)]; 132 TimeZone.setDefault(TimeZone.getTimeZone(timeZone)); 133 } 134 135 ourLog.info("Tests are using time zone: {}", TimeZone.getDefault().getID()); 136 } 137 138 139 /** 140 * <b>THIS IS FOR UNIT TESTS ONLY - DO NOT CALL THIS METHOD FROM USER CODE</b> 141 * <p> 142 * Wait for an atomicinteger to hit a given site and fail if it never does 143 */ 144 public static void waitForSize(int theTarget, AtomicInteger theInteger) { 145 long start = System.currentTimeMillis(); 146 while (theInteger.get() != theTarget && (System.currentTimeMillis() - start) <= 15000) { 147 try { 148 Thread.sleep(50); 149 } catch (InterruptedException theE) { 150 throw new Error(theE); 151 } 152 } 153 if ((System.currentTimeMillis() - start) >= 15000) { 154 throw new IllegalStateException("Size " + theInteger.get() + " is != target " + theTarget); 155 } 156 } 157 158 /** 159 * <b>THIS IS FOR UNIT TESTS ONLY - DO NOT CALL THIS METHOD FROM USER CODE</b> 160 * <p> 161 * Wait for an atomicinteger to hit a given site and fail if it never does 162 */ 163 public static void waitForSize(int theTarget, Callable<Integer> theSource) throws Exception { 164 long start = System.currentTimeMillis(); 165 while (theSource.call() != theTarget && (System.currentTimeMillis() - start) <= 15000) { 166 try { 167 Thread.sleep(50); 168 } catch (InterruptedException theE) { 169 throw new Error(theE); 170 } 171 } 172 if ((System.currentTimeMillis() - start) >= 15000) { 173 throw new IllegalStateException("Size " + theSource.call() + " is != target " + theTarget); 174 } 175 } 176 177 /** 178 * <b>THIS IS FOR UNIT TESTS ONLY - DO NOT CALL THIS METHOD FROM USER CODE</b> 179 * <p> 180 * Strip \r chars from a string to account for line ending platform differences 181 */ 182 public static String stripReturns(String theString) { 183 return defaultString(theString).replace("\r", ""); 184 } 185 186 /** 187 * <b>THIS IS FOR UNIT TESTS ONLY - DO NOT CALL THIS METHOD FROM USER CODE</b> 188 * <p> 189 * Strip \r chars from a string to account for line ending platform differences 190 */ 191 public static String stripWhitespace(String theString) { 192 return stripReturns(theString).replace(" ", ""); 193 } 194 195}