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.atomic.AtomicInteger;
36  
37  import static org.apache.commons.lang3.StringUtils.defaultString;
38  
39  public class TestUtil {
40  	private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(TestUtil.class);
41  
42  	/**
43  	 * <b>THIS IS FOR UNIT TESTS ONLY - DO NOT CALL THIS METHOD FROM USER CODE</b>
44  	 * <p>
45  	 * When we run the unit tests in cobertura, JUnit doesn't seem to clean up static fields which leads to
46  	 * tons of memory being used by the end and the JVM crashes in Travis. Manually clearing all of the
47  	 * static fields seems to solve this.
48  	 */
49  	public static void clearAllStaticFieldsForUnitTest() {
50  		HapiLocalizer.setOurFailOnMissingMessage(true);
51  
52  		Class<?> theType;
53  		try {
54  			throw new Exception();
55  		} catch (Exception e) {
56  			StackTraceElement[] st = e.getStackTrace();
57  			StackTraceElement elem = st[1];
58  			String clazzName = elem.getClassName();
59  			try {
60  				theType = Class.forName(clazzName);
61  			} catch (ClassNotFoundException e1) {
62  				throw new Error(e);
63  			}
64  		}
65  
66  		for (Field next : Arrays.asList(theType.getDeclaredFields())) {
67  			if (Modifier.isStatic(next.getModifiers())) {
68  				if (!Modifier.isFinal(next.getModifiers()) && !next.getType().isPrimitive()) {
69  					ourLog.info("Clearing value of field: {}", next.toString());
70  					try {
71  						next.setAccessible(true);
72  						next.set(theType, null);
73  					} catch (Exception e) {
74  						throw new Error(e);
75  					}
76  				}
77  				if (Modifier.isFinal(next.getModifiers())) {
78  					if (next.getType().equals(FhirContext.class)) {
79  						throw new Error("Test has final field of type FhirContext: " + next);
80  					}
81  				}
82  			}
83  
84  		}
85  
86  		randomizeLocale();
87  
88  		/*
89  		 * If we're running a CI build, set all loggers to TRACE level to ensure coverage
90  		 * on trace blocks
91  		 */
92  		try {
93  			if ("true".equals(System.getProperty("ci"))) {
94  				for (Logger next : ((LoggerContext) LoggerFactory.getILoggerFactory()).getLoggerList()) {
95  					next.setLevel(Level.TRACE);
96  				}
97  			}
98  		} catch (NoClassDefFoundError e) {
99  			// ignore
100 		}
101 	}
102 
103 	/**
104 	 * Set some system properties randomly after each test.. this is kind of hackish,
105 	 * but it helps us make sure we don't have any tests that depend on a particular
106 	 * environment
107 	 */
108 	public static void randomizeLocale() {
109 		Locale[] availableLocales = {Locale.CANADA, Locale.GERMANY, Locale.TAIWAN};
110 		Locale.setDefault(availableLocales[(int) (Math.random() * availableLocales.length)]);
111 		ourLog.info("Tests are running in locale: " + Locale.getDefault().getDisplayName());
112 		if (Math.random() < 0.5) {
113 			ourLog.info("Tests are using WINDOWS line endings and ISO-8851-1");
114 			System.setProperty("file.encoding", "ISO-8859-1");
115 			System.setProperty("line.separator", "\r\n");
116 		} else {
117 			ourLog.info("Tests are using UNIX line endings and UTF-8");
118 			System.setProperty("file.encoding", "UTF-8");
119 			System.setProperty("line.separator", "\n");
120 		}
121 		String availableTimeZones[] = {"GMT+08:00", "GMT-05:00", "GMT+00:00", "GMT+03:30"};
122 		String timeZone = availableTimeZones[(int) (Math.random() * availableTimeZones.length)];
123 		TimeZone.setDefault(TimeZone.getTimeZone(timeZone));
124 		ourLog.info("Tests are using time zone: {}", TimeZone.getDefault().getID());
125 	}
126 
127 
128 	/**
129 	 * <b>THIS IS FOR UNIT TESTS ONLY - DO NOT CALL THIS METHOD FROM USER CODE</b>
130 	 * <p>
131 	 * Wait for an atomicinteger to hit a given site and fail if it never does
132 	 */
133 	public static void waitForSize(int theTarget, AtomicInteger theInteger) {
134 		long start = System.currentTimeMillis();
135 		while (theInteger.get() != theTarget && (System.currentTimeMillis() - start) <= 15000) {
136 			try {
137 				Thread.sleep(50);
138 			} catch (InterruptedException theE) {
139 				throw new Error(theE);
140 			}
141 		}
142 		if ((System.currentTimeMillis() - start) >= 15000) {
143 			throw new IllegalStateException("Size " + theInteger.get() + " is != target " + theTarget);
144 		}
145 	}
146 
147 
148 	/**
149 	 * <b>THIS IS FOR UNIT TESTS ONLY - DO NOT CALL THIS METHOD FROM USER CODE</b>
150 	 * <p>
151 	 * Strip \r chars from a string to account for line ending platform differences
152 	 */
153 	public static String stripReturns(String theString) {
154 		return defaultString(theString).replace("\r", "");
155 	}
156 
157 }