org.junit.contrib.java.lang.system.EnvironmentVariables Maven / Gradle / Ivy
Show all versions of system-rules Show documentation
package org.junit.contrib.java.lang.system;
import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
import static java.lang.Class.forName;
import static java.lang.System.getenv;
/**
* The {@code EnvironmentVariables} rule allows you to set environment variables
* within your test. All changes to environment variables are reverted after the
* test.
*
* public class EnvironmentVariablesTest {
* @Rule
* public final EnvironmentVariables environmentVariables = new EnvironmentVariables();
*
* @Test
* public void test() {
* environmentVariables.set("name", "value");
* assertEquals("value", System.getenv("name"));
* }
* }
*
* Warning: This rule uses reflection for modifying internals of the
* environment variables map. It fails if your {@code SecurityManager} forbids
* such modifications.
*/
public class EnvironmentVariables implements TestRule {
/**
* Set the value of an environment variable. You can delete an environment
* variable by setting it to {@code null}.
*
* @param name the environment variable's name.
* @param value the environment variable's new value.
*/
public void set(String name, String value) {
set(getEditableMapOfVariables(), name, value);
set(getTheCaseInsensitiveEnvironment(), name, value);
}
private void set(Map variables, String name, String value) {
if (variables != null) //theCaseInsensitiveEnvironment may be null
if (value == null)
variables.remove(name);
else
variables.put(name, value);
}
public Statement apply(Statement base, Description description) {
return new EnvironmentVariablesStatement(base);
}
private static class EnvironmentVariablesStatement extends Statement {
final Statement baseStatement;
Map originalVariables;
EnvironmentVariablesStatement(Statement baseStatement) {
this.baseStatement = baseStatement;
}
@Override
public void evaluate() throws Throwable {
saveCurrentState();
try {
baseStatement.evaluate();
} finally {
restoreOriginalVariables();
}
}
void saveCurrentState() {
originalVariables = new HashMap(getenv());
}
void restoreOriginalVariables() {
restoreVariables(getEditableMapOfVariables());
Map theCaseInsensitiveEnvironment
= getTheCaseInsensitiveEnvironment();
if (theCaseInsensitiveEnvironment != null)
restoreVariables(theCaseInsensitiveEnvironment);
}
void restoreVariables(Map variables) {
variables.clear();
variables.putAll(originalVariables);
}
}
private static Map getEditableMapOfVariables() {
Class> classOfMap = getenv().getClass();
try {
return getFieldValue(classOfMap, getenv(), "m");
} catch (IllegalAccessException e) {
throw new RuntimeException("System Rules cannot access the field"
+ " 'm' of the map System.getenv().", e);
} catch (NoSuchFieldException e) {
throw new RuntimeException("System Rules expects System.getenv() to"
+ " have a field 'm' but it has not.", e);
}
}
/*
* The names of environment variables are case-insensitive in Windows.
* Therefore it stores the variables in a TreeMap named
* theCaseInsensitiveEnvironment.
*/
private static Map getTheCaseInsensitiveEnvironment() {
try {
Class> processEnvironment = forName("java.lang.ProcessEnvironment");
return getFieldValue(
processEnvironment, null, "theCaseInsensitiveEnvironment");
} catch (ClassNotFoundException e) {
throw new RuntimeException("System Rules expects the existence of"
+ " the class java.lang.ProcessEnvironment but it does not"
+ " exist.", e);
} catch (IllegalAccessException e) {
throw new RuntimeException("System Rules cannot access the static"
+ " field 'theCaseInsensitiveEnvironment' of the class"
+ " java.lang.ProcessEnvironment.", e);
} catch (NoSuchFieldException e) {
//this field is only available for Windows
return null;
}
}
private static Map getFieldValue(Class> klass,
Object object, String name)
throws NoSuchFieldException, IllegalAccessException {
Field field = klass.getDeclaredField(name);
field.setAccessible(true);
return (Map) field.get(object);
}
}