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