Merge pull request #305 from zoodles/master

This pull request provides the ability for a test case to specify a locale, using the @Values annotation.  Example: @Values( locale="fr" )
diff --git a/src/main/java/com/xtremelabs/robolectric/RobolectricConfig.java b/src/main/java/com/xtremelabs/robolectric/RobolectricConfig.java
index 3dbbc33..ccbf71e 100644
--- a/src/main/java/com/xtremelabs/robolectric/RobolectricConfig.java
+++ b/src/main/java/com/xtremelabs/robolectric/RobolectricConfig.java
@@ -31,6 +31,8 @@
     private int applicationFlags;
     private final List<ReceiverAndIntentFilter> receivers = new ArrayList<ReceiverAndIntentFilter>();
     private boolean strictI18n = false;
+    private String locale = "";
+    private String oldLocale = "";
 
     /**
      * Creates a Robolectric configuration using default Android files relative to the specified base directory.
@@ -248,6 +250,19 @@
     	strictI18n = strict;
     }
 
+    public void setLocale( String locale ){
+    	this.oldLocale = this.locale;
+    	this.locale = locale;
+    }
+    
+    public String getLocale() {
+    	return this.locale;
+    }
+    
+    public boolean isLocaleChanged() {
+    	return !locale.equals( oldLocale );
+    }
+    
     private static String getTagAttributeText(final Document doc, final String tag, final String attribute) {
         NodeList elementsByTagName = doc.getElementsByTagName(tag);
         for (int i = 0; i < elementsByTagName.getLength(); ++i) {
diff --git a/src/main/java/com/xtremelabs/robolectric/RobolectricTestRunner.java b/src/main/java/com/xtremelabs/robolectric/RobolectricTestRunner.java
index 46a6f42..8947159 100644
--- a/src/main/java/com/xtremelabs/robolectric/RobolectricTestRunner.java
+++ b/src/main/java/com/xtremelabs/robolectric/RobolectricTestRunner.java
@@ -1,7 +1,31 @@
 package com.xtremelabs.robolectric;
 
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.Map;
+
+import javassist.Loader;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.junit.runners.BlockJUnit4ClassRunner;
+import org.junit.runners.model.FrameworkMethod;
+import org.junit.runners.model.InitializationError;
+import org.junit.runners.model.Statement;
+import org.w3c.dom.Document;
+import org.xml.sax.SAXException;
+
 import android.app.Application;
 import android.net.Uri__FromAndroid;
+
 import com.xtremelabs.robolectric.bytecode.ClassHandler;
 import com.xtremelabs.robolectric.bytecode.RobolectricClassLoader;
 import com.xtremelabs.robolectric.bytecode.ShadowWrangler;
@@ -14,23 +38,6 @@
 import com.xtremelabs.robolectric.util.DatabaseConfig.DatabaseMap;
 import com.xtremelabs.robolectric.util.DatabaseConfig.UsingDatabaseMap;
 import com.xtremelabs.robolectric.util.SQLiteMap;
-import javassist.Loader;
-import org.junit.runners.BlockJUnit4ClassRunner;
-import org.junit.runners.model.FrameworkMethod;
-import org.junit.runners.model.InitializationError;
-import org.junit.runners.model.Statement;
-import org.w3c.dom.Document;
-import org.xml.sax.SAXException;
-
-import javax.xml.parsers.DocumentBuilder;
-import javax.xml.parsers.DocumentBuilderFactory;
-import javax.xml.parsers.ParserConfigurationException;
-import java.io.*;
-import java.lang.annotation.Annotation;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.Method;
-import java.util.HashMap;
-import java.util.Map;
 
 /**
  * Installs a {@link RobolectricClassLoader} and {@link com.xtremelabs.robolectric.res.ResourceLoader} in order to
@@ -265,7 +272,8 @@
 
     @Override protected Statement methodBlock(final FrameworkMethod method) {
         setupI18nStrictState(method.getMethod(), robolectricConfig);
-
+        lookForLocaleAnnotation( method.getMethod(), robolectricConfig );
+        
     	if (classHandler != null) {
             classHandler.configure(robolectricConfig);
             classHandler.beforeTest();
@@ -342,7 +350,8 @@
 
     public void setupApplicationState(final RobolectricConfig robolectricConfig) {
         setupLogging();
-        ResourceLoader resourceLoader = createResourceLoader(robolectricConfig);
+        
+        ResourceLoader resourceLoader = createResourceLoader(robolectricConfig );
 
         Robolectric.bindDefaultShadowClasses();
         bindShadowClasses();
@@ -355,9 +364,7 @@
 
         Robolectric.application = ShadowApplication.bind(createApplication(), resourceLoader);
     }
-
-
-
+    
     /**
      * Override this method to bind your own shadow classes
      */
@@ -442,6 +449,27 @@
 		return strictI18n;
 	}
 
+	private void lookForLocaleAnnotation( Method method, RobolectricConfig robolectricConfig ){
+		String locale = "";
+		// TODO: there are maybe better implementation for getAnnotation
+		// Have tried to use several other simple ways, but failed.
+		Annotation[] annos = method.getDeclaredAnnotations();
+		for( Annotation anno: annos ){
+			
+			if( anno.annotationType().getName().equals( "com.xtremelabs.robolectric.annotation.Values" )){
+				String annotationString = anno.toString();
+				int startIndex = annotationString.indexOf( '=' );
+				int endIndex = annotationString.indexOf( ')' );
+				
+				if( startIndex < 0 || endIndex < 0 ){ return; }
+				
+				locale = annotationString.substring( startIndex + 1, endIndex );
+			}
+		}
+		
+		robolectricConfig.setLocale( locale );
+	}
+	
     private void setupLogging() {
         String logging = System.getProperty("robolectric.logging");
         if (logging != null && ShadowLog.stream == null) {
@@ -481,13 +509,14 @@
 
     private ResourceLoader createResourceLoader(final RobolectricConfig robolectricConfig) {
         ResourceLoader resourceLoader = resourceLoaderForRootAndDirectory.get(robolectricConfig);
-        if (resourceLoader == null) {
+        // When locale has changed, reload the resource files.
+        if (resourceLoader == null || robolectricConfig.isLocaleChanged() ) {
             try {
                 robolectricConfig.validate();
 
                 String rClassName = robolectricConfig.getRClassName();
                 Class rClass = Class.forName(rClassName);
-                resourceLoader = new ResourceLoader(robolectricConfig.getRealSdkVersion(), rClass, robolectricConfig.getResourceDirectory(), robolectricConfig.getAssetsDirectory());
+                resourceLoader = new ResourceLoader(robolectricConfig.getRealSdkVersion(), rClass, robolectricConfig.getResourceDirectory(), robolectricConfig.getAssetsDirectory(), robolectricConfig.getLocale() );
                 resourceLoaderForRootAndDirectory.put(robolectricConfig, resourceLoader);
             } catch (Exception e) {
                 throw new RuntimeException(e);
diff --git a/src/main/java/com/xtremelabs/robolectric/annotation/Values.java b/src/main/java/com/xtremelabs/robolectric/annotation/Values.java
new file mode 100644
index 0000000..c592304
--- /dev/null
+++ b/src/main/java/com/xtremelabs/robolectric/annotation/Values.java
@@ -0,0 +1,12 @@
+package com.xtremelabs.robolectric.annotation;
+
+/**
+ * Indicate that roboletric should look for values that is specific for the locale
+ * 
+ */
+@java.lang.annotation.Documented
+@java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
+@java.lang.annotation.Target(java.lang.annotation.ElementType.METHOD)
+public @interface Values {
+	String locale();
+}
diff --git a/src/main/java/com/xtremelabs/robolectric/res/ResourceLoader.java b/src/main/java/com/xtremelabs/robolectric/res/ResourceLoader.java
index 9d9a4a8..f24bab6 100644
--- a/src/main/java/com/xtremelabs/robolectric/res/ResourceLoader.java
+++ b/src/main/java/com/xtremelabs/robolectric/res/ResourceLoader.java
@@ -6,6 +6,7 @@
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
 import android.preference.PreferenceScreen;
+import android.text.TextUtils;
 import android.view.Menu;
 import android.view.View;
 import android.view.ViewGroup;
@@ -60,13 +61,20 @@
 	private final IntegerResourceLoader integerResourceLoader;
 	private boolean isInitialized = false;
 	private boolean strictI18n = false;
+	private String locale="";
 	
 	private final Set<Integer> ninePatchDrawableIds = new HashSet<Integer>();
 
-	public ResourceLoader( int sdkVersion, Class rClass, File resourceDir, File assetsDir ) throws Exception {
+	public ResourceLoader(  int sdkVersion, Class rClass, File resourceDir, File assetsDir ) throws Exception {
+		this( sdkVersion, rClass, resourceDir, assetsDir, "");
+	}
+	
+	public ResourceLoader( int sdkVersion, Class rClass, File resourceDir, File assetsDir, String locale ) throws Exception {
 		this.sdkVersion = sdkVersion;
 		this.assetsDir = assetsDir;
 		this.rClass = rClass;
+		this.locale = locale;
+		
 		resourceExtractor = new ResourceExtractor();
 		resourceExtractor.addLocalRClass( rClass );
 		resourceExtractor.addSystemRClass( R.class );
@@ -236,7 +244,15 @@
 	}
 
 	private File getValueResourceDir( File xmlResourceDir ) {
-		return xmlResourceDir != null ? new File( xmlResourceDir, "values" ) : null;
+		String valuesDir = "values";
+		if( !TextUtils.isEmpty( locale ) ){
+			valuesDir += "-"+ locale;
+		}
+		File result = ( xmlResourceDir != null ) ? new File( xmlResourceDir, valuesDir ) : null;
+		if( result == null || !result.exists() ){
+			throw new RuntimeException("Couldn't find value resource directory: " + result.getAbsolutePath() );
+		}
+		return result;
 	}
 
 	private File getPreferenceResourceDir( File xmlResourceDir ) {
diff --git a/src/test/java/com/xtremelabs/robolectric/RobolectricTestRunnerTest.java b/src/test/java/com/xtremelabs/robolectric/RobolectricTestRunnerTest.java
index 00c44a9..a7859d9 100644
--- a/src/test/java/com/xtremelabs/robolectric/RobolectricTestRunnerTest.java
+++ b/src/test/java/com/xtremelabs/robolectric/RobolectricTestRunnerTest.java
@@ -7,24 +7,15 @@
 import org.junit.runner.RunWith;
 import org.junit.runners.model.InitializationError;
 
-import com.xtremelabs.robolectric.annotation.DisableStrictI18n;
-import com.xtremelabs.robolectric.annotation.EnableStrictI18n;
-import com.xtremelabs.robolectric.internal.Implements;
-import com.xtremelabs.robolectric.res.ResourceLoader;
-import com.xtremelabs.robolectric.shadows.ShadowActivity;
-import com.xtremelabs.robolectric.shadows.ShadowApplication;
-
 import android.app.Activity;
 import android.app.Application;
-import android.content.ComponentName;
-import android.content.Intent;
-import android.os.Bundle;
-import android.view.KeyEvent;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.SubMenu;
 import android.widget.TextView;
 
+import com.xtremelabs.robolectric.annotation.DisableStrictI18n;
+import com.xtremelabs.robolectric.annotation.EnableStrictI18n;
+import com.xtremelabs.robolectric.annotation.Values;
+import com.xtremelabs.robolectric.res.ResourceLoader;
+
 @RunWith(RobolectricTestRunnerTest.RunnerForTesting.class)
 public class RobolectricTestRunnerTest {
 	
@@ -56,6 +47,12 @@
     }
     
     @Test
+    @Values( locale="fr")
+    public void internalBeforeTest_setLocale(){
+    	assertEquals( RunnerForTesting.instance.robolectricConfig.getLocale(), "fr" );
+    }
+    
+    @Test
     public void internalBeforeTest_doesNotsetI18nStrictModeFromSystemIfPropertyAbsent() {
     	assertFalse(RunnerForTesting.instance.robolectricConfig.getStrictI18n());
     }
diff --git a/src/test/java/com/xtremelabs/robolectric/shadows/ResourcesTest.java b/src/test/java/com/xtremelabs/robolectric/shadows/ResourcesTest.java
index de91496..e6a240a 100644
--- a/src/test/java/com/xtremelabs/robolectric/shadows/ResourcesTest.java
+++ b/src/test/java/com/xtremelabs/robolectric/shadows/ResourcesTest.java
@@ -1,5 +1,16 @@
 package com.xtremelabs.robolectric.shadows;
 
+import static com.xtremelabs.robolectric.Robolectric.shadowOf;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.junit.Assert.assertThat;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
 import android.app.Activity;
 import android.content.res.Configuration;
 import android.content.res.Resources;
@@ -8,20 +19,12 @@
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.NinePatchDrawable;
 
+import com.xtremelabs.robolectric.R;
 import com.xtremelabs.robolectric.Robolectric;
 import com.xtremelabs.robolectric.WithTestDefaultsRunner;
-import com.xtremelabs.robolectric.R;
+import com.xtremelabs.robolectric.RobolectricTestRunnerTest.RunnerForTesting;
+import com.xtremelabs.robolectric.annotation.Values;
 import com.xtremelabs.robolectric.util.TestR;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import static com.xtremelabs.robolectric.Robolectric.shadowOf;
-import static org.hamcrest.CoreMatchers.equalTo;
-import static org.hamcrest.CoreMatchers.instanceOf;
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.CoreMatchers.notNullValue;
-import static org.junit.Assert.assertThat;
 
 
 @RunWith(WithTestDefaultsRunner.class)
@@ -77,6 +80,13 @@
     	assertThat( resources.getDrawable( TestR.anim.test_anim_1 ), instanceOf( AnimationDrawable.class ) );
     }
     
+    @Test
+    @Values( locale="fr" )
+    public void testGetResourceFromSpecificLocale(){
+    	String hello=resources.getString( R.string.hello );
+    	assertThat( hello, equalTo( "Bonjour" ) );
+    }
+    
     /**
      * given an R.color.id value, will return a ColorDrawable
      */
@@ -129,4 +139,6 @@
         assertThat(activity.getResources().getDisplayMetrics().heightPixels, equalTo(800));
         assertThat(activity.getResources().getDisplayMetrics().widthPixels, equalTo(480));
     }
+    
+ 
 }
diff --git a/src/test/resources/res/values-fr/strings.xml b/src/test/resources/res/values-fr/strings.xml
new file mode 100644
index 0000000..04fdf3f
--- /dev/null
+++ b/src/test/resources/res/values-fr/strings.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <string name="hello">Bonjour</string>
+</resources>
\ No newline at end of file