From 7fbe1d2472dd88deb938998007a77dc6f5ca74a7 Mon Sep 17 00:00:00 2001 From: Przemek Jakubczyk Date: Mon, 13 Jun 2016 11:39:38 +0200 Subject: [PATCH 1/6] Bump robolectric version to 3.1.\ --- robospock/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/robospock/build.gradle b/robospock/build.gradle index 4fb65ac..d1b09fd 100644 --- a/robospock/build.gradle +++ b/robospock/build.gradle @@ -23,7 +23,7 @@ dependencies { compile "org.spockframework:spock-core:1.0-groovy-2.4" // Android part - compile "org.robolectric:robolectric:3.0" + compile "org.robolectric:robolectric:3.1" provided 'com.intellij:annotations:12.0' provided 'org.robolectric:android-all:5.0.0_r2-robolectric-1' testCompile 'org.robolectric:android-all:5.0.0_r2-robolectric-1' From 9b32a194836b643d7d3e83c45580ad0d2cc84a07 Mon Sep 17 00:00:00 2001 From: Przemek Jakubczyk Date: Mon, 13 Jun 2016 11:39:57 +0200 Subject: [PATCH 2/6] method name is spelled correct :) --- robospock/src/main/java/org/robospock/internal/RoboSputnik.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/robospock/src/main/java/org/robospock/internal/RoboSputnik.java b/robospock/src/main/java/org/robospock/internal/RoboSputnik.java index eb5e140..3d1b051 100644 --- a/robospock/src/main/java/org/robospock/internal/RoboSputnik.java +++ b/robospock/src/main/java/org/robospock/internal/RoboSputnik.java @@ -189,7 +189,7 @@ protected AndroidManifest createAppManifest(FsFile manifestFile, FsFile resDir, public InstrumentationConfiguration createClassLoaderConfig() { return InstrumentationConfiguration.newBuilder() // .doNotAquireClass(ShadowMap.class.getName()) - .doNotAquireClass(DependencyResolver.class.getName()) + .doNotAcquireClass(DependencyResolver.class.getName()) .build(); } From 2ac266189ab24a91480e56a67f849e117c0feb38 Mon Sep 17 00:00:00 2001 From: Przemek Jakubczyk Date: Mon, 13 Jun 2016 11:40:36 +0200 Subject: [PATCH 3/6] Set main thread. As 3.1 source says to do ... --- .../main/java/org/robospock/internal/ParallelUniverseCompat.java | 1 + 1 file changed, 1 insertion(+) diff --git a/robospock/src/main/java/org/robospock/internal/ParallelUniverseCompat.java b/robospock/src/main/java/org/robospock/internal/ParallelUniverseCompat.java index 947b768..f7527f4 100644 --- a/robospock/src/main/java/org/robospock/internal/ParallelUniverseCompat.java +++ b/robospock/src/main/java/org/robospock/internal/ParallelUniverseCompat.java @@ -54,6 +54,7 @@ public class ParallelUniverseCompat implements ParallelUniverseInterface { @Override public void resetStaticState(Config config) { + RuntimeEnvironment.setMainThread(Thread.currentThread()); Robolectric.reset(); if (!loggingInitialized) { From c99a5e4b911064c7ddbddd22fb971f9f2c32d001 Mon Sep 17 00:00:00 2001 From: Przemek Jakubczyk Date: Mon, 13 Jun 2016 11:51:33 +0200 Subject: [PATCH 4/6] Updating ParallelUniverseCompat with Robolectric 3.1 code --- .../internal/ParallelUniverseCompat.java | 84 ++++++++++++++----- 1 file changed, 65 insertions(+), 19 deletions(-) diff --git a/robospock/src/main/java/org/robospock/internal/ParallelUniverseCompat.java b/robospock/src/main/java/org/robospock/internal/ParallelUniverseCompat.java index f7527f4..1084658 100644 --- a/robospock/src/main/java/org/robospock/internal/ParallelUniverseCompat.java +++ b/robospock/src/main/java/org/robospock/internal/ParallelUniverseCompat.java @@ -2,11 +2,15 @@ import android.app.Application; import android.content.Context; +import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; import android.content.res.Configuration; import android.content.res.Resources; +import android.os.Looper; +import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.robolectric.Robolectric; import org.robolectric.RuntimeEnvironment; import org.robolectric.ShadowsAdapter; @@ -15,6 +19,7 @@ import org.robolectric.internal.ParallelUniverseInterface; import org.robolectric.internal.SdkConfig; import org.robolectric.internal.fakes.RoboInstrumentation; +import org.robolectric.manifest.ActivityData; import org.robolectric.manifest.AndroidManifest; import org.robolectric.res.OverlayResourceLoader; import org.robolectric.res.PackageResourceLoader; @@ -23,14 +28,15 @@ import org.robolectric.res.ResourcePath; import org.robolectric.res.RoutingResourceLoader; import org.robolectric.res.builder.DefaultPackageManager; -import org.robolectric.shadows.ShadowActivityThread; -import org.robolectric.shadows.ShadowContextImpl; -import org.robolectric.shadows.ShadowLog; -import org.robolectric.shadows.ShadowResources; +import org.robolectric.res.builder.RobolectricPackageManager; +import org.robolectric.shadows.*; +import org.robolectric.util.ApplicationTestUtil; import org.robolectric.util.Pair; import org.robolectric.util.ReflectionHelpers; +import org.robolectric.util.Scheduler; import java.lang.reflect.Method; +import java.security.Security; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -81,29 +87,47 @@ private String addVersionQualifierToQualifiers(String qualifiers) { @Override public void setUpApplicationState(Method method, TestLifecycle testLifecycle, ResourceLoader systemResourceLoader, AndroidManifest appManifest, Config config) { RuntimeEnvironment.application = null; + RuntimeEnvironment.setMasterScheduler(new Scheduler()); + RuntimeEnvironment.setMainThread(Thread.currentThread()); RuntimeEnvironment.setRobolectricPackageManager(new DefaultPackageManager(shadowsAdapter)); RuntimeEnvironment.getRobolectricPackageManager().addPackage(DEFAULT_PACKAGE_NAME); ResourceLoader resourceLoader; + String packageName; if (appManifest != null) { // robolectric // resourceLoader = robolectricTestRunner.getAppResourceLoader(sdkConfig, systemResourceLoader, appManifest); resourceLoader = getAppResourceLoader(sdkConfig, systemResourceLoader, appManifest); RuntimeEnvironment.getRobolectricPackageManager().addManifest(appManifest, resourceLoader); + packageName = appManifest.getPackageName(); } else { + packageName = config.packageName() != null && !config.packageName().isEmpty() ? config.packageName() : DEFAULT_PACKAGE_NAME; + RuntimeEnvironment.getRobolectricPackageManager().addPackage(packageName); resourceLoader = systemResourceLoader; } - ShadowResources.setSystemResources(systemResourceLoader); + RuntimeEnvironment.setSystemResourceLoader(systemResourceLoader); + RuntimeEnvironment.setAppResourceLoader(resourceLoader); + + if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) { + Security.insertProviderAt(new BouncyCastleProvider(), 1); + } + String qualifiers = addVersionQualifierToQualifiers(config.qualifiers()); Resources systemResources = Resources.getSystem(); Configuration configuration = systemResources.getConfiguration(); shadowsAdapter.overrideQualifiers(configuration, qualifiers); systemResources.updateConfiguration(configuration, systemResources.getDisplayMetrics()); RuntimeEnvironment.setQualifiers(qualifiers); + RuntimeEnvironment.setApiLevel(sdkConfig.getApiLevel()); - Class contextImplClass = ReflectionHelpers.loadClass(getClass().getClassLoader(), ShadowContextImpl.CLASS_NAME); + Class contextImplClass = ReflectionHelpers.loadClass(getClass().getClassLoader(), shadowsAdapter.getShadowContextImplClassName()); - Class activityThreadClass = ReflectionHelpers.loadClass(getClass().getClassLoader(), ShadowActivityThread.CLASS_NAME); + Class activityThreadClass = ReflectionHelpers.loadClass(getClass().getClassLoader(), shadowsAdapter.getShadowActivityThreadClassName()); + // Looper needs to be prepared before the activity thread is created + if (Looper.myLooper() == null) { + Looper.prepareMainLooper(); + } + ShadowLooper.getShadowMainLooper().resetScheduler(); Object activityThread = ReflectionHelpers.newInstance(activityThreadClass); RuntimeEnvironment.setActivityThread(activityThread); @@ -113,9 +137,10 @@ public void setUpApplicationState(Method method, TestLifecycle testLifecycle, Re Context systemContextImpl = ReflectionHelpers.callStaticMethod(contextImplClass, "createSystemContext", ClassParameter.from(activityThreadClass, activityThread)); final Application application = (Application) testLifecycle.createApplication(method, appManifest, config); + RuntimeEnvironment.application = application; + if (application != null) { - String packageName = appManifest != null ? appManifest.getPackageName() : null; - if (packageName == null) packageName = DEFAULT_PACKAGE_NAME; + shadowsAdapter.bind(application, appManifest); ApplicationInfo applicationInfo; try { @@ -131,29 +156,50 @@ public void setUpApplicationState(Method method, TestLifecycle testLifecycle, Re ClassParameter.from(compatibilityInfoClass, null), ClassParameter.from(int.class, Context.CONTEXT_INCLUDE_CODE)); - shadowsAdapter.bind(application, appManifest, resourceLoader); - if (appManifest == null) { - // todo: make this cleaner... - shadowsAdapter.setPackageName(application, applicationInfo.packageName); - } - Resources appResources = application.getResources(); - ReflectionHelpers.setField(loadedApk, "mResources", appResources); try { Context contextImpl = systemContextImpl.createPackageContext(applicationInfo.packageName, Context.CONTEXT_INCLUDE_CODE); ReflectionHelpers.setField(activityThreadClass, activityThread, "mInitialApplication", application); - ReflectionHelpers.callInstanceMethod(Application.class, application, "attach", ReflectionHelpers.ClassParameter.from(Context.class, contextImpl)); + ApplicationTestUtil.attach(application, contextImpl); } catch (PackageManager.NameNotFoundException e) { throw new RuntimeException(e); } + addManifestActivitiesToPackageManager(appManifest, application); + + Resources appResources = application.getResources(); + ReflectionHelpers.setField(loadedApk, "mResources", appResources); + ReflectionHelpers.setField(loadedApk, "mApplication", application); + appResources.updateConfiguration(configuration, appResources.getDisplayMetrics()); - shadowsAdapter.setAssetsQualifiers(appResources.getAssets(), qualifiers); - RuntimeEnvironment.application = application; application.onCreate(); } } + private void addManifestActivitiesToPackageManager(AndroidManifest appManifest, Application application) { + if (appManifest != null) { + Map activityDatas = appManifest.getActivityDatas(); + + RobolectricPackageManager packageManager = (RobolectricPackageManager) application.getPackageManager(); + + for (ActivityData data : activityDatas.values()) { + String name = data.getName(); + String activityName = name.startsWith(".") ? appManifest.getPackageName() + name : name; + packageManager.addResolveInfoForIntent(new Intent(activityName), new ResolveInfo()); + } + } + } + + @Override + public Thread getMainThread() { + return RuntimeEnvironment.getMainThread(); + } + + @Override + public void setMainThread(Thread newMainThread) { + RuntimeEnvironment.setMainThread(newMainThread); + } + @Override public void tearDownApplication() { if (RuntimeEnvironment.application != null) { From c05987b92ef496fb2526f85f87ba9e0721f88a53 Mon Sep 17 00:00:00 2001 From: Przemek Jakubczyk Date: Mon, 13 Jun 2016 13:22:15 +0200 Subject: [PATCH 5/6] Update sample project to use latest Android. Removed depedency from robolectric --- gradle/wrapper/gradle-wrapper.properties | 2 +- sample/build.gradle | 13 ++++--- .../example/robospock/web/WebInterface.java | 6 ++-- .../robospock/web/WebInterfaceImpl.java | 36 +++++++++---------- 4 files changed, 29 insertions(+), 28 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index c2139c6..fac2e41 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.6-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-2.13-all.zip diff --git a/sample/build.gradle b/sample/build.gradle index cb78ab8..d68de99 100644 --- a/sample/build.gradle +++ b/sample/build.gradle @@ -1,11 +1,14 @@ buildscript { repositories { jcenter() + maven { + url "https://jitpack.io" + } } dependencies { - classpath 'com.android.tools.build:gradle:1.2.3' - classpath 'org.codehaus.groovy:gradle-groovy-android-plugin:0.3.6' - classpath 'com.jakewharton.sdkmanager:gradle-plugin:0.12.0' + classpath 'com.android.tools.build:gradle:2.1.0' + classpath 'org.codehaus.groovy:gradle-groovy-android-plugin:0.3.10' + classpath "com.github.JakeWharton:sdk-manager-plugin:e05218601b1274ea0721e13b33a426f641156f69" } } @@ -20,8 +23,8 @@ repositories { } android { - compileSdkVersion 22 - buildToolsVersion "22.0.1" + compileSdkVersion 23 + buildToolsVersion "23.0.3" defaultConfig { versionCode 1 diff --git a/sample/src/main/java/com/example/robospock/web/WebInterface.java b/sample/src/main/java/com/example/robospock/web/WebInterface.java index c81be6d..e23abc9 100644 --- a/sample/src/main/java/com/example/robospock/web/WebInterface.java +++ b/sample/src/main/java/com/example/robospock/web/WebInterface.java @@ -1,12 +1,10 @@ package com.example.robospock.web; -import org.apache.http.client.ClientProtocolException; - import java.io.File; import java.io.IOException; public interface WebInterface { - String execute(String resource) throws IllegalStateException, ClientProtocolException, IOException; + String execute(String resource) throws IllegalStateException, IOException; - File downloadFile(String resource, String path) throws ClientProtocolException, IOException; + File downloadFile(String resource, String path) throws IOException; } diff --git a/sample/src/main/java/com/example/robospock/web/WebInterfaceImpl.java b/sample/src/main/java/com/example/robospock/web/WebInterfaceImpl.java index e585235..72afdaa 100644 --- a/sample/src/main/java/com/example/robospock/web/WebInterfaceImpl.java +++ b/sample/src/main/java/com/example/robospock/web/WebInterfaceImpl.java @@ -1,29 +1,18 @@ package com.example.robospock.web; -import org.apache.http.client.ClientProtocolException; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.impl.client.DefaultHttpClient; - -import java.io.BufferedOutputStream; -import java.io.BufferedReader; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.Reader; -import java.io.StringWriter; -import java.io.Writer; +import java.io.*; +import java.net.HttpURLConnection; +import java.net.URL; public class WebInterfaceImpl implements WebInterface { @Override - public String execute(final String resource) throws IllegalStateException, ClientProtocolException, IOException { + public String execute(final String resource) throws IllegalStateException, IOException { final InputStream content = getStream(resource); return convertStreamToString(content); } @Override - public File downloadFile(final String resource, final String path) throws ClientProtocolException, IOException { + public File downloadFile(final String resource, final String path) throws IOException { final InputStream content = getStream(resource); final File file = new File(path); saveStreamToFile(content, file); @@ -31,8 +20,19 @@ public File downloadFile(final String resource, final String path) throws Client } - private InputStream getStream(final String resource) throws IOException, ClientProtocolException { - return new DefaultHttpClient().execute(new HttpGet(resource)).getEntity().getContent(); + private InputStream getStream(final String resource) throws IOException { + URL url = new URL(resource); + HttpURLConnection connection = null; + connection = (HttpURLConnection) url.openConnection(); + connection.connect(); + + // expect HTTP 200 OK, so we don't mistakenly save error report + // instead of the file + if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) { + throw new IOException("Server returned HTTP " + connection.getResponseCode() + + " " + connection.getResponseMessage()); + } + return connection.getInputStream(); } public static String convertStreamToString(final InputStream is) throws IOException { From 86bfe20ec2806d0b2444b3cee3f1a486386865dc Mon Sep 17 00:00:00 2001 From: Przemek Jakubczyk Date: Mon, 13 Jun 2016 13:22:31 +0200 Subject: [PATCH 6/6] Bump version to 1.0.1-SNAPSHOT --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 260496a..2c5fdb0 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ -version=1.0.0 +version=1.0.1-SNAPSHOT artifactId=robospock group=org.robospock