View Javadoc
1   package ca.uhn.fhir.util;
2   
3   /*
4    * #%L
5    * HAPI FHIR - Core Library
6    * %%
7    * Copyright (C) 2014 - 2018 University Health Network
8    * %%
9    * Licensed under the Apache License, Version 2.0 (the "License");
10   * you may not use this file except in compliance with the License.
11   * You may obtain a copy of the License at
12   * 
13   * http://www.apache.org/licenses/LICENSE-2.0
14   * 
15   * Unless required by applicable law or agreed to in writing, software
16   * distributed under the License is distributed on an "AS IS" BASIS,
17   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18   * See the License for the specific language governing permissions and
19   * limitations under the License.
20   * #L%
21   */
22  
23  import ca.uhn.fhir.context.FhirContext;
24  import ca.uhn.fhir.i18n.HapiLocalizer;
25  import ch.qos.logback.classic.Level;
26  import ch.qos.logback.classic.Logger;
27  import ch.qos.logback.classic.LoggerContext;
28  import org.slf4j.LoggerFactory;
29  
30  import java.lang.reflect.Field;
31  import java.lang.reflect.Modifier;
32  import java.util.Arrays;
33  import java.util.Locale;
34  import java.util.TimeZone;
35  import java.util.concurrent.Callable;
36  import java.util.concurrent.atomic.AtomicInteger;
37  
38  import static org.apache.commons.lang3.StringUtils.defaultString;
39  
40  public class TestUtil {
41  	private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(TestUtil.class);
42  
43  	/**
44  	 * <b>THIS IS FOR UNIT TESTS ONLY - DO NOT CALL THIS METHOD FROM USER CODE</b>
45  	 * <p>
46  	 * When we run the unit tests in cobertura, JUnit doesn't seem to clean up static fields which leads to
47  	 * tons of memory being used by the end and the JVM crashes in Travis. Manually clearing all of the
48  	 * static fields seems to solve this.
49  	 */
50  	public static void clearAllStaticFieldsForUnitTest() {
51  		HapiLocalizer.setOurFailOnMissingMessage(true);
52  
53  		Class<?> theType;
54  		try {
55  			throw new Exception();
56  		} catch (Exception e) {
57  			StackTraceElement[] st = e.getStackTrace();
58  			StackTraceElement elem = st[1];
59  			String clazzName = elem.getClassName();
60  			try {
61  				theType = Class.forName(clazzName);
62  			} catch (ClassNotFoundException e1) {
63  				throw new Error(e);
64  			}
65  		}
66  
67  		for (Field next : Arrays.asList(theType.getDeclaredFields())) {
68  			if (Modifier.isStatic(next.getModifiers())) {
69  				if (!Modifier.isFinal(next.getModifiers()) && !next.getType().isPrimitive()) {
70  					ourLog.info("Clearing value of field: {}", next.toString());
71  					try {
72  						next.setAccessible(true);
73  						next.set(theType, null);
74  					} catch (Exception e) {
75  						throw new Error(e);
76  					}
77  				}
78  				if (Modifier.isFinal(next.getModifiers())) {
79  					if (next.getType().equals(FhirContext.class)) {
80  						throw new Error("Test has final field of type FhirContext: " + next);
81  					}
82  				}
83  			}
84  
85  		}
86  
87  		randomizeLocale();
88  
89  		/*
90  		 * If we're running a CI build, set all loggers to TRACE level to ensure coverage
91  		 * on trace blocks
92  		 */
93  		try {
94  			if ("true".equals(System.getProperty("ci"))) {
95  				for (Logger next : ((LoggerContext) LoggerFactory.getILoggerFactory()).getLoggerList()) {
96  					next.setLevel(Level.TRACE);
97  				}
98  			}
99  		} catch (NoClassDefFoundError e) {
100 			// ignore
101 		}
102 	}
103 
104 	/**
105 	 * Set some system properties randomly after each test.. this is kind of hackish,
106 	 * but it helps us make sure we don't have any tests that depend on a particular
107 	 * environment
108 	 */
109 	public static void randomizeLocale() {
110  		Locale[] availableLocales = {Locale.CANADA, Locale.GERMANY, Locale.TAIWAN};
111 		Locale.setDefault(availableLocales[(int) (Math.random() * availableLocales.length)]);
112 		ourLog.info("Tests are running in locale: " + Locale.getDefault().getDisplayName());
113 		if (Math.random() < 0.5) {
114 			ourLog.info("Tests are using WINDOWS line endings and ISO-8851-1");
115 			System.setProperty("file.encoding", "ISO-8859-1");
116 			System.setProperty("line.separator", "\r\n");
117 		} else {
118 			ourLog.info("Tests are using UNIX line endings and UTF-8");
119 			System.setProperty("file.encoding", "UTF-8");
120 			System.setProperty("line.separator", "\n");
121 		}
122 		String availableTimeZones[] = {"GMT+08:00", "GMT-05:00", "GMT+00:00", "GMT+03:30"};
123 		String timeZone = availableTimeZones[(int) (Math.random() * availableTimeZones.length)];
124 		TimeZone.setDefault(TimeZone.getTimeZone(timeZone));
125 		ourLog.info("Tests are using time zone: {}", TimeZone.getDefault().getID());
126 	}
127 
128 
129 	/**
130 	 * <b>THIS IS FOR UNIT TESTS ONLY - DO NOT CALL THIS METHOD FROM USER CODE</b>
131 	 * <p>
132 	 * Wait for an atomicinteger to hit a given site and fail if it never does
133 	 */
134 	public static void waitForSize(int theTarget, AtomicInteger theInteger) {
135 		long start = System.currentTimeMillis();
136 		while (theInteger.get() != theTarget && (System.currentTimeMillis() - start) <= 15000) {
137 			try {
138 				Thread.sleep(50);
139 			} catch (InterruptedException theE) {
140 				throw new Error(theE);
141 			}
142 		}
143 		if ((System.currentTimeMillis() - start) >= 15000) {
144 			throw new IllegalStateException("Size " + theInteger.get() + " is != target " + theTarget);
145 		}
146 	}
147 
148 	/**
149 	 * <b>THIS IS FOR UNIT TESTS ONLY - DO NOT CALL THIS METHOD FROM USER CODE</b>
150 	 * <p>
151 	 * Wait for an atomicinteger to hit a given site and fail if it never does
152 	 */
153 	public static void waitForSize(int theTarget, Callable<Integer> theSource) throws Exception {
154 		long start = System.currentTimeMillis();
155 		while (theSource.call() != theTarget && (System.currentTimeMillis() - start) <= 15000) {
156 			try {
157 				Thread.sleep(50);
158 			} catch (InterruptedException theE) {
159 				throw new Error(theE);
160 			}
161 		}
162 		if ((System.currentTimeMillis() - start) >= 15000) {
163 			throw new IllegalStateException("Size " + theSource.call() + " is != target " + theTarget);
164 		}
165 	}
166 
167 	/**
168 	 * <b>THIS IS FOR UNIT TESTS ONLY - DO NOT CALL THIS METHOD FROM USER CODE</b>
169 	 * <p>
170 	 * Strip \r chars from a string to account for line ending platform differences
171 	 */
172 	public static String stripReturns(String theString) {
173 		return defaultString(theString).replace("\r", "");
174 	}
175 
176 }