001package ca.uhn.fhir.util;
002
003/*
004 * #%L
005 * HAPI FHIR - Core Library
006 * %%
007 * Copyright (C) 2014 - 2017 University Health Network
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 java.lang.reflect.Field;
024import java.lang.reflect.Modifier;
025import java.util.Arrays;
026import java.util.Locale;
027import java.util.TimeZone;
028
029import org.slf4j.LoggerFactory;
030
031import ca.uhn.fhir.context.FhirContext;
032import ch.qos.logback.classic.Level;
033import ch.qos.logback.classic.Logger;
034import ch.qos.logback.classic.LoggerContext;
035
036public class TestUtil {
037        private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(TestUtil.class);
038
039        /**
040         * <b>THIS IS FOR UNIT TESTS ONLY - DO NOT CALL THIS METHOD FROM USER CODE</b>
041         * 
042         * When we run the unit tests in cobertura, JUnit doesn't seem to clean up static fields which leads to
043         * tons of memory being used by the end and the JVM crashes in Travis. Manually clearing all of the
044         * static fields seems to solve this.
045         */
046        public static void clearAllStaticFieldsForUnitTest() {
047
048                Class<?> theType;
049                try {
050                        throw new Exception();
051                } catch (Exception e) {
052                        StackTraceElement[] st = e.getStackTrace();
053                        StackTraceElement elem = st[1];
054                        String clazzName = elem.getClassName();
055                        try {
056                                theType = Class.forName(clazzName);
057                        } catch (ClassNotFoundException e1) {
058                                throw new Error(e);
059                        }
060                }
061
062                for (Field next : Arrays.asList(theType.getDeclaredFields())) {
063                        if (Modifier.isStatic(next.getModifiers())) {
064                                if (!Modifier.isFinal(next.getModifiers()) && !next.getType().isPrimitive()) {
065                                        ourLog.info("Clearing value of field: {}", next.toString());
066                                        try {
067                                                next.setAccessible(true);
068                                                next.set(theType, null);
069                                        } catch (Exception e) {
070                                                throw new Error(e);
071                                        }
072                                }
073                                if (Modifier.isFinal(next.getModifiers())) {
074                                        if (next.getType().equals(FhirContext.class)) {
075                                                throw new Error("Test has final field of type FhirContext: " + next);
076                                        }
077                                }
078                        }
079
080                }
081
082                randomizeLocale();
083
084                /*
085                 * If we're running a CI build, set all loggers to TRACE level to ensure coverage
086                 * on trace blocks
087                 */
088                try {
089                        if ("true".equals(System.getProperty("ci"))) {
090                                for (Logger next : ((LoggerContext) LoggerFactory.getILoggerFactory()).getLoggerList()) {
091                                        next.setLevel(Level.TRACE);
092                                }
093                        }
094                } catch (NoClassDefFoundError e) {
095                        // ignore
096                }
097        }
098
099        /**
100         * Set some system properties randomly after each test.. this is kind of hackish,
101         * but it helps us make sure we don't have any tests that depend on a particular
102         * environment
103         */
104        public static void randomizeLocale() {
105                Locale[] availableLocales = { Locale.CANADA, Locale.GERMANY, Locale.TAIWAN };
106                Locale.setDefault(availableLocales[(int) (Math.random() * availableLocales.length)]);
107                ourLog.info("Tests are running in locale: " + Locale.getDefault().getDisplayName());
108                if (Math.random() < 0.5) {
109                        ourLog.info("Tests are using WINDOWS line endings and ISO-8851-1");
110                        System.setProperty("file.encoding", "ISO-8859-1");
111                        System.setProperty("line.separator", "\r\n");
112                } else {
113                        ourLog.info("Tests are using UNIX line endings and UTF-8");
114                        System.setProperty("file.encoding", "UTF-8");
115                        System.setProperty("line.separator", "\n");
116                }
117                String availableTimeZones[] = { "GMT+08:00", "GMT-05:00", "GMT+00:00", "GMT+03:30" };
118                String timeZone = availableTimeZones[(int) (Math.random() * availableTimeZones.length)];
119                TimeZone.setDefault(TimeZone.getTimeZone(timeZone));
120                ourLog.info("Tests are using time zone: {}", TimeZone.getDefault().getID());
121        }
122
123}