Migrate from Gradle to Maven.

- Restructure source to src/{main,test} style
- Add pom.xml and update Android.mk
- Migrate all tests to JUnit4 and Robolectric
 - RequestQueueTest is currently @Ignored as fixing it will
   involve more extensive refactoring.
- Main library still builds in Gradle; tests do not

Change-Id: I1edc53bb1a54f64d3e806e4572901295ef63e2ca
diff --git a/.classpath b/.classpath
deleted file mode 100644
index d57ec02..0000000
--- a/.classpath
+++ /dev/null
@@ -1,9 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<classpath>
-	<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
-	<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
-	<classpathentry kind="src" path="src"/>
-	<classpathentry kind="src" path="gen"/>
-	<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.DEPENDENCIES"/>
-	<classpathentry kind="output" path="bin/classes"/>
-</classpath>
diff --git a/.gitignore b/.gitignore
index 8eb3aaa..10b5ff5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,3 +3,6 @@
 .gradle
 build
 .settings
+target
+*.iml
+.idea
diff --git a/.project b/.project
deleted file mode 100644
index 16c2226..0000000
--- a/.project
+++ /dev/null
@@ -1,33 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<projectDescription>
-	<name>Volley</name>
-	<comment></comment>
-	<projects>
-	</projects>
-	<buildSpec>
-		<buildCommand>
-			<name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>
-			<arguments>
-			</arguments>
-		</buildCommand>
-		<buildCommand>
-			<name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>
-			<arguments>
-			</arguments>
-		</buildCommand>
-		<buildCommand>
-			<name>org.eclipse.jdt.core.javabuilder</name>
-			<arguments>
-			</arguments>
-		</buildCommand>
-		<buildCommand>
-			<name>com.android.ide.eclipse.adt.ApkBuilder</name>
-			<arguments>
-			</arguments>
-		</buildCommand>
-	</buildSpec>
-	<natures>
-		<nature>com.android.ide.eclipse.adt.AndroidNature</nature>
-		<nature>org.eclipse.jdt.core.javanature</nature>
-	</natures>
-</projectDescription>
diff --git a/Android.mk b/Android.mk
index a4375e7..dbe5194 100644
--- a/Android.mk
+++ b/Android.mk
@@ -20,7 +20,7 @@
 
 LOCAL_MODULE := volley
 LOCAL_SDK_VERSION := 17
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_SRC_FILES := $(call all-java-files-under, src/main/java)
 
 include $(BUILD_STATIC_JAVA_LIBRARY)
 
diff --git a/build.gradle b/build.gradle
index 7c0abdb..1a864dc 100644
--- a/build.gradle
+++ b/build.gradle
@@ -11,7 +11,6 @@
 
 android {
     compileSdkVersion 19
-    buildToolsVersion = '19.1.0'
+    buildToolsVersion = '21.1.0'
 }
 
-apply from: 'rules.gradle'
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..90061b1
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,63 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <groupId>com.android.volley</groupId>
+  <artifactId>volley</artifactId>
+  <version>1.0-SNAPSHOT</version>
+  <packaging>jar</packaging>
+
+  <name>volley</name>
+  <url>http://android.com</url>
+
+  <properties>
+    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+
+    <java.version>1.6</java.version>
+  </properties>
+
+  <dependencies>
+    <dependency>
+      <groupId>com.google.android</groupId>
+      <artifactId>android</artifactId>
+      <version>4.1.1.4</version>
+    </dependency>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <version>4.10</version>
+    </dependency>
+    <dependency>
+      <groupId>org.robolectric</groupId>
+      <artifactId>robolectric</artifactId>
+      <version>2.2</version>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <pluginManagement>
+      <plugins>
+        <plugin>
+          <groupId>com.jayway.maven.plugins.android.generation2</groupId>
+          <artifactId>android-maven-plugin</artifactId>
+          <version>3.8.1</version>
+          <configuration>
+            <sdk>
+              <platform>19</platform>
+            </sdk>
+          </configuration>
+        </plugin>
+
+        <plugin>
+          <groupId>org.apache.maven.plugins</groupId>
+          <artifactId>maven-compiler-plugin</artifactId>
+          <version>3.0</version>
+          <configuration>
+            <source>${java.version}</source>
+            <target>${java.version}</target>
+          </configuration>
+        </plugin>
+      </plugins>
+    </pluginManagement>
+  </build>
+</project>
diff --git a/project.properties b/project.properties
deleted file mode 100644
index 63f569f..0000000
--- a/project.properties
+++ /dev/null
@@ -1,15 +0,0 @@
-# This file is automatically generated by Android Tools.
-# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
-#
-# This file must be checked in Version Control Systems.
-#
-# To customize properties used by the Ant build system use,
-# "ant.properties", and override values to adapt the script to your
-# project structure.
-
-# Project target.
-target=android-19
-
-# Make sure to pass a valid value to renderscript
-# https://code.google.com/p/android/issues/detail?id=40487
-renderscript.opt.level=O0
diff --git a/rules.gradle b/rules.gradle
deleted file mode 100644
index 6e9e5a8..0000000
--- a/rules.gradle
+++ /dev/null
@@ -1,27 +0,0 @@
-apply plugin: 'android-library'
-
-android {
-    sourceSets {
-        defaultConfig {
-            testApplicationId 'com.android.volley.tests'
-        }
-
-        main {
-            assets.srcDirs       = ['assets']
-            res.srcDirs          = ['res']
-            aidl.srcDirs         = ['src']
-            resources.srcDirs    = ['src']
-            renderscript.srcDirs = ['src']
-            java.srcDirs         = ['src']
-            manifest.srcFile 'AndroidManifest.xml'
-
-        }
-
-        androidTest.setRoot('tests')
-        androidTest.java.srcDirs = ['tests/src']
-    }
-    lintOptions {
-        // TODO: fix errors and reenable.
-        abortOnError false
-    }
-}
diff --git a/AndroidManifest.xml b/src/main/AndroidManifest.xml
similarity index 100%
rename from AndroidManifest.xml
rename to src/main/AndroidManifest.xml
diff --git a/src/com/android/volley/AuthFailureError.java b/src/main/java/com/android/volley/AuthFailureError.java
similarity index 100%
rename from src/com/android/volley/AuthFailureError.java
rename to src/main/java/com/android/volley/AuthFailureError.java
diff --git a/src/com/android/volley/Cache.java b/src/main/java/com/android/volley/Cache.java
similarity index 100%
rename from src/com/android/volley/Cache.java
rename to src/main/java/com/android/volley/Cache.java
diff --git a/src/com/android/volley/CacheDispatcher.java b/src/main/java/com/android/volley/CacheDispatcher.java
similarity index 100%
rename from src/com/android/volley/CacheDispatcher.java
rename to src/main/java/com/android/volley/CacheDispatcher.java
diff --git a/src/com/android/volley/DefaultRetryPolicy.java b/src/main/java/com/android/volley/DefaultRetryPolicy.java
similarity index 100%
rename from src/com/android/volley/DefaultRetryPolicy.java
rename to src/main/java/com/android/volley/DefaultRetryPolicy.java
diff --git a/src/com/android/volley/ExecutorDelivery.java b/src/main/java/com/android/volley/ExecutorDelivery.java
similarity index 100%
rename from src/com/android/volley/ExecutorDelivery.java
rename to src/main/java/com/android/volley/ExecutorDelivery.java
diff --git a/src/com/android/volley/Network.java b/src/main/java/com/android/volley/Network.java
similarity index 100%
rename from src/com/android/volley/Network.java
rename to src/main/java/com/android/volley/Network.java
diff --git a/src/com/android/volley/NetworkDispatcher.java b/src/main/java/com/android/volley/NetworkDispatcher.java
similarity index 100%
rename from src/com/android/volley/NetworkDispatcher.java
rename to src/main/java/com/android/volley/NetworkDispatcher.java
diff --git a/src/com/android/volley/NetworkError.java b/src/main/java/com/android/volley/NetworkError.java
similarity index 100%
rename from src/com/android/volley/NetworkError.java
rename to src/main/java/com/android/volley/NetworkError.java
diff --git a/src/com/android/volley/NetworkResponse.java b/src/main/java/com/android/volley/NetworkResponse.java
similarity index 100%
rename from src/com/android/volley/NetworkResponse.java
rename to src/main/java/com/android/volley/NetworkResponse.java
diff --git a/src/com/android/volley/NoConnectionError.java b/src/main/java/com/android/volley/NoConnectionError.java
similarity index 100%
rename from src/com/android/volley/NoConnectionError.java
rename to src/main/java/com/android/volley/NoConnectionError.java
diff --git a/src/com/android/volley/ParseError.java b/src/main/java/com/android/volley/ParseError.java
similarity index 100%
rename from src/com/android/volley/ParseError.java
rename to src/main/java/com/android/volley/ParseError.java
diff --git a/src/com/android/volley/Request.java b/src/main/java/com/android/volley/Request.java
similarity index 100%
rename from src/com/android/volley/Request.java
rename to src/main/java/com/android/volley/Request.java
diff --git a/src/com/android/volley/RequestQueue.java b/src/main/java/com/android/volley/RequestQueue.java
similarity index 100%
rename from src/com/android/volley/RequestQueue.java
rename to src/main/java/com/android/volley/RequestQueue.java
diff --git a/src/com/android/volley/Response.java b/src/main/java/com/android/volley/Response.java
similarity index 100%
rename from src/com/android/volley/Response.java
rename to src/main/java/com/android/volley/Response.java
diff --git a/src/com/android/volley/ResponseDelivery.java b/src/main/java/com/android/volley/ResponseDelivery.java
similarity index 100%
rename from src/com/android/volley/ResponseDelivery.java
rename to src/main/java/com/android/volley/ResponseDelivery.java
diff --git a/src/com/android/volley/RetryPolicy.java b/src/main/java/com/android/volley/RetryPolicy.java
similarity index 100%
rename from src/com/android/volley/RetryPolicy.java
rename to src/main/java/com/android/volley/RetryPolicy.java
diff --git a/src/com/android/volley/ServerError.java b/src/main/java/com/android/volley/ServerError.java
similarity index 100%
rename from src/com/android/volley/ServerError.java
rename to src/main/java/com/android/volley/ServerError.java
diff --git a/src/com/android/volley/TimeoutError.java b/src/main/java/com/android/volley/TimeoutError.java
similarity index 100%
rename from src/com/android/volley/TimeoutError.java
rename to src/main/java/com/android/volley/TimeoutError.java
diff --git a/src/com/android/volley/VolleyError.java b/src/main/java/com/android/volley/VolleyError.java
similarity index 100%
rename from src/com/android/volley/VolleyError.java
rename to src/main/java/com/android/volley/VolleyError.java
diff --git a/src/com/android/volley/VolleyLog.java b/src/main/java/com/android/volley/VolleyLog.java
similarity index 100%
rename from src/com/android/volley/VolleyLog.java
rename to src/main/java/com/android/volley/VolleyLog.java
diff --git a/src/com/android/volley/toolbox/AndroidAuthenticator.java b/src/main/java/com/android/volley/toolbox/AndroidAuthenticator.java
similarity index 100%
rename from src/com/android/volley/toolbox/AndroidAuthenticator.java
rename to src/main/java/com/android/volley/toolbox/AndroidAuthenticator.java
diff --git a/src/com/android/volley/toolbox/Authenticator.java b/src/main/java/com/android/volley/toolbox/Authenticator.java
similarity index 100%
rename from src/com/android/volley/toolbox/Authenticator.java
rename to src/main/java/com/android/volley/toolbox/Authenticator.java
diff --git a/src/com/android/volley/toolbox/BasicNetwork.java b/src/main/java/com/android/volley/toolbox/BasicNetwork.java
similarity index 100%
rename from src/com/android/volley/toolbox/BasicNetwork.java
rename to src/main/java/com/android/volley/toolbox/BasicNetwork.java
diff --git a/src/com/android/volley/toolbox/ByteArrayPool.java b/src/main/java/com/android/volley/toolbox/ByteArrayPool.java
similarity index 100%
rename from src/com/android/volley/toolbox/ByteArrayPool.java
rename to src/main/java/com/android/volley/toolbox/ByteArrayPool.java
diff --git a/src/com/android/volley/toolbox/ClearCacheRequest.java b/src/main/java/com/android/volley/toolbox/ClearCacheRequest.java
similarity index 100%
rename from src/com/android/volley/toolbox/ClearCacheRequest.java
rename to src/main/java/com/android/volley/toolbox/ClearCacheRequest.java
diff --git a/src/com/android/volley/toolbox/DiskBasedCache.java b/src/main/java/com/android/volley/toolbox/DiskBasedCache.java
similarity index 100%
rename from src/com/android/volley/toolbox/DiskBasedCache.java
rename to src/main/java/com/android/volley/toolbox/DiskBasedCache.java
diff --git a/src/com/android/volley/toolbox/HttpClientStack.java b/src/main/java/com/android/volley/toolbox/HttpClientStack.java
similarity index 100%
rename from src/com/android/volley/toolbox/HttpClientStack.java
rename to src/main/java/com/android/volley/toolbox/HttpClientStack.java
diff --git a/src/com/android/volley/toolbox/HttpHeaderParser.java b/src/main/java/com/android/volley/toolbox/HttpHeaderParser.java
similarity index 100%
rename from src/com/android/volley/toolbox/HttpHeaderParser.java
rename to src/main/java/com/android/volley/toolbox/HttpHeaderParser.java
diff --git a/src/com/android/volley/toolbox/HttpStack.java b/src/main/java/com/android/volley/toolbox/HttpStack.java
similarity index 100%
rename from src/com/android/volley/toolbox/HttpStack.java
rename to src/main/java/com/android/volley/toolbox/HttpStack.java
diff --git a/src/com/android/volley/toolbox/HurlStack.java b/src/main/java/com/android/volley/toolbox/HurlStack.java
similarity index 100%
rename from src/com/android/volley/toolbox/HurlStack.java
rename to src/main/java/com/android/volley/toolbox/HurlStack.java
diff --git a/src/com/android/volley/toolbox/ImageLoader.java b/src/main/java/com/android/volley/toolbox/ImageLoader.java
similarity index 100%
rename from src/com/android/volley/toolbox/ImageLoader.java
rename to src/main/java/com/android/volley/toolbox/ImageLoader.java
diff --git a/src/com/android/volley/toolbox/ImageRequest.java b/src/main/java/com/android/volley/toolbox/ImageRequest.java
similarity index 100%
rename from src/com/android/volley/toolbox/ImageRequest.java
rename to src/main/java/com/android/volley/toolbox/ImageRequest.java
diff --git a/src/com/android/volley/toolbox/JsonArrayRequest.java b/src/main/java/com/android/volley/toolbox/JsonArrayRequest.java
similarity index 100%
rename from src/com/android/volley/toolbox/JsonArrayRequest.java
rename to src/main/java/com/android/volley/toolbox/JsonArrayRequest.java
diff --git a/src/com/android/volley/toolbox/JsonObjectRequest.java b/src/main/java/com/android/volley/toolbox/JsonObjectRequest.java
similarity index 100%
rename from src/com/android/volley/toolbox/JsonObjectRequest.java
rename to src/main/java/com/android/volley/toolbox/JsonObjectRequest.java
diff --git a/src/com/android/volley/toolbox/JsonRequest.java b/src/main/java/com/android/volley/toolbox/JsonRequest.java
similarity index 100%
rename from src/com/android/volley/toolbox/JsonRequest.java
rename to src/main/java/com/android/volley/toolbox/JsonRequest.java
diff --git a/src/com/android/volley/toolbox/NetworkImageView.java b/src/main/java/com/android/volley/toolbox/NetworkImageView.java
similarity index 100%
rename from src/com/android/volley/toolbox/NetworkImageView.java
rename to src/main/java/com/android/volley/toolbox/NetworkImageView.java
diff --git a/src/com/android/volley/toolbox/NoCache.java b/src/main/java/com/android/volley/toolbox/NoCache.java
similarity index 100%
rename from src/com/android/volley/toolbox/NoCache.java
rename to src/main/java/com/android/volley/toolbox/NoCache.java
diff --git a/src/com/android/volley/toolbox/PoolingByteArrayOutputStream.java b/src/main/java/com/android/volley/toolbox/PoolingByteArrayOutputStream.java
similarity index 100%
rename from src/com/android/volley/toolbox/PoolingByteArrayOutputStream.java
rename to src/main/java/com/android/volley/toolbox/PoolingByteArrayOutputStream.java
diff --git a/src/com/android/volley/toolbox/RequestFuture.java b/src/main/java/com/android/volley/toolbox/RequestFuture.java
similarity index 100%
rename from src/com/android/volley/toolbox/RequestFuture.java
rename to src/main/java/com/android/volley/toolbox/RequestFuture.java
diff --git a/src/com/android/volley/toolbox/StringRequest.java b/src/main/java/com/android/volley/toolbox/StringRequest.java
similarity index 100%
rename from src/com/android/volley/toolbox/StringRequest.java
rename to src/main/java/com/android/volley/toolbox/StringRequest.java
diff --git a/src/com/android/volley/toolbox/Volley.java b/src/main/java/com/android/volley/toolbox/Volley.java
similarity index 100%
rename from src/com/android/volley/toolbox/Volley.java
rename to src/main/java/com/android/volley/toolbox/Volley.java
diff --git a/tests/src/com/android/volley/CacheDispatcherTest.java b/src/test/java/com/android/volley/CacheDispatcherTest.java
similarity index 84%
rename from tests/src/com/android/volley/CacheDispatcherTest.java
rename to src/test/java/com/android/volley/CacheDispatcherTest.java
index 6ca70c6..42bdda0 100644
--- a/tests/src/com/android/volley/CacheDispatcherTest.java
+++ b/src/test/java/com/android/volley/CacheDispatcherTest.java
@@ -16,19 +16,23 @@
 
 package com.android.volley;
 
-import android.test.suitebuilder.annotation.MediumTest;
-
 import com.android.volley.mock.MockCache;
 import com.android.volley.mock.MockRequest;
 import com.android.volley.mock.MockResponseDelivery;
 import com.android.volley.mock.WaitableQueue;
 import com.android.volley.utils.CacheTestUtils;
 
-import junit.framework.TestCase;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
 
-@MediumTest
+import static org.junit.Assert.*;
+
+@RunWith(RobolectricTestRunner.class)
 @SuppressWarnings("rawtypes")
-public class CacheDispatcherTest extends TestCase {
+public class CacheDispatcherTest {
     private CacheDispatcher mDispatcher;
     private WaitableQueue mCacheQueue;
     private WaitableQueue mNetworkQueue;
@@ -38,10 +42,7 @@
 
     private static final long TIMEOUT_MILLIS = 5000;
 
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-
+    @Before public void setUp() throws Exception {
         mCacheQueue = new WaitableQueue();
         mNetworkQueue = new WaitableQueue();
         mCache = new MockCache();
@@ -53,15 +54,13 @@
         mDispatcher.start();
     }
 
-    @Override
-    protected void tearDown() throws Exception {
-        super.tearDown();
+    @After public void tearDown() throws Exception {
         mDispatcher.quit();
         mDispatcher.join();
     }
 
     // A cancelled request should not be processed at all.
-    public void testCancelledRequest() throws Exception {
+    @Test public void cancelledRequest() throws Exception {
         mRequest.cancel();
         mCacheQueue.add(mRequest);
         mCacheQueue.waitUntilEmpty(TIMEOUT_MILLIS);
@@ -70,7 +69,7 @@
     }
 
     // A cache miss does not post a response and puts the request on the network queue.
-    public void testCacheMiss() throws Exception {
+    @Test public void cacheMiss() throws Exception {
         mCacheQueue.add(mRequest);
         mCacheQueue.waitUntilEmpty(TIMEOUT_MILLIS);
         assertFalse(mDelivery.wasEitherResponseCalled());
@@ -80,7 +79,7 @@
     }
 
     // A non-expired cache hit posts a response and does not queue to the network.
-    public void testNonExpiredCacheHit() throws Exception {
+    @Test public void nonExpiredCacheHit() throws Exception {
         Cache.Entry entry = CacheTestUtils.makeRandomCacheEntry(null, false, false);
         mCache.setEntryToReturn(entry);
         mCacheQueue.add(mRequest);
@@ -90,7 +89,7 @@
     }
 
     // A soft-expired cache hit posts a response and queues to the network.
-    public void testSoftExpiredCacheHit() throws Exception {
+    @Test public void softExpiredCacheHit() throws Exception {
         Cache.Entry entry = CacheTestUtils.makeRandomCacheEntry(null, false, true);
         mCache.setEntryToReturn(entry);
         mCacheQueue.add(mRequest);
@@ -103,7 +102,7 @@
     }
 
     // An expired cache hit does not post a response and queues to the network.
-    public void testExpiredCacheHit() throws Exception {
+    @Test public void expiredCacheHit() throws Exception {
         Cache.Entry entry = CacheTestUtils.makeRandomCacheEntry(null, true, true);
         mCache.setEntryToReturn(entry);
         mCacheQueue.add(mRequest);
diff --git a/tests/src/com/android/volley/NetworkDispatcherTest.java b/src/test/java/com/android/volley/NetworkDispatcherTest.java
similarity index 83%
rename from tests/src/com/android/volley/NetworkDispatcherTest.java
rename to src/test/java/com/android/volley/NetworkDispatcherTest.java
index cc13ead..c5763bd 100644
--- a/tests/src/com/android/volley/NetworkDispatcherTest.java
+++ b/src/test/java/com/android/volley/NetworkDispatcherTest.java
@@ -16,20 +16,23 @@
 
 package com.android.volley;
 
-import android.test.suitebuilder.annotation.MediumTest;
-
 import com.android.volley.mock.MockCache;
 import com.android.volley.mock.MockNetwork;
 import com.android.volley.mock.MockRequest;
 import com.android.volley.mock.MockResponseDelivery;
 import com.android.volley.mock.WaitableQueue;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
 
 import java.util.Arrays;
 
-import junit.framework.TestCase;
+import static org.junit.Assert.*;
 
-@MediumTest
-public class NetworkDispatcherTest extends TestCase {
+@RunWith(RobolectricTestRunner.class)
+public class NetworkDispatcherTest {
     private NetworkDispatcher mDispatcher;
     private MockResponseDelivery mDelivery;
     private WaitableQueue mNetworkQueue;
@@ -40,10 +43,7 @@
     private static final byte[] CANNED_DATA = "Ceci n'est pas une vraie reponse".getBytes();
     private static final long TIMEOUT_MILLIS = 5000;
 
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-
+    @Before public void setUp() throws Exception {
         mDelivery = new MockResponseDelivery();
         mNetworkQueue = new WaitableQueue();
         mNetwork = new MockNetwork();
@@ -53,14 +53,12 @@
         mDispatcher.start();
     }
 
-    @Override
-    protected void tearDown() throws Exception {
-        super.tearDown();
+    @After public void tearDown() throws Exception {
         mDispatcher.quit();
         mDispatcher.join();
     }
 
-    public void testSuccessPostsResponse() throws Exception {
+    @Test public void successPostsResponse() throws Exception {
         mNetwork.setDataToReturn(CANNED_DATA);
         mNetwork.setNumExceptionsToThrow(0);
         mNetworkQueue.add(mRequest);
@@ -73,7 +71,7 @@
         assertTrue(Arrays.equals((byte[])response.result, CANNED_DATA));
     }
 
-    public void testExceptionPostsError() throws Exception {
+    @Test public void exceptionPostsError() throws Exception {
         mNetwork.setNumExceptionsToThrow(MockNetwork.ALWAYS_THROW_EXCEPTIONS);
         mNetworkQueue.add(mRequest);
         mNetworkQueue.waitUntilEmpty(TIMEOUT_MILLIS);
@@ -81,14 +79,14 @@
         assertTrue(mDelivery.postError_called);
     }
 
-    public void test_shouldCacheFalse() throws Exception {
+    @Test public void shouldCacheFalse() throws Exception {
         mRequest.setShouldCache(false);
         mNetworkQueue.add(mRequest);
         mNetworkQueue.waitUntilEmpty(TIMEOUT_MILLIS);
         assertFalse(mCache.putCalled);
     }
 
-    public void test_shouldCacheTrue() throws Exception {
+    @Test public void shouldCacheTrue() throws Exception {
         mNetwork.setDataToReturn(CANNED_DATA);
         mRequest.setShouldCache(true);
         mRequest.setCacheKey("bananaphone");
diff --git a/tests/src/com/android/volley/RequestQueueTest.java b/src/test/java/com/android/volley/RequestQueueTest.java
similarity index 89%
rename from tests/src/com/android/volley/RequestQueueTest.java
rename to src/test/java/com/android/volley/RequestQueueTest.java
index f99ff8e..cc88d3b 100644
--- a/tests/src/com/android/volley/RequestQueueTest.java
+++ b/src/test/java/com/android/volley/RequestQueueTest.java
@@ -24,9 +24,13 @@
 import com.android.volley.utils.ImmediateResponseDelivery;
 
 import android.os.SystemClock;
-import android.test.InstrumentationTestCase;
-import android.test.UiThreadTest;
-import android.test.suitebuilder.annotation.LargeTest;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
+import org.robolectric.annotation.Implements;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -35,14 +39,16 @@
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicInteger;
 
-@LargeTest
-public class RequestQueueTest extends InstrumentationTestCase {
+import static org.junit.Assert.*;
+
+// TODO: Resurrect these tests when we have something like a finish() observer.
+// They are really gross as-is and depend on a bunch of sleeping and whatnot.
+@Ignore
+@RunWith(RobolectricTestRunner.class)
+public class RequestQueueTest {
     private ResponseDelivery mDelivery;
 
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-
+    @Before public void setUp() throws Exception {
         mDelivery = new ImmediateResponseDelivery();
     }
 
@@ -65,8 +71,7 @@
         return requests;
     }
 
-    @UiThreadTest
-    public void testAdd_requestProcessedInCorrectOrder() throws Exception {
+    @Test public void add_requestProcessedInCorrectOrder() throws Exception {
         int requestsToMake = 100;
 
         OrderCheckingNetwork network = new OrderCheckingNetwork();
@@ -82,7 +87,7 @@
         queue.stop();
     }
 
-    public void testAdd_dedupeByCacheKey() throws Exception {
+    @Test public void add_dedupeByCacheKey() throws Exception {
         OrderCheckingNetwork network = new OrderCheckingNetwork();
         final AtomicInteger parsed = new AtomicInteger();
         final AtomicInteger delivered = new AtomicInteger();
@@ -106,7 +111,7 @@
         queue.stop();
     }
 
-    public void testCancelAll_onlyCorrectTag() throws Exception {
+    @Test public void cancelAll_onlyCorrectTag() throws Exception {
         MockNetwork network = new MockNetwork();
         RequestQueue queue = new RequestQueue(new NoCache(), network, 3, mDelivery);
         Object tagA = new Object();
diff --git a/tests/src/com/android/volley/RequestTest.java b/src/test/java/com/android/volley/RequestTest.java
similarity index 90%
rename from tests/src/com/android/volley/RequestTest.java
rename to src/test/java/com/android/volley/RequestTest.java
index 69f07e2..d5beca5 100644
--- a/tests/src/com/android/volley/RequestTest.java
+++ b/src/test/java/com/android/volley/RequestTest.java
@@ -16,20 +16,17 @@
 
 package com.android.volley;
 
-import android.test.suitebuilder.annotation.SmallTest;
-
 import com.android.volley.Request.Priority;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
 
-import junit.framework.TestCase;
+import static org.junit.Assert.*;
 
-@SmallTest
-public class RequestTest extends TestCase {
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-    }
-
-    public void testCompareTo() {
+@RunWith(RobolectricTestRunner.class)
+public class RequestTest {
+    
+    @Test public void compareTo() {
         int sequence = 0;
         TestRequest low = new TestRequest(Priority.LOW);
         low.setSequence(sequence++);
@@ -70,7 +67,7 @@
         }
     }
 
-    public void testUrlParsing() {
+    @Test public void urlParsing() {
         UrlParseRequest nullUrl = new UrlParseRequest(null);
         assertEquals(0, nullUrl.getTrafficStatsTag());
         UrlParseRequest emptyUrl = new UrlParseRequest("");
diff --git a/tests/src/com/android/volley/ResponseDeliveryTest.java b/src/test/java/com/android/volley/ResponseDeliveryTest.java
similarity index 79%
rename from tests/src/com/android/volley/ResponseDeliveryTest.java
rename to src/test/java/com/android/volley/ResponseDeliveryTest.java
index 82bb161..9fadfc3 100644
--- a/tests/src/com/android/volley/ResponseDeliveryTest.java
+++ b/src/test/java/com/android/volley/ResponseDeliveryTest.java
@@ -16,25 +16,26 @@
 
 package com.android.volley;
 
-import android.test.suitebuilder.annotation.MediumTest;
-
 import com.android.volley.mock.MockRequest;
 import com.android.volley.utils.CacheTestUtils;
 import com.android.volley.utils.ImmediateResponseDelivery;
 
-import junit.framework.TestCase;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
 
-@MediumTest
-public class ResponseDeliveryTest extends TestCase {
+import static org.junit.Assert.*;
+
+@RunWith(RobolectricTestRunner.class)
+public class ResponseDeliveryTest {
 
     private ExecutorDelivery mDelivery;
     private MockRequest mRequest;
     private Response<byte[]> mSuccessResponse;
 
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-
+    @Before public void setUp() throws Exception {
         // Make the delivery just run its posted responses immediately.
         mDelivery = new ImmediateResponseDelivery();
         mRequest = new MockRequest();
@@ -44,20 +45,20 @@
         mSuccessResponse = Response.success(data, cacheEntry);
     }
 
-    public void testPostResponse_callsDeliverResponse() {
+    @Test public void postResponseCallsDeliverResponse() {
         mDelivery.postResponse(mRequest, mSuccessResponse);
         assertTrue(mRequest.deliverResponse_called);
         assertFalse(mRequest.deliverError_called);
     }
 
-    public void testPostResponse_suppressesCanceled() {
+    @Test public void postResponseSuppressesCanceled() {
         mRequest.cancel();
         mDelivery.postResponse(mRequest, mSuccessResponse);
         assertFalse(mRequest.deliverResponse_called);
         assertFalse(mRequest.deliverError_called);
     }
 
-    public void testPostError_callsDeliverError() {
+    @Test public void postErrorCallsDeliverError() {
         Response<byte[]> errorResponse = Response.error(new ServerError());
 
         mDelivery.postResponse(mRequest, errorResponse);
diff --git a/tests/src/com/android/volley/mock/MockCache.java b/src/test/java/com/android/volley/mock/MockCache.java
similarity index 100%
rename from tests/src/com/android/volley/mock/MockCache.java
rename to src/test/java/com/android/volley/mock/MockCache.java
diff --git a/tests/src/com/android/volley/mock/MockHttpClient.java b/src/test/java/com/android/volley/mock/MockHttpClient.java
similarity index 100%
rename from tests/src/com/android/volley/mock/MockHttpClient.java
rename to src/test/java/com/android/volley/mock/MockHttpClient.java
diff --git a/tests/src/com/android/volley/mock/MockHttpStack.java b/src/test/java/com/android/volley/mock/MockHttpStack.java
similarity index 100%
rename from tests/src/com/android/volley/mock/MockHttpStack.java
rename to src/test/java/com/android/volley/mock/MockHttpStack.java
diff --git a/tests/src/com/android/volley/mock/MockHttpURLConnection.java b/src/test/java/com/android/volley/mock/MockHttpURLConnection.java
similarity index 100%
rename from tests/src/com/android/volley/mock/MockHttpURLConnection.java
rename to src/test/java/com/android/volley/mock/MockHttpURLConnection.java
diff --git a/tests/src/com/android/volley/mock/MockNetwork.java b/src/test/java/com/android/volley/mock/MockNetwork.java
similarity index 100%
rename from tests/src/com/android/volley/mock/MockNetwork.java
rename to src/test/java/com/android/volley/mock/MockNetwork.java
diff --git a/tests/src/com/android/volley/mock/MockRequest.java b/src/test/java/com/android/volley/mock/MockRequest.java
similarity index 100%
rename from tests/src/com/android/volley/mock/MockRequest.java
rename to src/test/java/com/android/volley/mock/MockRequest.java
diff --git a/tests/src/com/android/volley/mock/MockResponseDelivery.java b/src/test/java/com/android/volley/mock/MockResponseDelivery.java
similarity index 100%
rename from tests/src/com/android/volley/mock/MockResponseDelivery.java
rename to src/test/java/com/android/volley/mock/MockResponseDelivery.java
diff --git a/tests/src/com/android/volley/mock/TestRequest.java b/src/test/java/com/android/volley/mock/TestRequest.java
similarity index 100%
rename from tests/src/com/android/volley/mock/TestRequest.java
rename to src/test/java/com/android/volley/mock/TestRequest.java
diff --git a/tests/src/com/android/volley/mock/WaitableQueue.java b/src/test/java/com/android/volley/mock/WaitableQueue.java
similarity index 100%
rename from tests/src/com/android/volley/mock/WaitableQueue.java
rename to src/test/java/com/android/volley/mock/WaitableQueue.java
diff --git a/tests/src/com/android/volley/toolbox/BasicNetworkTest.java b/src/test/java/com/android/volley/toolbox/BasicNetworkTest.java
similarity index 87%
rename from tests/src/com/android/volley/toolbox/BasicNetworkTest.java
rename to src/test/java/com/android/volley/toolbox/BasicNetworkTest.java
index ccbb1e8..89718b1 100644
--- a/tests/src/com/android/volley/toolbox/BasicNetworkTest.java
+++ b/src/test/java/com/android/volley/toolbox/BasicNetworkTest.java
@@ -25,22 +25,21 @@
 import org.apache.http.entity.StringEntity;
 import org.apache.http.message.BasicHttpResponse;
 
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+import static org.junit.Assert.*;
 
 import java.util.HashMap;
 import java.util.Map;
 
-@SmallTest
-public class BasicNetworkTest extends AndroidTestCase {
+@RunWith(RobolectricTestRunner.class)
+public class BasicNetworkTest {
 
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-        mContext = getContext();
-    }
-
-    public void testHeadersAndPostParams() throws Exception {
+    @Test public void headersAndPostParams() throws Exception {
         MockHttpStack mockHttpStack = new MockHttpStack();
         BasicHttpResponse fakeResponse = new BasicHttpResponse(new ProtocolVersion("HTTP", 1, 1),
                 200, "OK");
diff --git a/tests/src/com/android/volley/toolbox/ByteArrayPoolTest.java b/src/test/java/com/android/volley/toolbox/ByteArrayPoolTest.java
similarity index 86%
rename from tests/src/com/android/volley/toolbox/ByteArrayPoolTest.java
rename to src/test/java/com/android/volley/toolbox/ByteArrayPoolTest.java
index 793d4df..661e994 100644
--- a/tests/src/com/android/volley/toolbox/ByteArrayPoolTest.java
+++ b/src/test/java/com/android/volley/toolbox/ByteArrayPoolTest.java
@@ -16,10 +16,15 @@
 
 package com.android.volley.toolbox;
 
-import android.test.AndroidTestCase;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
 
-public class ByteArrayPoolTest extends AndroidTestCase {
-    public void testReusesBuffer() {
+import static org.junit.Assert.*;
+
+public class ByteArrayPoolTest {
+    @Test public void reusesBuffer() {
         ByteArrayPool pool = new ByteArrayPool(32);
 
         byte[] buf1 = pool.getBuf(16);
@@ -35,7 +40,7 @@
         assertTrue(buf3 != buf4);
     }
 
-    public void testObeysSizeLimit() {
+    @Test public void obeysSizeLimit() {
         ByteArrayPool pool = new ByteArrayPool(32);
 
         byte[] buf1 = pool.getBuf(16);
@@ -56,7 +61,7 @@
         assertTrue(buf6 != buf1 && buf6 != buf2 && buf6 != buf3);
     }
 
-    public void testReturnsBufferWithRightSize() {
+    @Test public void returnsBufferWithRightSize() {
         ByteArrayPool pool = new ByteArrayPool(32);
 
         byte[] buf1 = pool.getBuf(16);
diff --git a/tests/src/com/android/volley/toolbox/DiskBasedCacheTest.java b/src/test/java/com/android/volley/toolbox/DiskBasedCacheTest.java
similarity index 93%
rename from tests/src/com/android/volley/toolbox/DiskBasedCacheTest.java
rename to src/test/java/com/android/volley/toolbox/DiskBasedCacheTest.java
index 674d5b4..4b2955d 100644
--- a/tests/src/com/android/volley/toolbox/DiskBasedCacheTest.java
+++ b/src/test/java/com/android/volley/toolbox/DiskBasedCacheTest.java
@@ -16,20 +16,21 @@
 
 package com.android.volley.toolbox;
 
-import android.test.AndroidTestCase;
-
 import com.android.volley.Cache;
 import com.android.volley.toolbox.DiskBasedCache.CacheHeader;
+import org.junit.Test;
 
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.util.HashMap;
 import java.util.Map;
 
-public class DiskBasedCacheTest extends AndroidTestCase {
+import static org.junit.Assert.*;
+
+public class DiskBasedCacheTest {
 
     // Simple end-to-end serialize/deserialize test.
-    public void testCacheHeaderSerialization() throws Exception {
+    @Test public void cacheHeaderSerialization() throws Exception {
         Cache.Entry e = new Cache.Entry();
         e.data = new byte[8];
         e.serverDate = 1234567L;
@@ -53,7 +54,7 @@
         assertEquals(first.responseHeaders, second.responseHeaders);
     }
 
-    public void testSerializeInt() throws Exception {
+    @Test public void serializeInt() throws Exception {
         ByteArrayOutputStream baos = new ByteArrayOutputStream();
         DiskBasedCache.writeInt(baos, 0);
         DiskBasedCache.writeInt(baos, 19791214);
@@ -68,7 +69,7 @@
         assertEquals(DiskBasedCache.readInt(bais), Integer.MAX_VALUE);
     }
 
-    public void testSerializeLong() throws Exception {
+    @Test public void serializeLong() throws Exception {
         ByteArrayOutputStream baos = new ByteArrayOutputStream();
         DiskBasedCache.writeLong(baos, 0);
         DiskBasedCache.writeLong(baos, 31337);
@@ -87,7 +88,7 @@
         assertEquals(DiskBasedCache.readLong(bais), Long.MAX_VALUE);
     }
 
-    public void testSerializeString() throws Exception {
+    @Test public void serializeString() throws Exception {
         ByteArrayOutputStream baos = new ByteArrayOutputStream();
         DiskBasedCache.writeString(baos, "");
         DiskBasedCache.writeString(baos, "This is a string.");
@@ -98,7 +99,7 @@
         assertEquals(DiskBasedCache.readString(bais), "ファイカス");
     }
 
-    public void testSerializeMap() throws Exception {
+    @Test public void serializeMap() throws Exception {
         ByteArrayOutputStream baos = new ByteArrayOutputStream();
         Map<String, String> empty = new HashMap<String, String>();
         DiskBasedCache.writeStringStringMap(empty, baos);
diff --git a/tests/src/com/android/volley/toolbox/HttpClientStackTest.java b/src/test/java/com/android/volley/toolbox/HttpClientStackTest.java
similarity index 81%
rename from tests/src/com/android/volley/toolbox/HttpClientStackTest.java
rename to src/test/java/com/android/volley/toolbox/HttpClientStackTest.java
index c25821e..0c417d4 100644
--- a/tests/src/com/android/volley/toolbox/HttpClientStackTest.java
+++ b/src/test/java/com/android/volley/toolbox/HttpClientStackTest.java
@@ -29,19 +29,16 @@
 import org.apache.http.client.methods.HttpTrace;
 import org.apache.http.client.methods.HttpUriRequest;
 
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
 
-@SmallTest
-public class HttpClientStackTest extends AndroidTestCase {
+import static org.junit.Assert.*;
 
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-        mContext = getContext();
-    }
+@RunWith(RobolectricTestRunner.class)
+public class HttpClientStackTest {
 
-    public void testCreateDeprecatedGetRequest() throws Exception {
+    @Test public void createDeprecatedGetRequest() throws Exception {
         TestRequest.DeprecatedGet request = new TestRequest.DeprecatedGet();
         assertEquals(request.getMethod(), Method.DEPRECATED_GET_OR_POST);
 
@@ -49,7 +46,7 @@
         assertTrue(httpRequest instanceof HttpGet);
     }
 
-    public void testCreateDeprecatedPostRequest() throws Exception {
+    @Test public void createDeprecatedPostRequest() throws Exception {
         TestRequest.DeprecatedPost request = new TestRequest.DeprecatedPost();
         assertEquals(request.getMethod(), Method.DEPRECATED_GET_OR_POST);
 
@@ -57,7 +54,7 @@
         assertTrue(httpRequest instanceof HttpPost);
     }
 
-    public void testCreateGetRequest() throws Exception {
+    @Test public void createGetRequest() throws Exception {
         TestRequest.Get request = new TestRequest.Get();
         assertEquals(request.getMethod(), Method.GET);
 
@@ -65,7 +62,7 @@
         assertTrue(httpRequest instanceof HttpGet);
     }
 
-    public void testCreatePostRequest() throws Exception {
+    @Test public void createPostRequest() throws Exception {
         TestRequest.Post request = new TestRequest.Post();
         assertEquals(request.getMethod(), Method.POST);
 
@@ -73,7 +70,7 @@
         assertTrue(httpRequest instanceof HttpPost);
     }
 
-    public void testCreatePostRequestWithBody() throws Exception {
+    @Test public void createPostRequestWithBody() throws Exception {
         TestRequest.PostWithBody request = new TestRequest.PostWithBody();
         assertEquals(request.getMethod(), Method.POST);
 
@@ -81,7 +78,7 @@
         assertTrue(httpRequest instanceof HttpPost);
     }
 
-    public void testCreatePutRequest() throws Exception {
+    @Test public void createPutRequest() throws Exception {
         TestRequest.Put request = new TestRequest.Put();
         assertEquals(request.getMethod(), Method.PUT);
 
@@ -89,7 +86,7 @@
         assertTrue(httpRequest instanceof HttpPut);
     }
 
-    public void testCreatePutRequestWithBody() throws Exception {
+    @Test public void createPutRequestWithBody() throws Exception {
         TestRequest.PutWithBody request = new TestRequest.PutWithBody();
         assertEquals(request.getMethod(), Method.PUT);
 
@@ -97,7 +94,7 @@
         assertTrue(httpRequest instanceof HttpPut);
     }
 
-    public void testCreateDeleteRequest() throws Exception {
+    @Test public void createDeleteRequest() throws Exception {
         TestRequest.Delete request = new TestRequest.Delete();
         assertEquals(request.getMethod(), Method.DELETE);
 
@@ -105,7 +102,7 @@
         assertTrue(httpRequest instanceof HttpDelete);
     }
 
-    public void testCreateHeadRequest() throws Exception {
+    @Test public void createHeadRequest() throws Exception {
         TestRequest.Head request = new TestRequest.Head();
         assertEquals(request.getMethod(), Method.HEAD);
 
@@ -113,7 +110,7 @@
         assertTrue(httpRequest instanceof HttpHead);
     }
 
-    public void testCreateOptionsRequest() throws Exception {
+    @Test public void createOptionsRequest() throws Exception {
         TestRequest.Options request = new TestRequest.Options();
         assertEquals(request.getMethod(), Method.OPTIONS);
 
@@ -121,7 +118,7 @@
         assertTrue(httpRequest instanceof HttpOptions);
     }
 
-    public void testCreateTraceRequest() throws Exception {
+    @Test public void createTraceRequest() throws Exception {
         TestRequest.Trace request = new TestRequest.Trace();
         assertEquals(request.getMethod(), Method.TRACE);
 
@@ -129,7 +126,7 @@
         assertTrue(httpRequest instanceof HttpTrace);
     }
 
-    public void testCreatePatchRequest() throws Exception {
+    @Test public void createPatchRequest() throws Exception {
         TestRequest.Patch request = new TestRequest.Patch();
         assertEquals(request.getMethod(), Method.PATCH);
 
@@ -137,7 +134,7 @@
         assertTrue(httpRequest instanceof HttpPatch);
     }
 
-    public void testCreatePatchRequestWithBody() throws Exception {
+    @Test public void createPatchRequestWithBody() throws Exception {
         TestRequest.PatchWithBody request = new TestRequest.PatchWithBody();
         assertEquals(request.getMethod(), Method.PATCH);
 
diff --git a/tests/src/com/android/volley/toolbox/HttpHeaderParserTest.java b/src/test/java/com/android/volley/toolbox/HttpHeaderParserTest.java
similarity index 88%
rename from tests/src/com/android/volley/toolbox/HttpHeaderParserTest.java
rename to src/test/java/com/android/volley/toolbox/HttpHeaderParserTest.java
index 8f703f0..b8c4847 100644
--- a/tests/src/com/android/volley/toolbox/HttpHeaderParserTest.java
+++ b/src/test/java/com/android/volley/toolbox/HttpHeaderParserTest.java
@@ -16,15 +16,15 @@
 
 package com.android.volley.toolbox;
 
-import android.test.suitebuilder.annotation.SmallTest;
-
 import com.android.volley.Cache;
 import com.android.volley.NetworkResponse;
 
-import junit.framework.TestCase;
-
 import org.apache.http.Header;
 import org.apache.http.message.BasicHeader;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
 
 import java.text.DateFormat;
 import java.text.SimpleDateFormat;
@@ -33,8 +33,10 @@
 import java.util.Locale;
 import java.util.Map;
 
-@SmallTest
-public class HttpHeaderParserTest extends TestCase {
+import static org.junit.Assert.*;
+
+@RunWith(RobolectricTestRunner.class)
+public class HttpHeaderParserTest {
 
     private static long ONE_MINUTE_MILLIS = 1000L * 60;
     private static long ONE_HOUR_MILLIS = 1000L * 60 * 60;
@@ -42,14 +44,12 @@
     private NetworkResponse response;
     private Map<String, String> headers;
 
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
+    @Before public void setUp() throws Exception {
         headers = new HashMap<String, String>();
         response = new NetworkResponse(0, null, headers, false);
     }
 
-    public void testParseCacheHeaders_noHeaders() {
+    @Test public void parseCacheHeaders_noHeaders() {
         Cache.Entry entry = HttpHeaderParser.parseCacheHeaders(response);
 
         assertNotNull(entry);
@@ -59,7 +59,7 @@
         assertEquals(0, entry.softTtl);
     }
 
-    public void testParseCacheHeaders_headersSet() {
+    @Test public void parseCacheHeaders_headersSet() {
         headers.put("MyCustomHeader", "42");
 
         Cache.Entry entry = HttpHeaderParser.parseCacheHeaders(response);
@@ -70,7 +70,7 @@
         assertEquals("42", entry.responseHeaders.get("MyCustomHeader"));
     }
 
-    public void testParseCacheHeaders_etag() {
+    @Test public void parseCacheHeaders_etag() {
         headers.put("ETag", "Yow!");
 
         Cache.Entry entry = HttpHeaderParser.parseCacheHeaders(response);
@@ -79,7 +79,7 @@
         assertEquals("Yow!", entry.etag);
     }
 
-    public void testParseCacheHeaders_normalExpire() {
+    @Test public void parseCacheHeaders_normalExpire() {
         long now = System.currentTimeMillis();
         headers.put("Date", rfc1123Date(now));
         headers.put("Expires", rfc1123Date(now + ONE_HOUR_MILLIS));
@@ -93,7 +93,7 @@
         assertTrue(entry.ttl == entry.softTtl);
     }
 
-    public void testParseCacheHeaders_expiresInPast() {
+    @Test public void parseCacheHeaders_expiresInPast() {
         long now = System.currentTimeMillis();
         headers.put("Date", rfc1123Date(now));
         headers.put("Expires", rfc1123Date(now - ONE_HOUR_MILLIS));
@@ -107,7 +107,7 @@
         assertEquals(0, entry.softTtl);
     }
 
-    public void testParseCacheHeaders_serverRelative() {
+    @Test public void parseCacheHeaders_serverRelative() {
 
         long now = System.currentTimeMillis();
         // Set "current" date as one hour in the future
@@ -121,7 +121,7 @@
         assertEquals(entry.softTtl, entry.ttl);
     }
 
-    public void testParseCacheHeaders_cacheControlOverridesExpires() {
+    @Test public void parseCacheHeaders_cacheControlOverridesExpires() {
         long now = System.currentTimeMillis();
         headers.put("Date", rfc1123Date(now));
         headers.put("Expires", rfc1123Date(now + ONE_HOUR_MILLIS));
@@ -135,7 +135,7 @@
         assertEquals(entry.softTtl, entry.ttl);
     }
 
-    public void testParseCacheHeaders_cacheControlNoCache() {
+    @Test public void parseCacheHeaders_cacheControlNoCache() {
         long now = System.currentTimeMillis();
         headers.put("Date", rfc1123Date(now));
         headers.put("Expires", rfc1123Date(now + ONE_HOUR_MILLIS));
@@ -146,7 +146,7 @@
         assertNull(entry);
     }
 
-    public void testParseCacheHeaders_cacheControlMustRevalidate() {
+    @Test public void parseCacheHeaders_cacheControlMustRevalidate() {
         long now = System.currentTimeMillis();
         headers.put("Date", rfc1123Date(now));
         headers.put("Expires", rfc1123Date(now + ONE_HOUR_MILLIS));
@@ -172,7 +172,7 @@
 
     // --------------------------
 
-    public void testParseCharset() {
+    @Test public void parseCharset() {
         // Like the ones we usually see
         headers.put("Content-Type", "text/plain; charset=utf-8");
         assertEquals("utf-8", HttpHeaderParser.parseCharset(headers));
@@ -202,7 +202,7 @@
         assertEquals("ISO-8859-1", HttpHeaderParser.parseCharset(headers));
     }
 
-    public void testParseCaseInsensitive() {
+    @Test public void parseCaseInsensitive() {
 
         long now = System.currentTimeMillis();
 
diff --git a/tests/src/com/android/volley/toolbox/HurlStackTest.java b/src/test/java/com/android/volley/toolbox/HurlStackTest.java
similarity index 81%
rename from tests/src/com/android/volley/toolbox/HurlStackTest.java
rename to src/test/java/com/android/volley/toolbox/HurlStackTest.java
index f35b9e5..42aeea8 100644
--- a/tests/src/com/android/volley/toolbox/HurlStackTest.java
+++ b/src/test/java/com/android/volley/toolbox/HurlStackTest.java
@@ -20,22 +20,23 @@
 import com.android.volley.mock.MockHttpURLConnection;
 import com.android.volley.mock.TestRequest;
 
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
 
-@SmallTest
-public class HurlStackTest extends AndroidTestCase {
+import static org.junit.Assert.*;
+
+@RunWith(RobolectricTestRunner.class)
+public class HurlStackTest {
 
     private MockHttpURLConnection mMockConnection;
 
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-        mContext = getContext();
+    @Before public void setUp() throws Exception {
         mMockConnection = new MockHttpURLConnection();
     }
 
-    public void testConnectionForDeprecatedGetRequest() throws Exception {
+    @Test public void connectionForDeprecatedGetRequest() throws Exception {
         TestRequest.DeprecatedGet request = new TestRequest.DeprecatedGet();
         assertEquals(request.getMethod(), Method.DEPRECATED_GET_OR_POST);
 
@@ -44,7 +45,7 @@
         assertFalse(mMockConnection.getDoOutput());
     }
 
-    public void testConnectionForDeprecatedPostRequest() throws Exception {
+    @Test public void connectionForDeprecatedPostRequest() throws Exception {
         TestRequest.DeprecatedPost request = new TestRequest.DeprecatedPost();
         assertEquals(request.getMethod(), Method.DEPRECATED_GET_OR_POST);
 
@@ -53,7 +54,7 @@
         assertTrue(mMockConnection.getDoOutput());
     }
 
-    public void testConnectionForGetRequest() throws Exception {
+    @Test public void connectionForGetRequest() throws Exception {
         TestRequest.Get request = new TestRequest.Get();
         assertEquals(request.getMethod(), Method.GET);
 
@@ -62,7 +63,7 @@
         assertFalse(mMockConnection.getDoOutput());
     }
 
-    public void testConnectionForPostRequest() throws Exception {
+    @Test public void connectionForPostRequest() throws Exception {
         TestRequest.Post request = new TestRequest.Post();
         assertEquals(request.getMethod(), Method.POST);
 
@@ -71,7 +72,7 @@
         assertFalse(mMockConnection.getDoOutput());
     }
 
-    public void testConnectionForPostWithBodyRequest() throws Exception {
+    @Test public void connectionForPostWithBodyRequest() throws Exception {
         TestRequest.PostWithBody request = new TestRequest.PostWithBody();
         assertEquals(request.getMethod(), Method.POST);
 
@@ -80,7 +81,7 @@
         assertTrue(mMockConnection.getDoOutput());
     }
 
-    public void testConnectionForPutRequest() throws Exception {
+    @Test public void connectionForPutRequest() throws Exception {
         TestRequest.Put request = new TestRequest.Put();
         assertEquals(request.getMethod(), Method.PUT);
 
@@ -89,7 +90,7 @@
         assertFalse(mMockConnection.getDoOutput());
     }
 
-    public void testConnectionForPutWithBodyRequest() throws Exception {
+    @Test public void connectionForPutWithBodyRequest() throws Exception {
         TestRequest.PutWithBody request = new TestRequest.PutWithBody();
         assertEquals(request.getMethod(), Method.PUT);
 
@@ -98,7 +99,7 @@
         assertTrue(mMockConnection.getDoOutput());
     }
 
-    public void testConnectionForDeleteRequest() throws Exception {
+    @Test public void connectionForDeleteRequest() throws Exception {
         TestRequest.Delete request = new TestRequest.Delete();
         assertEquals(request.getMethod(), Method.DELETE);
 
@@ -107,7 +108,7 @@
         assertFalse(mMockConnection.getDoOutput());
     }
 
-    public void testConnectionForHeadRequest() throws Exception {
+    @Test public void connectionForHeadRequest() throws Exception {
         TestRequest.Head request = new TestRequest.Head();
         assertEquals(request.getMethod(), Method.HEAD);
 
@@ -116,7 +117,7 @@
         assertFalse(mMockConnection.getDoOutput());
     }
 
-    public void testConnectionForOptionsRequest() throws Exception {
+    @Test public void connectionForOptionsRequest() throws Exception {
         TestRequest.Options request = new TestRequest.Options();
         assertEquals(request.getMethod(), Method.OPTIONS);
 
@@ -125,7 +126,7 @@
         assertFalse(mMockConnection.getDoOutput());
     }
 
-    public void testConnectionForTraceRequest() throws Exception {
+    @Test public void connectionForTraceRequest() throws Exception {
         TestRequest.Trace request = new TestRequest.Trace();
         assertEquals(request.getMethod(), Method.TRACE);
 
@@ -134,7 +135,7 @@
         assertFalse(mMockConnection.getDoOutput());
     }
 
-    public void testConnectionForPatchRequest() throws Exception {
+    @Test public void connectionForPatchRequest() throws Exception {
         TestRequest.Patch request = new TestRequest.Patch();
         assertEquals(request.getMethod(), Method.PATCH);
 
@@ -143,7 +144,7 @@
         assertFalse(mMockConnection.getDoOutput());
     }
 
-    public void testConnectionForPatchWithBodyRequest() throws Exception {
+    @Test public void connectionForPatchWithBodyRequest() throws Exception {
         TestRequest.PatchWithBody request = new TestRequest.PatchWithBody();
         assertEquals(request.getMethod(), Method.PATCH);
 
diff --git a/tests/src/com/android/volley/toolbox/ImageRequestTest.java b/src/test/java/com/android/volley/toolbox/ImageRequestTest.java
similarity index 73%
rename from tests/src/com/android/volley/toolbox/ImageRequestTest.java
rename to src/test/java/com/android/volley/toolbox/ImageRequestTest.java
index c5a7ada..2f4495a 100644
--- a/tests/src/com/android/volley/toolbox/ImageRequestTest.java
+++ b/src/test/java/com/android/volley/toolbox/ImageRequestTest.java
@@ -16,28 +16,40 @@
 
 package com.android.volley.toolbox;
 
-import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.Bitmap.Config;
-import android.test.InstrumentationTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
 
 import com.android.volley.NetworkResponse;
 import com.android.volley.Response;
-import com.android.volley.tests.R;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.shadows.ShadowBitmapFactory;
 
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 
-@SmallTest
-public class ImageRequestTest extends InstrumentationTestCase {
+import static org.junit.Assert.*;
 
-    public void testParseNetworkResponse_resizing() throws Exception {
-        byte[] jpegBytes = readRawResource(
-                getInstrumentation().getContext().getResources(), R.raw.large_jpeg_1024_500);
+@RunWith(RobolectricTestRunner.class)
+public class ImageRequestTest {
+
+    @Test public void parseNetworkResponse_resizing() throws Exception {
+        // This is a horrible hack but Robolectric doesn't have a way to provide
+        // width and height hints for decodeByteArray. It works because the byte array
+        // "file:fake" is ASCII encodable and thus the name in Robolectric's fake
+        // bitmap creator survives as-is, and provideWidthAndHeightHints puts
+        // "file:" + name in its lookaside map. I write all this because it will
+        // probably break mysteriously at some point and I feel terrible about your
+        // having to debug it.
+        byte[] jpegBytes = "file:fake".getBytes();
+        ShadowBitmapFactory.provideWidthAndHeightHints("fake", 1024, 500);
         NetworkResponse jpeg = new NetworkResponse(jpegBytes);
 
+        // No resize
+        verifyResize(jpeg, 0, 0, 1024, 500);
+
         // Exact sizes
         verifyResize(jpeg, 512, 250, 512, 250); // exactly half
         verifyResize(jpeg, 511, 249, 509, 249); // just under half
@@ -53,9 +65,6 @@
         verifyResize(jpeg, 0, 250, 512, 250);
         verifyResize(jpeg, 0, 391, 800, 391);
         verifyResize(jpeg, 0, 500, 1024, 500);
-
-        // No resize
-        verifyResize(jpeg, 0, 0, 1024, 500);
     }
 
     private void verifyResize(NetworkResponse networkResponse, int maxWidth, int maxHeight,
@@ -71,7 +80,7 @@
         assertEquals(expectedHeight, bitmap.getHeight());
     }
 
-    public void testFindBestSampleSize() {
+    @Test public void findBestSampleSize() {
         // desired == actual == 1
         assertEquals(1, ImageRequest.findBestSampleSize(100, 150, 100, 150));
 
@@ -85,8 +94,7 @@
         assertEquals(4, ImageRequest.findBestSampleSize(100, 200, 24, 50));
     }
 
-    private static byte[] readRawResource(Resources res, int resId) throws IOException {
-        InputStream in = res.openRawResource(resId);
+    private static byte[] readInputStream(InputStream in) throws IOException {
         ByteArrayOutputStream bytes = new ByteArrayOutputStream();
         byte[] buffer = new byte[1024];
         int count;
@@ -96,5 +104,4 @@
         in.close();
         return bytes.toByteArray();
     }
-
 }
diff --git a/tests/src/com/android/volley/toolbox/NetworkImageViewTest.java b/src/test/java/com/android/volley/toolbox/NetworkImageViewTest.java
similarity index 74%
rename from tests/src/com/android/volley/toolbox/NetworkImageViewTest.java
rename to src/test/java/com/android/volley/toolbox/NetworkImageViewTest.java
index 613926e..48c81b6 100644
--- a/tests/src/com/android/volley/toolbox/NetworkImageViewTest.java
+++ b/src/test/java/com/android/volley/toolbox/NetworkImageViewTest.java
@@ -1,20 +1,26 @@
 package com.android.volley.toolbox;
 
-import android.test.InstrumentationTestCase;
 import android.view.ViewGroup.LayoutParams;
 
-public class NetworkImageViewTest extends InstrumentationTestCase {
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.Robolectric;
+import org.robolectric.RobolectricTestRunner;
+
+import static org.junit.Assert.*;
+
+@RunWith(RobolectricTestRunner.class)
+public class NetworkImageViewTest {
     private NetworkImageView mNIV;
     private MockImageLoader mMockImageLoader;
 
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
+    @Before public void setUp() throws Exception {
         mMockImageLoader = new MockImageLoader();
-        mNIV = new NetworkImageView(getInstrumentation().getContext());
+        mNIV = new NetworkImageView(Robolectric.application);
     }
 
-    public void testSetImageUrl_requestsImage() {
+    @Test public void setImageUrl_requestsImage() {
         mNIV.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
         mNIV.setImageUrl("http://foo", mMockImageLoader);
         assertEquals("http://foo", mMockImageLoader.lastRequestUrl);
diff --git a/tests/src/com/android/volley/toolbox/PoolingByteArrayOutputStreamTest.java b/src/test/java/com/android/volley/toolbox/PoolingByteArrayOutputStreamTest.java
similarity index 83%
rename from tests/src/com/android/volley/toolbox/PoolingByteArrayOutputStreamTest.java
rename to src/test/java/com/android/volley/toolbox/PoolingByteArrayOutputStreamTest.java
index 14feb71..c3bfac7 100644
--- a/tests/src/com/android/volley/toolbox/PoolingByteArrayOutputStreamTest.java
+++ b/src/test/java/com/android/volley/toolbox/PoolingByteArrayOutputStreamTest.java
@@ -19,33 +19,33 @@
 import java.io.IOException;
 import java.util.Arrays;
 
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
+import org.junit.Test;
 
-@SmallTest
-public class PoolingByteArrayOutputStreamTest extends AndroidTestCase {
-    public void testPooledOneBuffer() throws IOException {
+import static org.junit.Assert.*;
+
+public class PoolingByteArrayOutputStreamTest {
+    @Test public void pooledOneBuffer() throws IOException {
         ByteArrayPool pool = new ByteArrayPool(32768);
         writeOneBuffer(pool);
         writeOneBuffer(pool);
         writeOneBuffer(pool);
     }
 
-    public void testPooledIndividualWrites() throws IOException {
+    @Test public void pooledIndividualWrites() throws IOException {
         ByteArrayPool pool = new ByteArrayPool(32768);
         writeBytesIndividually(pool);
         writeBytesIndividually(pool);
         writeBytesIndividually(pool);
     }
 
-    public void testUnpooled() throws IOException {
+    @Test public void unpooled() throws IOException {
         ByteArrayPool pool = new ByteArrayPool(0);
         writeOneBuffer(pool);
         writeOneBuffer(pool);
         writeOneBuffer(pool);
     }
 
-    public void testUnpooledIndividualWrites() throws IOException {
+    @Test public void unpooledIndividualWrites() throws IOException {
         ByteArrayPool pool = new ByteArrayPool(0);
         writeBytesIndividually(pool);
         writeBytesIndividually(pool);
diff --git a/tests/src/com/android/volley/utils/CacheTestUtils.java b/src/test/java/com/android/volley/utils/CacheTestUtils.java
similarity index 100%
rename from tests/src/com/android/volley/utils/CacheTestUtils.java
rename to src/test/java/com/android/volley/utils/CacheTestUtils.java
diff --git a/tests/src/com/android/volley/utils/ImmediateResponseDelivery.java b/src/test/java/com/android/volley/utils/ImmediateResponseDelivery.java
similarity index 100%
rename from tests/src/com/android/volley/utils/ImmediateResponseDelivery.java
rename to src/test/java/com/android/volley/utils/ImmediateResponseDelivery.java
diff --git a/tests/.classpath b/tests/.classpath
deleted file mode 100644
index 14d7f1b..0000000
--- a/tests/.classpath
+++ /dev/null
@@ -1,10 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<classpath>
-	<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
-	<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
-	<classpathentry combineaccessrules="false" kind="src" path="/Volley"/>
-	<classpathentry kind="src" path="src"/>
-	<classpathentry kind="src" path="gen"/>
-	<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.DEPENDENCIES"/>
-	<classpathentry kind="output" path="bin/classes"/>
-</classpath>
diff --git a/tests/.project b/tests/.project
deleted file mode 100644
index 6214a6d..0000000
--- a/tests/.project
+++ /dev/null
@@ -1,34 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<projectDescription>
-	<name>VolleyTests</name>
-	<comment></comment>
-	<projects>
-        <project>Volley</project>
-	</projects>
-	<buildSpec>
-		<buildCommand>
-			<name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>
-			<arguments>
-			</arguments>
-		</buildCommand>
-		<buildCommand>
-			<name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>
-			<arguments>
-			</arguments>
-		</buildCommand>
-		<buildCommand>
-			<name>org.eclipse.jdt.core.javabuilder</name>
-			<arguments>
-			</arguments>
-		</buildCommand>
-		<buildCommand>
-			<name>com.android.ide.eclipse.adt.ApkBuilder</name>
-			<arguments>
-			</arguments>
-		</buildCommand>
-	</buildSpec>
-	<natures>
-		<nature>com.android.ide.eclipse.adt.AndroidNature</nature>
-		<nature>org.eclipse.jdt.core.javanature</nature>
-	</natures>
-</projectDescription>
diff --git a/tests/Android.mk b/tests/Android.mk
deleted file mode 100644
index fa1bcfb..0000000
--- a/tests/Android.mk
+++ /dev/null
@@ -1,27 +0,0 @@
-# Copyright (C) 2012 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_STATIC_JAVA_LIBRARIES := volley
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
-LOCAL_PACKAGE_NAME := volley_tests
-
-include $(BUILD_PACKAGE)
diff --git a/tests/AndroidManifest.xml b/tests/AndroidManifest.xml
deleted file mode 100644
index bb024f1..0000000
--- a/tests/AndroidManifest.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- -->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.android.volley.tests">
-
-    <uses-sdk android:minSdkVersion="8" />
-
-    <application>
-        <uses-library android:name="android.test.runner" />
-    </application>
-
-    <instrumentation android:name="android.test.InstrumentationTestRunner"
-        android:targetPackage="com.android.volley" />
-
-</manifest>
diff --git a/tests/project.properties b/tests/project.properties
deleted file mode 100644
index f049142..0000000
--- a/tests/project.properties
+++ /dev/null
@@ -1,11 +0,0 @@
-# This file is automatically generated by Android Tools.
-# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
-#
-# This file must be checked in Version Control Systems.
-#
-# To customize properties used by the Ant build system use,
-# "ant.properties", and override values to adapt the script to your
-# project structure.
-
-# Project target.
-target=android-10
diff --git a/tests/res/raw/large_jpeg_1024_500.jpg b/tests/res/raw/large_jpeg_1024_500.jpg
deleted file mode 100644
index e888dbc..0000000
--- a/tests/res/raw/large_jpeg_1024_500.jpg
+++ /dev/null
Binary files differ