diff options
author | Christian Williams & Dimitris Couchell <github+xian+dimitris@squareup.com> | 2012-11-16 13:26:26 -0800 |
---|---|---|
committer | Christian Williams & Dimitris Couchell <github+xian+dimitris@squareup.com> | 2012-11-16 13:26:26 -0800 |
commit | 9e11c64f01e0d72a3f469d5866b695c778387c26 (patch) | |
tree | 55ac8eab369bbdc4c044ed094e807d7872ab1677 | |
parent | 75038b993ec0db85953f392a0c129648212d9ad6 (diff) | |
parent | 809d8cbbad66c0e51847c681f0e04526929f99b8 (diff) | |
download | robolectric-shadows-9e11c64f01e0d72a3f469d5866b695c778387c26.tar.gz |
Merge remote-tracking branch 'square/master' into robolectric-2-master
* square/master: (152 commits)
Reverted pom to r2 since our build server can't handle r3
Implement ShadowBitmapDrawable.mutate()
Attempt to be DRY about SDK. Ha.
And update the manifest to API 16.
Update resource loading to use API 16.
'snapshots' -> 'url'
Update to a Maps dependnecy from this decade.
Allow for specifying a custom nexus URL via profile.
Allow system features to be toggled. Fix Fragment resume state tracking.
Return drawables when setting CompoundDrawables with resourceIds
Include text values in TextView dump. Implement TextUtils.htmlEncode.
Implement CheckedTestView toggling etc.
Call View#onAttachedToWindow, View#onDetachedFromWindow, Fragment#onAttach, and Fragment#onDetach as appropriate.
Add Robolectric.dump() and Robolectric.innerText() convenience methods. Include visibility attribute in view dump.
Implement TypedArray#getTextArray.
Provide a way to inject a Resources object on resources-related shadows (implement UsesResources).
Support enabling bluetooth
Add getOnFocusChangeListener to View
When popping a fragment from the back stack, restore the previoius fragment.
Implement Bundle.set/get*Array.
...
Conflicts:
.gitignore
.pairs
README.md
build.xml
find-android.bat
lib/main/junit-dep-4.8.2.jar
pom.xml
src/main/java/android/os/TestVibrator.java
src/main/java/android/webkit/TestWebSettings.java
src/main/java/com/xtremelabs/robolectric/Robolectric.java
src/main/java/com/xtremelabs/robolectric/RobolectricConfig.java
src/main/java/com/xtremelabs/robolectric/RobolectricTestRunner.java
src/main/java/com/xtremelabs/robolectric/res/ColorResourceLoader.java
src/main/java/com/xtremelabs/robolectric/res/DrawableResourceLoader.java
src/main/java/com/xtremelabs/robolectric/res/IntegerResourceLoader.java
src/main/java/com/xtremelabs/robolectric/res/ResourceLoader.java
src/main/java/com/xtremelabs/robolectric/res/RobolectricPackageManager.java
src/main/java/com/xtremelabs/robolectric/res/ViewLoader.java
src/main/java/com/xtremelabs/robolectric/shadows/ShadowActivity.java
src/main/java/com/xtremelabs/robolectric/shadows/ShadowAlertDialog.java
src/main/java/com/xtremelabs/robolectric/shadows/ShadowApplication.java
src/main/java/com/xtremelabs/robolectric/shadows/ShadowBitmapDrawable.java
src/main/java/com/xtremelabs/robolectric/shadows/ShadowBitmapFactory.java
src/main/java/com/xtremelabs/robolectric/shadows/ShadowBluetoothAdapter.java
src/main/java/com/xtremelabs/robolectric/shadows/ShadowBundle.java
src/main/java/com/xtremelabs/robolectric/shadows/ShadowCanvas.java
src/main/java/com/xtremelabs/robolectric/shadows/ShadowCheckedTextView.java
src/main/java/com/xtremelabs/robolectric/shadows/ShadowContentResolver.java
src/main/java/com/xtremelabs/robolectric/shadows/ShadowContext.java
src/main/java/com/xtremelabs/robolectric/shadows/ShadowContextWrapper.java
src/main/java/com/xtremelabs/robolectric/shadows/ShadowDialog.java
src/main/java/com/xtremelabs/robolectric/shadows/ShadowDialogFragment.java
src/main/java/com/xtremelabs/robolectric/shadows/ShadowEditText.java
src/main/java/com/xtremelabs/robolectric/shadows/ShadowFragment.java
src/main/java/com/xtremelabs/robolectric/shadows/ShadowFragmentActivity.java
src/main/java/com/xtremelabs/robolectric/shadows/ShadowFrameLayout.java
src/main/java/com/xtremelabs/robolectric/shadows/ShadowHtml.java
src/main/java/com/xtremelabs/robolectric/shadows/ShadowImageView.java
src/main/java/com/xtremelabs/robolectric/shadows/ShadowIntent.java
src/main/java/com/xtremelabs/robolectric/shadows/ShadowLinearLayout.java
src/main/java/com/xtremelabs/robolectric/shadows/ShadowListActivity.java
src/main/java/com/xtremelabs/robolectric/shadows/ShadowLocation.java
src/main/java/com/xtremelabs/robolectric/shadows/ShadowLocationManager.java
src/main/java/com/xtremelabs/robolectric/shadows/ShadowRect.java
src/main/java/com/xtremelabs/robolectric/shadows/ShadowResources.java
src/main/java/com/xtremelabs/robolectric/shadows/ShadowSQLiteCloseable.java
src/main/java/com/xtremelabs/robolectric/shadows/ShadowSensorManager.java
src/main/java/com/xtremelabs/robolectric/shadows/ShadowSettings.java
src/main/java/com/xtremelabs/robolectric/shadows/ShadowTextUtils.java
src/main/java/com/xtremelabs/robolectric/shadows/ShadowTextView.java
src/main/java/com/xtremelabs/robolectric/shadows/ShadowTypedArray.java
src/main/java/com/xtremelabs/robolectric/shadows/ShadowView.java
src/main/java/com/xtremelabs/robolectric/shadows/ShadowViewAnimator.java
src/main/java/com/xtremelabs/robolectric/shadows/ShadowViewGroup.java
src/main/java/com/xtremelabs/robolectric/tester/android/util/TestAttributeSet.java
src/main/java/com/xtremelabs/robolectric/tester/android/view/TestMenuItem.java
src/main/java/com/xtremelabs/robolectric/tester/org/apache/http/FakeHttpLayer.java
src/test/java/android/webkit/TestWebSettingsTest.java
src/test/java/com/xtremelabs/robolectric/R.java
src/test/java/com/xtremelabs/robolectric/RobolectricConfigTest.java
src/test/java/com/xtremelabs/robolectric/res/DrawableResourceLoaderTest.java
src/test/java/com/xtremelabs/robolectric/res/IntegerResourceLoaderTest.java
src/test/java/com/xtremelabs/robolectric/res/MenuResourceLoaderTest.java
src/test/java/com/xtremelabs/robolectric/res/RawResourceLoaderTest.java
src/test/java/com/xtremelabs/robolectric/res/ResourceLoaderTest.java
src/test/java/com/xtremelabs/robolectric/res/TestAttributeSetTest.java
src/test/java/com/xtremelabs/robolectric/res/ViewLoaderTest.java
src/test/java/com/xtremelabs/robolectric/shadows/ActivityTest.java
src/test/java/com/xtremelabs/robolectric/shadows/BitmapFactoryTest.java
src/test/java/com/xtremelabs/robolectric/shadows/BluetoothAdapterTest.java
src/test/java/com/xtremelabs/robolectric/shadows/CheckedTextViewTest.java
src/test/java/com/xtremelabs/robolectric/shadows/ContextTest.java
src/test/java/com/xtremelabs/robolectric/shadows/DefaultRequestDirectorTest.java
src/test/java/com/xtremelabs/robolectric/shadows/DialogFragmentTest.java
src/test/java/com/xtremelabs/robolectric/shadows/DialogTest.java
src/test/java/com/xtremelabs/robolectric/shadows/EditTextTest.java
src/test/java/com/xtremelabs/robolectric/shadows/FragmentTest.java
src/test/java/com/xtremelabs/robolectric/shadows/FrameLayoutTest.java
src/test/java/com/xtremelabs/robolectric/shadows/HtmlTest.java
src/test/java/com/xtremelabs/robolectric/shadows/LocationTest.java
src/test/java/com/xtremelabs/robolectric/shadows/PreferenceActivityTest.java
src/test/java/com/xtremelabs/robolectric/shadows/ResourcesTest.java
src/test/java/com/xtremelabs/robolectric/shadows/SettingsTest.java
src/test/java/com/xtremelabs/robolectric/shadows/SpannableStringBuilderTest.java
src/test/java/com/xtremelabs/robolectric/shadows/TextUtilsTest.java
src/test/java/com/xtremelabs/robolectric/shadows/TextViewTest.java
src/test/java/com/xtremelabs/robolectric/shadows/TypedArrayTest.java
src/test/java/com/xtremelabs/robolectric/shadows/ViewAnimatorTest.java
src/test/java/com/xtremelabs/robolectric/shadows/ViewGroupTest.java
src/test/java/com/xtremelabs/robolectric/shadows/ViewTest.java
src/test/java/com/xtremelabs/robolectric/shadows/testing/OnMethodTestActivity.java
src/test/java/com/xtremelabs/robolectric/tester/android/view/TestWindowTest.java
src/test/java/com/xtremelabs/robolectric/util/TestUtil.java
145 files changed, 4502 insertions, 1087 deletions
diff --git a/.gitignore b/.gitignore index eec04e897..c70a1f0a3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,19 +1,37 @@ +# Eclipse +.classpath +.project +.settings +eclipsebin + +# Ant bin/ +gen/ +build/ out/ +lib/ + +# Maven +target +pom.xml.* +release.properties + +# IntelliJ .idea -robolectric.iml -tmp/ -target/ -pages -.DS_Store -local.properties -.project -.classpath -.settings -*.iws *.iml +*.iws *.ipr +classes + +# Other editors *.orig *.swp *~ \#*\# + +# Mac +.DS_Store + +tmp +local.properties +pages @@ -23,5 +23,3 @@ cd maven-android-sdk-deployer/ maven install -P <ANDROID_VERSION> ``` - - @@ -84,21 +84,20 @@ <groupId>com.google.android</groupId> <artifactId>android</artifactId> <version>4.1.1.4</version> - <!-- <version>4.0.1.2</version> --> <scope>provided</scope> </dependency> <dependency> <groupId>com.google.android.maps</groupId> <artifactId>maps</artifactId> - <version>10_r2</version> + <version>16_r2</version> <scope>provided</scope> </dependency> <dependency> <groupId>com.google.android</groupId> <artifactId>support-v4</artifactId> - <version>r6</version> + <version>r7</version> </dependency> <dependency> diff --git a/src/main/java/android/os/TestVibrator.java b/src/main/java/android/os/TestVibrator.java index ad3326d2a..26cedb618 100644 --- a/src/main/java/android/os/TestVibrator.java +++ b/src/main/java/android/os/TestVibrator.java @@ -4,23 +4,19 @@ public class TestVibrator extends Vibrator { @Override public void cancel() { - // TODO Auto-generated method stub } @Override public boolean hasVibrator() { - // TODO Auto-generated method stub return false; } @Override public void vibrate(long arg0) { - // TODO Auto-generated method stub } @Override public void vibrate(long[] arg0, int arg1) { - // TODO Auto-generated method stub } } diff --git a/src/main/java/android/webkit/TestWebSettings.java b/src/main/java/android/webkit/TestWebSettings.java index 660c53cea..0df72c6f8 100644 --- a/src/main/java/android/webkit/TestWebSettings.java +++ b/src/main/java/android/webkit/TestWebSettings.java @@ -26,24 +26,24 @@ public class TestWebSettings extends WebSettings { private boolean supportMultipleWindows = false; private boolean supportZoom = true; - public TestWebSettings() { - } - - public boolean getAllowFileAccessFromFileURLs() { - return allowFileAccessFromFile; - } - - public boolean getAllowUniversalAccessFromFileURLs() { - return allowUniversalAccessFromFile; - } - - public void setAllowFileAccessFromFileURLs(boolean allow) { - allowFileAccessFromFile = allow; - } - - public void setAllowUniversalAccessFromFileURLs(boolean allow) { - allowUniversalAccessFromFile = allow; - } + public TestWebSettings() { + } + + public boolean getAllowFileAccessFromFileURLs() { + return allowFileAccessFromFile; + } + + public boolean getAllowUniversalAccessFromFileURLs() { + return allowUniversalAccessFromFile; + } + + public void setAllowFileAccessFromFileURLs(boolean allow) { + allowFileAccessFromFile = allow; + } + + public void setAllowUniversalAccessFromFileURLs(boolean allow) { + allowUniversalAccessFromFile = allow; + } @Implementation public boolean getAllowFileAccess() { diff --git a/src/main/java/com/xtremelabs/robolectric/Robolectric.java b/src/main/java/com/xtremelabs/robolectric/Robolectric.java index 76338d192..b71b2940d 100644 --- a/src/main/java/com/xtremelabs/robolectric/Robolectric.java +++ b/src/main/java/com/xtremelabs/robolectric/Robolectric.java @@ -20,6 +20,7 @@ import android.hardware.Camera; import android.hardware.SensorManager; import android.location.Address; import android.location.Geocoder; +import android.location.Location; import android.location.LocationManager; import android.media.AudioManager; import android.media.MediaPlayer; @@ -165,6 +166,7 @@ public class Robolectric { return Arrays.asList( ShadowAbsListView.class, ShadowAbsoluteLayout.class, + ShadowAbsoluteLayout.ShadowLayoutParams.class, ShadowAbsSeekBar.class, ShadowAbsSpinner.class, ShadowAbstractCursor.class, @@ -243,6 +245,9 @@ public class Robolectric { ShadowDialog.class, ShadowDialogFragment.class, ShadowDialogPreference.class, + ShadowDownloadManager.class, + ShadowDownloadManager.ShadowRequest.class, + ShadowDownloadManager.ShadowQuery.class, ShadowEditText.class, ShadowEditTextPreference.class, ShadowEnvironment.class, @@ -261,6 +266,7 @@ public class Robolectric { ShadowHandler.class, ShadowHandlerThread.class, ShadowHtml.class, + ShadowImageButton.class, ShadowImageView.class, ShadowInputMethodManager.class, ShadowInputDevice.class, @@ -284,6 +290,7 @@ public class Robolectric { ShadowListActivity.class, ShadowListPreference.class, ShadowListView.class, + ShadowLocalActivityManager.class, ShadowLocalBroadcastManager.class, ShadowLocation.class, ShadowLocationManager.class, @@ -295,6 +302,7 @@ public class Robolectric { ShadowMarginLayoutParams.class, ShadowMatrix.class, ShadowMatrixCursor.class, + ShadowMeasureSpec.class, ShadowMediaPlayer.class, ShadowMediaRecorder.class, ShadowMediaStore.ShadowImages.ShadowMedia.class, @@ -338,6 +346,7 @@ public class Robolectric { ShadowRadioGroup.class, ShadowRatingBar.class, ShadowRect.class, + ShadowRectF.class, ShadowRelativeLayout.class, ShadowRelativeLayoutParams.class, ShadowResolveInfo.class, @@ -362,6 +371,7 @@ public class Robolectric { ShadowSmsManager.class, ShadowSpannableString.class, ShadowSpannableStringBuilder.class, + ShadowSpannedString.class, ShadowSparseArray.class, ShadowSparseBooleanArray.class, ShadowSparseIntArray.class, @@ -687,6 +697,10 @@ public class Robolectric { return (ShadowExpandableListView) shadowOf_(instance); } + public static ShadowLocation shadowOf(Location instance) { + return (ShadowLocation) shadowOf_(instance); + } + public static ShadowFilter shadowOf(Filter instance) { return (ShadowFilter) shadowOf_(instance); } @@ -1040,6 +1054,10 @@ public class Robolectric { return (ShadowTextView) shadowOf_(instance); } + public static ShadowResources.ShadowTheme shadowOf(Resources.Theme instance) { + return (ShadowResources.ShadowTheme) shadowOf_(instance); + } + public static ShadowToast shadowOf(Toast instance) { return (ShadowToast) shadowOf_(instance); } @@ -1198,7 +1216,7 @@ public class Robolectric { * @return the requested request. */ public static HttpRequest getSentHttpRequest(int index) { - return ShadowDefaultRequestDirector.getSentHttpRequest(index); + return getFakeHttpLayer().getSentHttpRequestInfo(index).getHttpRequest(); } public static HttpRequest getLatestSentHttpRequest() { @@ -1225,7 +1243,26 @@ public class Robolectric { * @return the requested request metadata. */ public static HttpRequestInfo getSentHttpRequestInfo(int index) { - return ShadowDefaultRequestDirector.getSentHttpRequestInfo(index); + return getFakeHttpLayer().getSentHttpRequestInfo(index); + } + + /** + * Accessor to obtain HTTP requests made during the current test in the order in which they were made. + * + * @return the requested request or null if there are none. + */ + public static HttpRequest getNextSentHttpRequest() { + HttpRequestInfo httpRequestInfo = getFakeHttpLayer().getNextSentHttpRequestInfo(); + return httpRequestInfo == null ? null : httpRequestInfo.getHttpRequest(); + } + + /** + * Accessor to obtain metadata for an HTTP request made during the current test in the order in which they were made. + * + * @return the requested request metadata or null if there are none. + */ + public static HttpRequestInfo getNextSentHttpRequestInfo() { + return getFakeHttpLayer().getNextSentHttpRequestInfo(); } /** @@ -1442,5 +1479,4 @@ public class Robolectric { return oldValue; } } - } diff --git a/src/main/java/com/xtremelabs/robolectric/RobolectricConfig.java b/src/main/java/com/xtremelabs/robolectric/RobolectricConfig.java index 31ab3d2c6..565642c3c 100644 --- a/src/main/java/com/xtremelabs/robolectric/RobolectricConfig.java +++ b/src/main/java/com/xtremelabs/robolectric/RobolectricConfig.java @@ -9,10 +9,13 @@ import org.w3c.dom.NodeList; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import java.io.File; +import java.io.FileInputStream; import java.io.FileNotFoundException; +import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Properties; import static android.content.pm.ApplicationInfo.*; import static com.xtremelabs.robolectric.Robolectric.DEFAULT_SDK_VERSION; @@ -69,11 +72,58 @@ public class RobolectricConfig { } public static RobolectricConfig fromBaseDirWithLibraries(File baseDir) { - return new RobolectricConfig(baseDir); - // todo: -// List<File> resources = new ArrayList<File>(); -// buildResourcePath(baseDir, resources); -// return new RobolectricConfig(new File(baseDir, "AndroidManifest.xml"), resources, new File(baseDir, "assets")); + List<File> resources = new ArrayList<File>(); + buildResourcePath(baseDir, resources); + return new RobolectricConfig(new File(baseDir, "AndroidManifest.xml"), resources, new File(baseDir, "assets")); + } + + private static void buildResourcePath(File baseDir, List<File> resources) { + resources.add(new File(baseDir, "res")); + + Properties properties = getProperties(new File(baseDir, "project.properties")); + if (properties != null) { + int libRef = 1; + String lib; + while ((lib = properties.getProperty("android.library.reference." + libRef)) != null) { + buildResourcePath(new File(baseDir, lib), resources); + libRef++; + } + return; + } else { + // Try unpack folder from maven. + File unpack = new File(baseDir, "target/unpack/apklibs"); + if (unpack.exists()) { + File[] libs = unpack.listFiles(); + if (libs != null) { + for (File lib : libs) { + resources.add(new File(lib, "res")); + } + } + } + } + } + + private static Properties getProperties(File propertiesFile) { + if (!propertiesFile.exists()) return null; + + Properties properties = new Properties(); + FileInputStream stream; + try { + stream = new FileInputStream(propertiesFile); + } catch (FileNotFoundException e) { + throw new RuntimeException(e); + } + + try { + try { + properties.load(stream); + } finally { + stream.close(); + } + } catch (IOException e) { + throw new RuntimeException(e); + } + return properties; } public String getRClassName() throws Exception { @@ -86,8 +136,10 @@ public class RobolectricConfig { throw new FileNotFoundException(androidManifestFile.getAbsolutePath() + " not found or not a file; it should point to your project's AndroidManifest.xml"); } - if (!getResourceDirectory().exists() || !getResourceDirectory().isDirectory()) { - throw new FileNotFoundException(getResourceDirectory().getAbsolutePath() + " not found or not a directory; it should point to your project's res directory"); + for (File f : getResourcePath()) { + if (!f.exists() || !f.isDirectory()) { + throw new FileNotFoundException(f.getAbsolutePath() + " not found or not a directory; it should point to a res directory"); + } } } @@ -120,7 +172,7 @@ public class RobolectricConfig { processName = getTagAttributeText(manifestDocument, "application", "android:process"); if (processName == null) { - processName = packageName; + processName = packageName; } parseApplicationFlags(manifestDocument); @@ -187,14 +239,14 @@ public class RobolectricConfig { } private int getApplicationFlag(final Document doc, final String attribute, final int attributeValue) { - String flagString = getTagAttributeText(doc, "application", attribute); - return "true".equalsIgnoreCase(flagString) ? attributeValue : 0; + String flagString = getTagAttributeText(doc, "application", attribute); + return "true".equalsIgnoreCase(flagString) ? attributeValue : 0; } - + private Integer getTagAttributeIntValue(final Document doc, final String tag, final String attribute) { return getTagAttributeIntValue(doc, tag, attribute, null); } - + private Integer getTagAttributeIntValue(final Document doc, final String tag, final String attribute, final Integer defaultValue) { String valueString = getTagAttributeText(doc, tag, attribute); if (valueString != null) { @@ -212,11 +264,11 @@ public class RobolectricConfig { parseAndroidManifest(); return packageName; } - + public int getMinSdkVersion() { - parseAndroidManifest(); - return minSdkVersion; - } + parseAndroidManifest(); + return minSdkVersion; + } public int getSdkVersion() { parseAndroidManifest(); @@ -224,8 +276,8 @@ public class RobolectricConfig { } public int getApplicationFlags() { - parseAndroidManifest(); - return applicationFlags; + parseAndroidManifest(); + return applicationFlags; } public String getProcessName() { @@ -262,11 +314,11 @@ public class RobolectricConfig { } public boolean getStrictI18n() { - return strictI18n; + return strictI18n; } - + public void setStrictI18n(boolean strict) { - strictI18n = strict; + strictI18n = strict; } public void setValuesResQualifiers( String qualifiers ){ @@ -293,7 +345,7 @@ public class RobolectricConfig { } return null; } - + private static Application newApplicationInstance(final String packageName, final String applicationName) { Application application; try { @@ -323,7 +375,8 @@ public class RobolectricConfig { if (getAssetsDirectory() != null ? !getAssetsDirectory().equals(that.getAssetsDirectory()) : that.getAssetsDirectory() != null) { return false; } - if (getResourceDirectory() != null ? !getResourceDirectory().equals(that.getResourceDirectory()) : that.getResourceDirectory() != null) { + + if (getResourcePath() != null ? !getResourcePath().equals(that.getResourcePath()) : that.getResourcePath() != null) { return false; } @@ -333,11 +386,11 @@ public class RobolectricConfig { @Override public int hashCode() { int result = androidManifestFile != null ? androidManifestFile.hashCode() : 0; - result = 31 * result + (getResourceDirectory() != null ? getResourceDirectory().hashCode() : 0); + result = 31 * result + (getResourcePath() != null ? getResourcePath().hashCode() : 0); result = 31 * result + (getAssetsDirectory() != null ? getAssetsDirectory().hashCode() : 0); return result; } - + public int getRealSdkVersion() { parseAndroidManifest(); if (sdkVersionSpecified) { diff --git a/src/main/java/com/xtremelabs/robolectric/bytecode/ShadowWrangler.java b/src/main/java/com/xtremelabs/robolectric/bytecode/ShadowWrangler.java index e74e27ab8..b75b957a8 100644 --- a/src/main/java/com/xtremelabs/robolectric/bytecode/ShadowWrangler.java +++ b/src/main/java/com/xtremelabs/robolectric/bytecode/ShadowWrangler.java @@ -8,7 +8,6 @@ import javassist.CannotCompileException; import javassist.CtClass; import javassist.CtField; import javassist.NotFoundException; - import java.lang.annotation.Annotation; import java.lang.reflect.*; import java.util.*; diff --git a/src/main/java/com/xtremelabs/robolectric/matchers/ViewVisibilityMatcher.java b/src/main/java/com/xtremelabs/robolectric/matchers/ViewVisibilityMatcher.java new file mode 100644 index 000000000..ef7db47f8 --- /dev/null +++ b/src/main/java/com/xtremelabs/robolectric/matchers/ViewVisibilityMatcher.java @@ -0,0 +1,63 @@ +package com.xtremelabs.robolectric.matchers; + +import android.view.View; +import org.hamcrest.Description; +import org.hamcrest.Factory; +import org.hamcrest.Matcher; +import org.junit.internal.matchers.TypeSafeMatcher; + +import java.util.HashMap; +import java.util.Map; + +public class ViewVisibilityMatcher<T extends View> extends TypeSafeMatcher<T> { + + private static final Map<Integer, String> VISIBILITY_DESCRIPTIONS; + static { + VISIBILITY_DESCRIPTIONS = new HashMap<Integer, String>(); + VISIBILITY_DESCRIPTIONS.put(View.VISIBLE, "'Visible'"); + VISIBILITY_DESCRIPTIONS.put(View.INVISIBLE, "'Invisible'"); + VISIBILITY_DESCRIPTIONS.put(View.GONE, "'Gone'"); + } + + private final int expectedVisibility; + private int actualVisibility = -1; + + public ViewVisibilityMatcher(int expectedVisibility) { + this.expectedVisibility = expectedVisibility; + } + + @Override + public boolean matchesSafely(T t) { + if (t == null){ + return false; + } + actualVisibility = t.getVisibility(); + return expectedVisibility == actualVisibility; + } + + @Override + public void describeTo(Description description) { + if (actualVisibility >= 0){ + description.appendText(VISIBILITY_DESCRIPTIONS.get(actualVisibility)); + description.appendText(" to be "); + description.appendText(VISIBILITY_DESCRIPTIONS.get(expectedVisibility)); + } else { + description.appendText("View to be non-null."); + } + } + + @Factory + public static <T extends View> Matcher<T> isVisible() { + return new ViewVisibilityMatcher<T>(View.VISIBLE); + } + + @Factory + public static <T extends View> Matcher<T> isInvisible() { + return new ViewVisibilityMatcher<T>(View.INVISIBLE); + } + + @Factory + public static <T extends View> Matcher<T> isGone() { + return new ViewVisibilityMatcher<T>(View.GONE); + } +} diff --git a/src/main/java/com/xtremelabs/robolectric/res/AttrResourceLoader.java b/src/main/java/com/xtremelabs/robolectric/res/AttrResourceLoader.java index 1ec1dd436..790729d4a 100644 --- a/src/main/java/com/xtremelabs/robolectric/res/AttrResourceLoader.java +++ b/src/main/java/com/xtremelabs/robolectric/res/AttrResourceLoader.java @@ -7,6 +7,7 @@ import org.w3c.dom.NodeList; import javax.xml.xpath.XPathConstants; import javax.xml.xpath.XPathExpression; +import javax.xml.xpath.XPathExpressionException; import javax.xml.xpath.XPathFactory; import java.io.File; import java.util.HashMap; @@ -15,59 +16,143 @@ import java.util.Map; import java.util.Set; public class AttrResourceLoader extends XmlLoader { - Map<String, String> classAttrEnumToValue = new HashMap<String, String>(); - Set<String> knownClassAttrs = new HashSet<String>(); + private static final String ANDROID_XML_NAMESPACE = "http://schemas.android.com/apk/res/android"; + private static final String ANDROID_PREFIX = "android:"; + Map<String, EnumDef> enums = new HashMap<String, EnumDef>(); + Map<String, EnumRef> enumRefs = new HashMap<String, EnumRef>(); + boolean resolved = false; + + Map<String, String> classEnumToValue = new HashMap<String, String>(); + Set<String> knownClassEnums = new HashSet<String>(); + public AttrResourceLoader(ResourceExtractor resourceExtractor) { super(resourceExtractor); } - @Override - protected void processResourceXml(File xmlFile, Document document, boolean isSystem) throws Exception { - XPathExpression stringsXPath = XPathFactory.newInstance().newXPath().compile("/resources/declare-styleable/attr/enum"); - NodeList stringNodes = (NodeList) stringsXPath.evaluate(document, XPathConstants.NODESET); - for (int i = 0; i < stringNodes.getLength(); i++) { - Node node = stringNodes.item(i); - String viewName = node.getParentNode().getParentNode().getAttributes().getNamedItem("name").getNodeValue(); - String enumName = node.getParentNode().getAttributes().getNamedItem("name").getNodeValue(); - String name = node.getAttributes().getNamedItem("name").getNodeValue(); - String value = node.getAttributes().getNamedItem("value").getNodeValue(); - - classAttrEnumToValue.put(key(viewName, enumName, name, isSystem), value); - knownClassAttrs.add(key(viewName, enumName, isSystem)); + static class EnumDef { + final String name; + final Map<String, String> values = new HashMap<String, String>(); + + EnumDef(String name) { this.name = name; } + } + + static class EnumRef { + final String viewName; + final String enumName; + + EnumRef(String viewName, String enumName) { + this.enumName = enumName; + this.viewName = viewName; + } + } + + @Override protected void processResourceXml(File xmlFile, Document document, boolean system) throws Exception { + + // Pick up inline enum definitions + { + NodeList nodeList = findNodes(document, "/resources/declare-styleable/attr/enum|/resources/declare-styleable/attr/flag"); + for (int i = 0; i < nodeList.getLength(); i++) { + Node node = nodeList.item(i); + String viewName = node.getParentNode().getParentNode().getAttributes().getNamedItem("name").getNodeValue(); + String enumName = enumName(node.getParentNode().getAttributes().getNamedItem("name").getNodeValue(), system); + String name = node.getAttributes().getNamedItem("name").getNodeValue(); + String value = node.getAttributes().getNamedItem("value").getNodeValue(); + + classEnumToValue.put(key(viewName, enumName, name), value); + knownClassEnums.add(key(viewName, enumName)); + } + } + + // Look for any global enum definitions. + { + NodeList nodeList = findNodes(document, "/resources/attr/enum|/resources/attr/flag"); + for (int i = 0; i < nodeList.getLength(); i++) { + Node node = nodeList.item(i); + + String enumName = enumName(node.getParentNode().getAttributes().getNamedItem("name").getNodeValue(), system); + EnumDef enumDef = enums.get(enumName); + if (enumDef == null) { + enumDef = new EnumDef(enumName); + enums.put(enumName, enumDef); + } + enumDef.values.put(node.getAttributes().getNamedItem("name").getNodeValue(), + node.getAttributes().getNamedItem("value").getNodeValue()); + } + } + + // Note uses of system enums and top level local enums by childless attr nodes + { + NodeList nodeList = findNodes(document, "/resources/declare-styleable/attr[not(node())]"); + for (int i = 0; i < nodeList.getLength(); i++) { + Node node = nodeList.item(i); + + String viewName = node.getParentNode().getAttributes().getNamedItem("name").getNodeValue(); + String enumName = enumName(node.getAttributes().getNamedItem("name").getNodeValue(), system); + + enumRefs.put(key(viewName, enumName), new EnumRef(viewName, enumName)); + } } } public String convertValueToEnum(Class<? extends View> viewClass, String namespace, String attrName, String attrValue) { - boolean isSystem = "android".equals(namespace); - String className = findKnownAttrClass(attrName, viewClass, isSystem); - return classAttrEnumToValue.get(key(className, attrName, attrValue, isSystem)); + resolveReferences(); + if (ANDROID_XML_NAMESPACE.equals(namespace)) attrName = ANDROID_PREFIX + attrName; + String className = findKnownAttrClass(attrName, viewClass).getSimpleName(); + return classEnumToValue.get(key(className, attrName, attrValue)); } public boolean hasAttributeFor(Class<? extends View> viewClass, String namespace, String attrName) { - boolean isSystem = "android".equals(namespace); - return findKnownAttrClass(attrName, viewClass, isSystem) != null; + resolveReferences(); + if (ANDROID_XML_NAMESPACE.equals(namespace)) attrName = ANDROID_PREFIX + attrName; + return findKnownAttrClass(attrName, viewClass) != null; } - private String findKnownAttrClass(String attrName, Class<?> clazz, boolean isSystem) { - while (clazz != null) { - String className = clazz.getName(); - if (isSystem) { - className = clazz.getSimpleName(); + private String enumName(String name, boolean system) { + String enumName = name; + enumName = system ? ANDROID_PREFIX + enumName : enumName; + return enumName; + } + + private NodeList findNodes(Document document, String path) throws XPathExpressionException { + XPathExpression nestedEnumsXPath = XPathFactory.newInstance().newXPath().compile(path); + return (NodeList) nestedEnumsXPath.evaluate(document, XPathConstants.NODESET); + } + + private void resolveReferences() { + if (!resolved) { + for (EnumRef enumRef : enumRefs.values()) { + noteEnumUses(enumRef.viewName, enumRef.enumName); } - if (knownClassAttrs.contains(key(className, attrName, isSystem))) { - return className; + resolved = true; + } + } + + private void noteEnumUses(String viewName, String enumName) { + EnumDef enumDef = enums.get(enumName); + if (enumDef == null) return; + + for (Map.Entry<String, String> entry : enumDef.values.entrySet()) { + classEnumToValue.put(key(viewName, enumName, entry.getKey()), entry.getValue()); + } + knownClassEnums.add(key(viewName, enumName)); + } + + private Class<?> findKnownAttrClass(String attrName, Class<?> clazz) { + while (clazz != null) { + if (knownClassEnums.contains(key(clazz.getSimpleName(), attrName))) { + return clazz; } clazz = clazz.getSuperclass(); } return null; } - private String key(String viewName, String attrName, String name, boolean isSystem) { - return key(viewName, attrName, isSystem) + "#" + name; + private String key(String viewName, String enumName, String name) { + return viewName + "#" + enumName + "#" + name; } - private String key(String viewName, String attrName, boolean isSystem) { - return (isSystem ? "android:" : "") + viewName + "#" + attrName; + private String key(String viewName, String enunName) { + return viewName + "#" + enunName; } } diff --git a/src/main/java/com/xtremelabs/robolectric/res/ColorResourceLoader.java b/src/main/java/com/xtremelabs/robolectric/res/ColorResourceLoader.java index f81046daa..fd4f2eed5 100644 --- a/src/main/java/com/xtremelabs/robolectric/res/ColorResourceLoader.java +++ b/src/main/java/com/xtremelabs/robolectric/res/ColorResourceLoader.java @@ -9,6 +9,7 @@ import java.util.Map; public class ColorResourceLoader extends XpathResourceXmlLoader implements ResourceValueConverter { private ResourceReferenceResolver<Integer> colorResolver = new ResourceReferenceResolver<Integer>("color"); private static Map<String, Integer> androidColors = new HashMap<String, Integer>(); + private static Map<Integer, Integer> androidColorsFromIds = new HashMap<Integer, Integer>(); static { androidColors.put("black", Color.BLACK); @@ -22,6 +23,24 @@ public class ColorResourceLoader extends XpathResourceXmlLoader implements Resou androidColors.put("yellow", Color.YELLOW); androidColors.put("cyan", Color.CYAN); androidColors.put("magenta", Color.MAGENTA); + androidColors.put("transparent", Color.TRANSPARENT); + + androidColors.put("@android:color/black", Color.BLACK); + androidColors.put("@android:color/darkgray", Color.DKGRAY); + androidColors.put("@android:color/gray", Color.GRAY); + androidColors.put("@android:color/lightgray", Color.LTGRAY); + androidColors.put("@android:color/white", Color.WHITE); + androidColors.put("@android:color/red", Color.RED); + androidColors.put("@android:color/green", Color.GREEN); + androidColors.put("@android:color/blue", Color.BLUE); + androidColors.put("@android:color/yellow", Color.YELLOW); + androidColors.put("@android:color/cyan", Color.CYAN); + androidColors.put("@android:color/magenta", Color.MAGENTA); + androidColors.put("@android:color/transparent", Color.TRANSPARENT); + + androidColorsFromIds.put(android.R.color.black, Color.BLACK); + androidColorsFromIds.put(android.R.color.white, Color.WHITE); + androidColorsFromIds.put(android.R.color.transparent, Color.TRANSPARENT); } public ColorResourceLoader(ResourceExtractor resourceExtractor) { @@ -29,13 +48,14 @@ public class ColorResourceLoader extends XpathResourceXmlLoader implements Resou } public int getValue(int colorId) { - String resourceName = resourceExtractor.getResourceName(colorId); - if (resourceName == null) { - return -1; + if (androidColorsFromIds.containsKey(colorId)) { + return androidColorsFromIds.get(colorId); + } else { + String resourceName = resourceExtractor.getResourceName(colorId); + if (resourceName == null) return -1; + Integer resolved = colorResolver.getValue(resourceName); + return resolved == null ? -1 : resolved; } - - Integer colorResolverValue = colorResolver.getValue(resourceName); - return colorResolverValue == null ? -1 : colorResolverValue; } @Override diff --git a/src/main/java/com/xtremelabs/robolectric/res/DrawableResourceLoader.java b/src/main/java/com/xtremelabs/robolectric/res/DrawableResourceLoader.java index bac82e164..001f4ac25 100644 --- a/src/main/java/com/xtremelabs/robolectric/res/DrawableResourceLoader.java +++ b/src/main/java/com/xtremelabs/robolectric/res/DrawableResourceLoader.java @@ -38,18 +38,13 @@ public class DrawableResourceLoader extends XmlLoader { /** document */ protected Map<String, Document> documents = new HashMap<String, Document>(); - /** resource directory */ - protected File resourceDirectory; - /** * DrawableResourceLoader constructor. * * @param extractor Extractor - * @param resourceDirectory Resource directory */ - public DrawableResourceLoader(ResourceExtractor extractor, File resourceDirectory) { + public DrawableResourceLoader(ResourceExtractor extractor) { super(extractor); - this.resourceDirectory = resourceDirectory; } /** diff --git a/src/main/java/com/xtremelabs/robolectric/res/MenuLoader.java b/src/main/java/com/xtremelabs/robolectric/res/MenuLoader.java index 8f2ba007b..821b123cf 100644 --- a/src/main/java/com/xtremelabs/robolectric/res/MenuLoader.java +++ b/src/main/java/com/xtremelabs/robolectric/res/MenuLoader.java @@ -1,24 +1,22 @@ package com.xtremelabs.robolectric.res; -import java.io.File; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.w3c.dom.Document; -import org.w3c.dom.NamedNodeMap; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; - import android.content.Context; import android.text.TextUtils; import android.view.Menu; import android.view.MenuItem; import android.view.SubMenu; - import com.xtremelabs.robolectric.tester.android.util.TestAttributeSet; import com.xtremelabs.robolectric.util.I18nException; +import org.w3c.dom.Document; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import java.io.File; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; public class MenuLoader extends XmlLoader { private Map<String, MenuNode> menuNodesByMenuName = new HashMap<String, MenuNode>(); @@ -94,21 +92,20 @@ public class MenuLoader extends XmlLoader { || nodei.getNodeName().equals("group"); } - public void inflateMenu(Context context, String key, Menu root) { - inflateMenu(context, key, null, root); + public boolean inflateMenu(Context context, String key, Menu root) { + return inflateMenu(context, key, null, root); } - public void inflateMenu(Context context, int resourceId, Menu root) { - inflateMenu(context, resourceExtractor.getResourceName(resourceId), + public boolean inflateMenu(Context context, int resourceId, Menu root) { + return inflateMenu(context, resourceExtractor.getResourceName(resourceId), root); } - private void inflateMenu(Context context, String key, + private boolean inflateMenu(Context context, String key, Map<String, String> attributes, Menu root) { MenuNode menuNode = menuNodesByMenuName.get(key); - if (menuNode == null) { - throw new RuntimeException("Could not find menu " + key); - } + if (menuNode == null) return false; + try { if (attributes != null) { for (Map.Entry<String, String> entry : attributes.entrySet()) { @@ -119,6 +116,7 @@ public class MenuLoader extends XmlLoader { } } menuNode.inflate(context, root); + return true; } catch (I18nException e) { throw e; } catch (Exception e) { diff --git a/src/main/java/com/xtremelabs/robolectric/res/PreferenceLoader.java b/src/main/java/com/xtremelabs/robolectric/res/PreferenceLoader.java index 1d80ae490..16c186aa9 100644 --- a/src/main/java/com/xtremelabs/robolectric/res/PreferenceLoader.java +++ b/src/main/java/com/xtremelabs/robolectric/res/PreferenceLoader.java @@ -1,5 +1,18 @@ package com.xtremelabs.robolectric.res; +import android.content.Context; +import android.preference.Preference; +import android.preference.PreferenceGroup; +import android.preference.PreferenceScreen; +import android.util.AttributeSet; +import com.xtremelabs.robolectric.Robolectric; +import com.xtremelabs.robolectric.tester.android.util.TestAttributeSet; +import com.xtremelabs.robolectric.util.I18nException; +import org.w3c.dom.Document; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + import java.io.File; import java.lang.reflect.Constructor; import java.util.ArrayList; @@ -7,35 +20,20 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import org.w3c.dom.Document; -import org.w3c.dom.NamedNodeMap; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; - -import com.xtremelabs.robolectric.Robolectric; -import com.xtremelabs.robolectric.tester.android.util.TestAttributeSet; -import com.xtremelabs.robolectric.util.I18nException; - -import android.content.Context; -import android.preference.Preference; -import android.preference.PreferenceGroup; -import android.preference.PreferenceScreen; -import android.util.AttributeSet; - public class PreferenceLoader extends XmlLoader { - + private Map<String, PreferenceNode> prefNodesByResourceName = new HashMap<String, PreferenceNode>(); - public PreferenceLoader(ResourceExtractor resourceExtractor) { - super(resourceExtractor); - } + public PreferenceLoader(ResourceExtractor resourceExtractor) { + super(resourceExtractor); + } - @Override - protected void processResourceXml(File xmlFile, Document document, boolean isSystem) throws Exception { - PreferenceNode topLevelNode = new PreferenceNode("top-level", new HashMap<String, String>()); - processChildren(document.getChildNodes(), topLevelNode); - prefNodesByResourceName.put( "xml/" + xmlFile.getName().replace(".xml", ""), topLevelNode.getChildren().get(0)); - } + @Override + protected void processResourceXml(File xmlFile, Document document, boolean isSystem) throws Exception { + PreferenceNode topLevelNode = new PreferenceNode("top-level", new HashMap<String, String>()); + processChildren(document.getChildNodes(), topLevelNode); + prefNodesByResourceName.put( "xml/" + xmlFile.getName().replace(".xml", ""), topLevelNode.getChildren().get(0)); + } private void processChildren(NodeList childNodes, PreferenceNode parent) { for (int i = 0; i < childNodes.getLength(); i++) { @@ -43,12 +41,12 @@ public class PreferenceLoader extends XmlLoader { processNode(node, parent); } } - + private void processNode(Node node, PreferenceNode parent) { String name = node.getNodeName(); NamedNodeMap attributes = node.getAttributes(); Map<String, String> attrMap = new HashMap<String, String>(); - + if (attributes != null) { int length = attributes.getLength(); for (int i = 0; i < length; i++) { @@ -56,34 +54,34 @@ public class PreferenceLoader extends XmlLoader { attrMap.put(attr.getNodeName(), attr.getNodeValue()); } } - + if (!name.startsWith("#")) { - PreferenceNode prefNode = new PreferenceNode(name, attrMap); - if (parent != null) parent.addChild(prefNode); - - processChildren(node.getChildNodes(), prefNode); + PreferenceNode prefNode = new PreferenceNode(name, attrMap); + if (parent != null) parent.addChild(prefNode); + + processChildren(node.getChildNodes(), prefNode); } } - - public PreferenceScreen inflatePreferences(Context context, int resourceId) { - return inflatePreferences(context, resourceExtractor.getResourceName(resourceId)); - } - - public PreferenceScreen inflatePreferences(Context context, String key) { + + public PreferenceScreen inflatePreferences(Context context, int resourceId) { + return inflatePreferences(context, resourceExtractor.getResourceName(resourceId)); + } + + public PreferenceScreen inflatePreferences(Context context, String key) { try { - PreferenceNode prefNode = prefNodesByResourceName.get(key); - return (PreferenceScreen) prefNode.inflate(context, null); + PreferenceNode prefNode = prefNodesByResourceName.get(key); + return (PreferenceScreen) prefNode.inflate(context, null); } catch (I18nException e) { - throw e; + throw e; } catch (Exception e) { throw new RuntimeException("error inflating " + key, e); } - } + } public class PreferenceNode { private String name; private final Map<String, String> attributes; - + private List<PreferenceNode> children = new ArrayList<PreferenceNode>(); public PreferenceNode(String name, Map<String, String> attributes) { @@ -98,47 +96,56 @@ public class PreferenceLoader extends XmlLoader { public void addChild(PreferenceNode prefNode) { children.add(prefNode); } - + public Preference inflate(Context context, Preference parent) throws Exception { - Preference preference = create(context, (PreferenceGroup) parent); + Preference preference = create(context, (PreferenceGroup) parent); for (PreferenceNode child : children) { child.inflate(context, preference); } - - return preference; + + return preference; } - + private Preference create(Context context, PreferenceGroup parent) throws Exception { - Preference preference = constructPreference(context, parent); + Preference preference = constructPreference(context, parent); if (parent != null && parent != preference) { parent.addPreference(preference); - } + } return preference; } - + private Preference constructPreference(Context context, PreferenceGroup parent) throws Exception { - Class<? extends Preference> clazz = pickViewClass(); - - if (clazz.equals(PreferenceScreen.class)) { - return Robolectric.newInstanceOf(PreferenceScreen.class); - } - - try { - TestAttributeSet attributeSet = new TestAttributeSet(attributes); - if (strictI18n) { - attributeSet.validateStrictI18n(); - } + Class<? extends Preference> clazz = pickViewClass(); + + TestAttributeSet attributeSet = new TestAttributeSet(attributes); + if (strictI18n) { + attributeSet.validateStrictI18n(); + } + + /** + * This block is required because the PreferenceScreen(Context, AttributeSet) constructor is somehow hidden + * from reflection. The only way to set keys/titles/summaries on PreferenceScreens is to set them manually. + */ + if (clazz.equals(PreferenceScreen.class)) { + PreferenceScreen screen = Robolectric.newInstanceOf(PreferenceScreen.class); + screen.setKey(attributes.get("android:key")); + screen.setTitle(attributes.get("android:title")); + screen.setSummary(attributes.get("android:summary")); + return screen; + } + + try { return ((Constructor<? extends Preference>) clazz.getConstructor(Context.class, AttributeSet.class)).newInstance(context, attributeSet); } catch (NoSuchMethodException e) { - try { - return ((Constructor<? extends Preference>) clazz.getConstructor(Context.class)).newInstance(context); - } catch (NoSuchMethodException e1) { - return ((Constructor<? extends Preference>) clazz.getConstructor(Context.class, String.class)).newInstance(context, ""); - } + try { + return ((Constructor<? extends Preference>) clazz.getConstructor(Context.class)).newInstance(context); + } catch (NoSuchMethodException e1) { + return ((Constructor<? extends Preference>) clazz.getConstructor(Context.class, String.class)).newInstance(context, ""); + } } } - + private Class<? extends Preference> pickViewClass() { Class<? extends Preference> clazz = loadClass(name); if (clazz == null) { @@ -149,7 +156,7 @@ public class PreferenceLoader extends XmlLoader { } return clazz; } - + private Class<? extends Preference> loadClass(String className) { try { //noinspection unchecked @@ -157,6 +164,6 @@ public class PreferenceLoader extends XmlLoader { } catch (ClassNotFoundException e) { return null; } - } + } } } diff --git a/src/main/java/com/xtremelabs/robolectric/res/ResourceLoader.java b/src/main/java/com/xtremelabs/robolectric/res/ResourceLoader.java index 93c98dc93..d30bf2995 100644 --- a/src/main/java/com/xtremelabs/robolectric/res/ResourceLoader.java +++ b/src/main/java/com/xtremelabs/robolectric/res/ResourceLoader.java @@ -27,6 +27,8 @@ import java.util.List; import java.util.Properties; import java.util.Set; +import java.util.*; + import static com.xtremelabs.robolectric.Robolectric.shadowOf; public class ResourceLoader { @@ -49,36 +51,41 @@ public class ResourceLoader { } }; - private File resourceDir; - private File assetsDir; - private int sdkVersion; - private Class rClass; + private List<File> resourcePath; + private File assetsDir; + private int sdkVersion; + private Class rClass; - private final ResourceExtractor resourceExtractor; - private ViewLoader viewLoader; - private MenuLoader menuLoader; + private final ResourceExtractor resourceExtractor; + private ViewLoader viewLoader; + private MenuLoader menuLoader; private XmlFileLoader xmlFileLoader; - private PreferenceLoader preferenceLoader; - private final StringResourceLoader stringResourceLoader; - private final PluralResourceLoader pluralResourceLoader; - private final StringArrayResourceLoader stringArrayResourceLoader; - private final AttrResourceLoader attrResourceLoader; - private final ColorResourceLoader colorResourceLoader; - private final DrawableResourceLoader drawableResourceLoader; - private final RawResourceLoader rawResourceLoader; - private final DimenResourceLoader dimenResourceLoader; - private final IntegerResourceLoader integerResourceLoader; - private final BoolResourceLoader boolResourceLoader; - private boolean isInitialized = false; - private boolean strictI18n = false; - - private final Set<Integer> ninePatchDrawableIds = new HashSet<Integer>(); + private PreferenceLoader preferenceLoader; + private final StringResourceLoader stringResourceLoader; + private final DimenResourceLoader dimenResourceLoader; + private final IntegerResourceLoader integerResourceLoader; + private final PluralResourceLoader pluralResourceLoader; + private final StringArrayResourceLoader stringArrayResourceLoader; + private final AttrResourceLoader attrResourceLoader; + private final ColorResourceLoader colorResourceLoader; + private final DrawableResourceLoader drawableResourceLoader; + private final BoolResourceLoader boolResourceLoader; + private final List<RawResourceLoader> rawResourceLoaders = new ArrayList<RawResourceLoader>(); + private boolean isInitialized = false; + private boolean strictI18n = false; + private boolean isSystem = false; + private final Set<Integer> ninePatchDrawableIds = new HashSet<Integer>(); + + @Deprecated + public ResourceLoader(int sdkVersion, Class rClass, File resourceDir, File assetsDir) throws Exception { + this(sdkVersion, rClass, safeFileList(resourceDir), assetsDir); + } - public ResourceLoader( int sdkVersion, Class rClass, List<File> resourcePath, File assetsDir) throws Exception { - this(sdkVersion, rClass, resourcePath.get(0), assetsDir); // todo + private static List<File> safeFileList(File resourceDir) { + return resourceDir == null ? Collections.<File> emptyList() : Collections.singletonList(resourceDir); } - public ResourceLoader( int sdkVersion, Class rClass, File resourceDir, File assetsDir) throws Exception { + public ResourceLoader(int sdkVersion, Class rClass, List<File> resourcePath, File assetsDir) throws Exception { this.sdkVersion = sdkVersion; this.assetsDir = assetsDir; this.rClass = rClass; @@ -87,190 +94,226 @@ public class ResourceLoader { resourceExtractor.addLocalRClass( rClass ); resourceExtractor.addSystemRClass( R.class ); - stringResourceLoader = new StringResourceLoader( resourceExtractor ); - pluralResourceLoader = new PluralResourceLoader( resourceExtractor, stringResourceLoader ); - stringArrayResourceLoader = new StringArrayResourceLoader( resourceExtractor, stringResourceLoader ); - colorResourceLoader = new ColorResourceLoader( resourceExtractor ); - attrResourceLoader = new AttrResourceLoader( resourceExtractor ); - drawableResourceLoader = new DrawableResourceLoader( resourceExtractor, resourceDir ); - rawResourceLoader = new RawResourceLoader( resourceExtractor, resourceDir ); - dimenResourceLoader = new DimenResourceLoader( resourceExtractor ); - integerResourceLoader = new IntegerResourceLoader( resourceExtractor ); - boolResourceLoader = new BoolResourceLoader( resourceExtractor ); - - this.resourceDir = resourceDir; - } + stringResourceLoader = new StringResourceLoader(resourceExtractor); + dimenResourceLoader = new DimenResourceLoader( resourceExtractor ); + integerResourceLoader = new IntegerResourceLoader(resourceExtractor); + pluralResourceLoader = new PluralResourceLoader(resourceExtractor, stringResourceLoader); + stringArrayResourceLoader = new StringArrayResourceLoader(resourceExtractor, stringResourceLoader); + colorResourceLoader = new ColorResourceLoader(resourceExtractor); + attrResourceLoader = new AttrResourceLoader(resourceExtractor); + drawableResourceLoader = new DrawableResourceLoader(resourceExtractor); + boolResourceLoader = new BoolResourceLoader( resourceExtractor ); + + this.resourcePath = Collections.unmodifiableList(resourcePath); + } + + public ResourceLoader(ResourceLoader toCopy) throws Exception { + this.sdkVersion = toCopy.sdkVersion; + this.assetsDir = toCopy.assetsDir; + resourceExtractor = new ResourceExtractor(); + resourceExtractor.addLocalRClass(toCopy.rClass); + resourceExtractor.addSystemRClass(R.class); + + stringResourceLoader = new StringResourceLoader(resourceExtractor); + dimenResourceLoader = new DimenResourceLoader(resourceExtractor); + integerResourceLoader = new IntegerResourceLoader(resourceExtractor); + pluralResourceLoader = new PluralResourceLoader(resourceExtractor, stringResourceLoader); + stringArrayResourceLoader = new StringArrayResourceLoader(resourceExtractor, stringResourceLoader); + colorResourceLoader = new ColorResourceLoader(resourceExtractor); + attrResourceLoader = new AttrResourceLoader(resourceExtractor); + drawableResourceLoader = new DrawableResourceLoader(resourceExtractor); + boolResourceLoader = new BoolResourceLoader(resourceExtractor); + + this.resourcePath = Collections.unmodifiableList(toCopy.resourcePath); + } - public void setStrictI18n( boolean strict ) { - this.strictI18n = strict; - if ( viewLoader != null ) { - viewLoader.setStrictI18n( strict ); - } - if ( menuLoader != null ) { - menuLoader.setStrictI18n( strict ); - } - if ( preferenceLoader != null ) { - preferenceLoader.setStrictI18n( strict ); - } - if ( xmlFileLoader != null ) { - xmlFileLoader.setStrictI18n( strict ); - } - } + /* + * For tests only... + */ + protected ResourceLoader(StringResourceLoader stringResourceLoader) { + resourceExtractor = new ResourceExtractor(); + resourcePath = Collections.emptyList(); + this.stringResourceLoader = stringResourceLoader; + this.integerResourceLoader = null; + pluralResourceLoader = null; + viewLoader = null; + stringArrayResourceLoader = null; + attrResourceLoader = null; + colorResourceLoader = null; + drawableResourceLoader = null; + dimenResourceLoader = null; + boolResourceLoader = null; + } - public boolean getStrictI18n() { - return strictI18n; - } + public void setStrictI18n(boolean strict) { + this.strictI18n = strict; + if (viewLoader != null ) { viewLoader.setStrictI18n(strict); } + if (menuLoader != null ) { menuLoader.setStrictI18n(strict); } + if (preferenceLoader != null ) { preferenceLoader.setStrictI18n(strict); } + if (xmlFileLoader != null) { xmlFileLoader.setStrictI18n( strict ); } + } + + public boolean getStrictI18n() { return strictI18n; } + + private void init() { + if (isInitialized) { + return; + } + + if (!resourcePath.isEmpty()) { + try { + viewLoader = new ViewLoader(resourceExtractor, attrResourceLoader); + menuLoader = new MenuLoader(resourceExtractor, attrResourceLoader); + preferenceLoader = new PreferenceLoader(resourceExtractor); + + viewLoader.setStrictI18n(strictI18n); + menuLoader.setStrictI18n(strictI18n); + preferenceLoader.setStrictI18n(strictI18n); + + File systemResourceDir = getSystemResourceDir(getPathToAndroidResources()); + File systemValueResourceDir = getValueResourceDir(systemResourceDir, null, false); + + loadStringResources(systemValueResourceDir, stringResourceLoader, true); + loadPluralsResources(systemValueResourceDir, true); + loadValueResources(systemValueResourceDir, stringArrayResourceLoader, colorResourceLoader, attrResourceLoader, true); + loadDimenResources( systemValueResourceDir, true ); + loadIntegerResources(systemValueResourceDir, integerResourceLoader, true); + loadViewResources(systemResourceDir, viewLoader, true); + + if (!isSystem) { + loadLocalResources(); + } + } catch(I18nException e) { + throw e; + } catch (Exception e) { + throw new RuntimeException(e); + } + } + isInitialized = true; + } - private void init() { - if ( isInitialized ) { - return; - } - - try { - if ( resourceDir != null ) { - viewLoader = new ViewLoader( resourceExtractor, attrResourceLoader ); - menuLoader = new MenuLoader( resourceExtractor, attrResourceLoader ); - preferenceLoader = new PreferenceLoader( resourceExtractor ); - xmlFileLoader = new XmlFileLoader( resourceExtractor ); - - viewLoader.setStrictI18n( strictI18n ); - menuLoader.setStrictI18n( strictI18n ); - preferenceLoader.setStrictI18n( strictI18n ); - xmlFileLoader.setStrictI18n( strictI18n ); - - File systemResourceDir = getSystemResourceDir( getPathToAndroidResources() ); - File localValueResourceDir = getValueResourceDir( resourceDir, null, true ); - File systemValueResourceDir = getValueResourceDir( systemResourceDir, null, false ); - File preferenceDir = getPreferenceResourceDir( resourceDir ); - - loadStringResources( localValueResourceDir, systemValueResourceDir ); - loadPluralsResources( localValueResourceDir, systemValueResourceDir ); - loadValueResources( localValueResourceDir, systemValueResourceDir ); - loadDimenResources( localValueResourceDir, systemValueResourceDir ); - loadIntegerResource( localValueResourceDir, systemValueResourceDir ); - loadViewResources( systemResourceDir, resourceDir ); - loadMenuResources( resourceDir ); - loadDrawableResources( resourceDir ); - loadPreferenceResources( preferenceDir ); - loadXmlFileResources( preferenceDir ); - loadOtherResources(resourceDir); // todo ? - - listNinePatchResources(ninePatchDrawableIds, resourceDir); - } else { - viewLoader = null; - menuLoader = null; - preferenceLoader = null; - xmlFileLoader = null; - } - } catch ( I18nException e ) { - throw e; - } catch ( Exception e ) { - throw new RuntimeException( e ); - } - isInitialized = true; - } + private void loadLocalResources() throws Exception { + for (File resourceDir : resourcePath) { + File localValueResourceDir = getValueResourceDir(resourceDir, null, true); + RawResourceLoader rawResourceLoader = new RawResourceLoader(resourceExtractor, resourceDir); + File preferenceDir = getPreferenceResourceDir(resourceDir); + + loadStringResources(localValueResourceDir, stringResourceLoader, false); + loadPluralsResources(localValueResourceDir, false); + loadValueResources(localValueResourceDir, stringArrayResourceLoader, colorResourceLoader, attrResourceLoader, false); + loadDimenResources(localValueResourceDir, false); + loadIntegerResources(localValueResourceDir, integerResourceLoader, false); + loadViewResources(resourceDir, viewLoader, false); + loadMenuResources(resourceDir, menuLoader); + loadDrawableResources(resourceDir); + loadPreferenceResources(preferenceDir); + loadOtherResources(resourceDir); + + listNinePatchResources(ninePatchDrawableIds, resourceDir); + + rawResourceLoaders.add(rawResourceLoader); + } + } - /** - * Reload values resources, include String, Plurals, Dimen, Prefs, Menu - * - * @param locale - */ - public void reloadValuesResouces( String qualifiers ) { - - File systemResourceDir = getSystemResourceDir( getPathToAndroidResources() ); - File localValueResourceDir = getValueResourceDir( resourceDir, qualifiers, true ); - File systemValueResourceDir = getValueResourceDir( systemResourceDir, null, false ); - File preferenceDir = getPreferenceResourceDir( resourceDir ); - - try { - loadStringResources( localValueResourceDir, systemValueResourceDir ); - loadPluralsResources( localValueResourceDir, systemValueResourceDir ); - loadValueResources( localValueResourceDir, systemValueResourceDir ); - loadDimenResources( localValueResourceDir, systemValueResourceDir ); - loadIntegerResource( localValueResourceDir, systemValueResourceDir ); - loadMenuResources( resourceDir ); - loadPreferenceResources( preferenceDir ); - } catch ( Exception e ) { - throw new RuntimeException( e ); - } - } - - private File getSystemResourceDir( String pathToAndroidResources ) { - return pathToAndroidResources != null ? new File( pathToAndroidResources ) : null; - } + /** + * Reload values resources, include String, Plurals, Dimen, Prefs, Menu + * + * @param qualifiers + */ + public void reloadValuesResouces( String qualifiers ) { + + File systemResourceDir = getSystemResourceDir( getPathToAndroidResources() ); + File systemValueResourceDir = getValueResourceDir( systemResourceDir, null, false ); + + try { + loadStringResources(systemValueResourceDir, stringResourceLoader, true); + loadPluralsResources(systemValueResourceDir, true); + loadValueResources(systemValueResourceDir, stringArrayResourceLoader, colorResourceLoader, attrResourceLoader, true); + loadDimenResources( systemValueResourceDir, true ); + loadIntegerResources(systemValueResourceDir, integerResourceLoader, true); + loadViewResources(systemResourceDir, viewLoader, true); + + if (!isSystem) { + loadLocalResources(); + } + } catch ( Exception e ) { + throw new RuntimeException( e ); + } + } - private void loadStringResources( File localResourceDir, File systemValueResourceDir ) throws Exception { - DocumentLoader stringResourceDocumentLoader = new DocumentLoader( this.stringResourceLoader ); - loadValueResourcesFromDirs( stringResourceDocumentLoader, localResourceDir, systemValueResourceDir ); - } - private void loadPluralsResources( File localResourceDir, File systemValueResourceDir ) throws Exception { - DocumentLoader stringResourceDocumentLoader = new DocumentLoader( this.pluralResourceLoader ); - loadValueResourcesFromDirs( stringResourceDocumentLoader, localResourceDir, systemValueResourceDir ); - } + private File getSystemResourceDir(String pathToAndroidResources) { + return pathToAndroidResources != null ? new File(pathToAndroidResources) : null; + } - private void loadValueResources( File localResourceDir, File systemValueResourceDir ) throws Exception { - DocumentLoader valueResourceLoader = new DocumentLoader( stringArrayResourceLoader, colorResourceLoader, - attrResourceLoader ); - loadValueResourcesFromDirs( valueResourceLoader, localResourceDir, systemValueResourceDir ); - } + private void loadStringResources(File resourceDir, StringResourceLoader stringResourceLoader, boolean system) throws Exception { + DocumentLoader stringResourceDocumentLoader = new DocumentLoader(stringResourceLoader); + loadValueResourcesFromDirs(stringResourceDocumentLoader, resourceDir, system); + } - private void loadDimenResources( File localResourceDir, File systemValueResourceDir ) throws Exception { - DocumentLoader dimenResourceDocumentLoader = new DocumentLoader( this.dimenResourceLoader ); - loadValueResourcesFromDirs( dimenResourceDocumentLoader, localResourceDir, systemValueResourceDir ); - } + private void loadPluralsResources(File resourceDir, boolean system) throws Exception { + DocumentLoader stringResourceDocumentLoader = new DocumentLoader(this.pluralResourceLoader); + loadValueResourcesFromDirs(stringResourceDocumentLoader, resourceDir, system); + } - private void loadIntegerResource( File localResourceDir, File systemValueResourceDir ) throws Exception { - DocumentLoader integerResourceDocumentLoader = new DocumentLoader( this.integerResourceLoader ); - loadValueResourcesFromDirs( integerResourceDocumentLoader, localResourceDir, systemValueResourceDir ); - } + private void loadValueResources(File resourceDir, StringArrayResourceLoader stringArrayResourceLoader, ColorResourceLoader colorResourceLoader, AttrResourceLoader attrResourceLoader, boolean system) throws Exception { + DocumentLoader valueResourceLoader = new DocumentLoader(stringArrayResourceLoader, colorResourceLoader, attrResourceLoader); + loadValueResourcesFromDirs(valueResourceLoader, resourceDir, system); + } - private void loadViewResources( File systemResourceDir, File xmlResourceDir ) throws Exception { - DocumentLoader viewDocumentLoader = new DocumentLoader( viewLoader ); - loadLayoutResourceXmlSubDirs( viewDocumentLoader, xmlResourceDir, false ); - loadLayoutResourceXmlSubDirs( viewDocumentLoader, systemResourceDir, true ); - } + private void loadDimenResources( File resourceDir, boolean system) throws Exception { + DocumentLoader dimenResourceDocumentLoader = new DocumentLoader( this.dimenResourceLoader ); + loadValueResourcesFromDirs(dimenResourceDocumentLoader, resourceDir, system); + } - private void loadMenuResources( File xmlResourceDir ) throws Exception { - DocumentLoader menuDocumentLoader = new DocumentLoader( menuLoader ); - loadMenuResourceXmlDirs( menuDocumentLoader, xmlResourceDir ); - } + private void loadIntegerResources(File resourceDir, IntegerResourceLoader integerResourceLoader, boolean system) throws Exception { + DocumentLoader integerResourceDocumentLoader = new DocumentLoader(integerResourceLoader); + loadValueResourcesFromDirs(integerResourceDocumentLoader, resourceDir, system); + } - private void loadDrawableResources( File xmlResourceDir ) throws Exception { - DocumentLoader drawableDocumentLoader = new DocumentLoader( drawableResourceLoader ); - loadDrawableResourceXmlDirs( drawableDocumentLoader, xmlResourceDir ); - } + private void loadViewResources(File resourceDir, ViewLoader viewLoader, boolean system) throws Exception { + DocumentLoader viewDocumentLoader = new DocumentLoader(viewLoader); + loadLayoutResourceXmlSubDirs(viewDocumentLoader, resourceDir, system); + } + + private void loadMenuResources(File xmlResourceDir, MenuLoader menuLoader /* todo , boolean system */) throws Exception { + DocumentLoader menuDocumentLoader = new DocumentLoader(menuLoader); + loadMenuResourceXmlDirs(menuDocumentLoader, xmlResourceDir); + } - private void loadPreferenceResources( File xmlResourceDir ) throws Exception { + private void loadDrawableResources( File xmlResourceDir ) throws Exception { + DocumentLoader drawableDocumentLoader = new DocumentLoader( drawableResourceLoader ); + loadDrawableResourceXmlDirs( drawableDocumentLoader, xmlResourceDir ); + } + + private void loadPreferenceResources( File xmlResourceDir ) throws Exception { if ( xmlResourceDir.exists() ) { DocumentLoader preferenceDocumentLoader = new DocumentLoader( preferenceLoader ); preferenceDocumentLoader.loadResourceXmlDir( xmlResourceDir ); } } - + /** - * All the Xml files should be loaded. + * All the Xml files should be loaded. */ private void loadXmlFileResources( File xmlResourceDir ) throws Exception { if ( xmlResourceDir.exists() ) { - DocumentLoader xmlFileDocumentLoader = + DocumentLoader xmlFileDocumentLoader = new DocumentLoader( xmlFileLoader ); xmlFileDocumentLoader.loadResourceXmlDir( xmlResourceDir ); } } - // todo ? protected void loadOtherResources(File xmlResourceDir) { } - private void loadLayoutResourceXmlSubDirs( DocumentLoader layoutDocumentLoader, File xmlResourceDir, boolean isSystem ) - throws Exception { - if ( xmlResourceDir != null ) { - layoutDocumentLoader.loadResourceXmlDirs( isSystem, xmlResourceDir.listFiles( LAYOUT_DIR_FILE_FILTER ) ); - } - } + private void loadLayoutResourceXmlSubDirs(DocumentLoader layoutDocumentLoader, File xmlResourceDir, boolean isSystem) throws Exception { + if (xmlResourceDir != null) { + layoutDocumentLoader.loadResourceXmlDirs(isSystem, xmlResourceDir.listFiles(LAYOUT_DIR_FILE_FILTER)); + } + } - private void loadMenuResourceXmlDirs( DocumentLoader menuDocumentLoader, File xmlResourceDir ) throws Exception { + private void loadMenuResourceXmlDirs( DocumentLoader menuDocumentLoader, File xmlResourceDir ) throws Exception { if ( xmlResourceDir != null ) { menuDocumentLoader.loadResourceXmlDirs( xmlResourceDir.listFiles( MENU_DIR_FILE_FILTER ) ); } @@ -282,11 +325,13 @@ public class ResourceLoader { } } - private void loadValueResourcesFromDirs( DocumentLoader documentLoader, File localValueResourceDir, - File systemValueResourceDir ) throws Exception { - loadValueResourcesFromDir( documentLoader, localValueResourceDir ); - loadSystemResourceXmlDir( documentLoader, systemValueResourceDir ); - } + private void loadValueResourcesFromDirs(DocumentLoader documentLoader, File resourceDir, boolean system) throws Exception { + if (system) { + loadSystemResourceXmlDir(documentLoader, resourceDir); + } else { + loadValueResourcesFromDir(documentLoader, resourceDir); + } + } private void loadValueResourcesFromDir( DocumentLoader documentloader, File xmlResourceDir ) throws Exception { if ( xmlResourceDir != null ) { @@ -332,32 +377,32 @@ public class ResourceLoader { return null; } - private String getAndroidResourcePathFromLocalProperties() { - // Hand tested - // This is the path most often taken by IntelliJ - File rootDir = resourceDir.getParentFile(); - String localPropertiesFileName = "local.properties"; - File localPropertiesFile = new File( rootDir, localPropertiesFileName ); - if ( !localPropertiesFile.exists() ) { - localPropertiesFile = new File( localPropertiesFileName ); - } - if ( localPropertiesFile.exists() ) { - Properties localProperties = new Properties(); - try { - localProperties.load( new FileInputStream( localPropertiesFile ) ); - PropertiesHelper.doSubstitutions( localProperties ); - String sdkPath = localProperties.getProperty( "sdk.dir" ); - if ( sdkPath != null ) { - return getResourcePathFromSdkPath( sdkPath ); - } - } catch ( IOException e ) { - // fine, we'll try something else - } - } - return null; - } + private String getAndroidResourcePathFromLocalProperties() { + // Hand tested + // This is the path most often taken by IntelliJ + File rootDir = resourcePath.get(0).getParentFile(); + String localPropertiesFileName = "local.properties"; + File localPropertiesFile = new File(rootDir, localPropertiesFileName); + if (!localPropertiesFile.exists()) { + localPropertiesFile = new File(localPropertiesFileName); + } + if (localPropertiesFile.exists()) { + Properties localProperties = new Properties(); + try { + localProperties.load(new FileInputStream(localPropertiesFile)); + PropertiesHelper.doSubstitutions(localProperties); + String sdkPath = localProperties.getProperty("sdk.dir"); + if (sdkPath != null) { + return getResourcePathFromSdkPath(sdkPath); + } + } catch (IOException e) { + // fine, we'll try something else + } + } + return null; + } - private String getAndroidResourcePathFromSystemEnvironment() { + private String getAndroidResourcePathFromSystemEnvironment() { // Hand tested String resourcePath = System.getenv().get( "ANDROID_HOME" ); if ( resourcePath != null ) { @@ -375,23 +420,23 @@ public class ResourceLoader { return null; } - private String getAndroidResourcePathByExecingWhichAndroid() { - // Hand tested - // Should always work from the command line. Often fails in IDEs because - // they don't pass the full PATH in the environment - try { - Process process = Runtime.getRuntime().exec( new String[] { "which", "android" } ); - String sdkPath = new BufferedReader( new InputStreamReader( process.getInputStream() ) ).readLine(); - if ( sdkPath != null && sdkPath.endsWith( "tools/android" ) ) { - return getResourcePathFromSdkPath( sdkPath.substring( 0, sdkPath.indexOf( "tools/android" ) ) ); - } - } catch ( IOException e ) { - // fine we'll try something else - } - return null; - } + private String getAndroidResourcePathByExecingWhichAndroid() { + // Hand tested + // Should always work from the command line. Often fails in IDEs because + // they don't pass the full PATH in the environment + try { + Process process = Runtime.getRuntime().exec( new String[] { "which", "android" } ); + String sdkPath = new BufferedReader( new InputStreamReader( process.getInputStream() ) ).readLine(); + if ( sdkPath != null && sdkPath.endsWith( "tools/android" ) ) { + return getResourcePathFromSdkPath( sdkPath.substring( 0, sdkPath.indexOf( "tools/android" ) ) ); + } + } catch ( IOException e ) { + // fine we'll try something else + } + return null; + } - private String getResourcePathFromSdkPath( String sdkPath ) { + private String getResourcePathFromSdkPath( String sdkPath ) { File androidResourcePath = new File( sdkPath, getAndroidResourceSubPath() ); return androidResourcePath.exists() ? androidResourcePath.toString() : null; } @@ -400,35 +445,17 @@ public class ResourceLoader { return "platforms/android-" + sdkVersion + "/data/res"; } - static boolean isLayoutDirectory( String path ) { - return path.contains( File.separator + "layout" ); - } - - static boolean isDrawableDirectory( String path ) { - return path.contains( File.separator + "drawable" ); - } + static boolean isLayoutDirectory( String path ) { + return path.contains( File.separator + "layout" ); + } - static boolean isMenuDirectory( String path ) { - return path.contains( File.separator + "menu" ); - } + static boolean isDrawableDirectory( String path ) { + return path.contains( File.separator + "drawable" ); + } - /* - * For tests only... - */ - protected ResourceLoader( StringResourceLoader stringResourceLoader ) { - resourceExtractor = new ResourceExtractor(); - this.stringResourceLoader = stringResourceLoader; - pluralResourceLoader = null; - viewLoader = null; - stringArrayResourceLoader = null; - attrResourceLoader = null; - colorResourceLoader = null; - drawableResourceLoader = null; - rawResourceLoader = null; - dimenResourceLoader = null; - integerResourceLoader = null; - boolResourceLoader = null; - } + static boolean isMenuDirectory( String path ) { + return path.contains( File.separator + "menu" ); + } public static ResourceLoader getFrom( Context context ) { ResourceLoader resourceLoader = shadowOf( context.getApplicationContext() ).getResourceLoader(); @@ -441,42 +468,58 @@ public class ResourceLoader { return resourceExtractor.getResourceName( viewId ); } - public View inflateView( Context context, int resource, ViewGroup viewGroup ) { - init(); - return viewLoader.inflateView( context, resource, viewGroup ); - } + public View inflateView(Context context, int resource, ViewGroup viewGroup) { + init(); - public int getColorValue( int id ) { - init(); - return colorResourceLoader.getValue( id ); - } + View viewNode = viewLoader.inflateView(context, resource, viewGroup); + if (viewNode != null) return viewNode; - public String getStringValue( int id ) { - init(); - return stringResourceLoader.getValue( id ); - } + throw new RuntimeException("Could not find layout " + resourceExtractor.getResourceName(resource)); + } - public String getPluralStringValue( int id, int quantity ) { - init(); - return pluralResourceLoader.getValue( id, quantity ); - } + public int getColorValue(int id) { + init(); - public float getDimenValue( int id ) { - init(); - return dimenResourceLoader.getValue( id ); - } + int value = colorResourceLoader.getValue(id); + if (value != -1) return value; - public int getIntegerValue( int id ) { - init(); - return integerResourceLoader.getValue( id ); - } - - public boolean getBooleanValue( int id ) { - init(); - return boolResourceLoader.getValue( id ); - } - - public XmlResourceParser getXml( int id ) { + return -1; + } + + public String getStringValue(int id) { + init(); + + String value = stringResourceLoader.getValue(id); + if (value != null) return value; + + return null; + } + + public String getPluralStringValue(int id, int quantity) { + init(); + return pluralResourceLoader.getValue(id, quantity); + } + + public float getDimenValue( int id ) { + init(); + return dimenResourceLoader.getValue( id ); + } + + public int getIntegerValue(int id) { + init(); + + Integer value = integerResourceLoader.getValue(id); + if (value != null) return value; + + return 0; + } + + public boolean getBooleanValue( int id ) { + init(); + return boolResourceLoader.getValue( id ); + } + + public XmlResourceParser getXml( int id ) { init(); return xmlFileLoader.getXml( id ); } @@ -568,20 +611,33 @@ public class ResourceLoader { } } - public InputStream getRawValue( int id ) { - init(); - return rawResourceLoader.getValue( id ); - } + public InputStream getRawValue(int id) { + init(); - public String[] getStringArrayValue( int id ) { - init(); - return stringArrayResourceLoader.getArrayValue( id ); - } + for (RawResourceLoader rawResourceLoader : rawResourceLoaders) { + InputStream stream = rawResourceLoader.getValue(id); + if (stream != null) return stream; + } - public void inflateMenu( Context context, int resource, Menu root ) { - init(); - menuLoader.inflateMenu( context, resource, root ); - } + return null; + } + + public String[] getStringArrayValue(int id) { + init(); + + String[] arrayValue = stringArrayResourceLoader.getArrayValue(id); + if (arrayValue != null) return arrayValue; + + return null; + } + + public void inflateMenu(Context context, int resource, Menu root) { + init(); + + if (menuLoader.inflateMenu(context, resource, root)) return; + + throw new RuntimeException("Could not find menu " + resourceExtractor.getResourceName(resource)); + } public PreferenceScreen inflatePreferences( Context context, int resourceId ) { init(); @@ -597,20 +653,24 @@ public class ResourceLoader { return rClass; } - public void setLocalRClass( Class clazz ) { - rClass = clazz; - } + public ViewLoader.ViewNode getLayoutViewNode(String layoutName) { + return viewLoader.viewNodesByLayoutName.get(layoutName); + } - public ResourceExtractor getResourceExtractor() { - return resourceExtractor; - } + public void setSystem(boolean isSystem) { + this.isSystem = isSystem; + } - public ViewLoader.ViewNode getLayoutViewNode( String layoutName ) { - return viewLoader.viewNodesByLayoutName.get( layoutName ); - } + public void setLayoutQualifierSearchPath( String... locations ) { + init(); + viewLoader.setLayoutQualifierSearchPath( locations ); + } - public void setLayoutQualifierSearchPath( String... locations ) { - init(); - viewLoader.setLayoutQualifierSearchPath( locations ); - } + public void setLocalRClass( Class clazz ) { + rClass = clazz; + } + + public ResourceExtractor getResourceExtractor() { + return resourceExtractor; + } } diff --git a/src/main/java/com/xtremelabs/robolectric/res/RobolectricPackageManager.java b/src/main/java/com/xtremelabs/robolectric/res/RobolectricPackageManager.java index d58a7c5c8..5121ef030 100644 --- a/src/main/java/com/xtremelabs/robolectric/res/RobolectricPackageManager.java +++ b/src/main/java/com/xtremelabs/robolectric/res/RobolectricPackageManager.java @@ -1,8 +1,5 @@ package com.xtremelabs.robolectric.res; -import java.util.*; -import java.util.Map.Entry; - import android.content.ComponentName; import android.content.ContextWrapper; import android.content.Intent; @@ -11,10 +8,11 @@ import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.ResolveInfo; import android.graphics.drawable.Drawable; - import com.xtremelabs.robolectric.RobolectricConfig; import com.xtremelabs.robolectric.tester.android.content.pm.StubPackageManager; +import java.util.*; + public class RobolectricPackageManager extends StubPackageManager { private Map<String, PackageInfo> packageList; @@ -119,7 +117,7 @@ public class RobolectricPackageManager extends StubPackageManager { drawableList.put( intent.getComponent(), d); } - @Override + @Override public Intent getLaunchIntentForPackage(String packageName) { Intent i = new Intent(); i.setComponent( new ComponentName(packageName, "") ); diff --git a/src/main/java/com/xtremelabs/robolectric/res/ViewLoader.java b/src/main/java/com/xtremelabs/robolectric/res/ViewLoader.java index b3b847b29..4f7f3839b 100644 --- a/src/main/java/com/xtremelabs/robolectric/res/ViewLoader.java +++ b/src/main/java/com/xtremelabs/robolectric/res/ViewLoader.java @@ -24,6 +24,9 @@ import java.util.*; import static com.xtremelabs.robolectric.Robolectric.shadowOf; public class ViewLoader extends XmlLoader { + /** + * Map of "layout/foo" to the View nodes for that layout file + */ protected Map<String, ViewNode> viewNodesByLayoutName = new HashMap<String, ViewNode>(); private AttrResourceLoader attrResourceLoader; private List<String> qualifierSearchPath = new ArrayList<String>(); @@ -37,11 +40,19 @@ public class ViewLoader extends XmlLoader { protected void processResourceXml(File xmlFile, Document document, boolean isSystem) throws Exception { ViewNode topLevelNode = new ViewNode("top-level", new HashMap<String, String>(), isSystem); processChildren(document.getChildNodes(), topLevelNode); - String layoutName = xmlFile.getParentFile().getName() + "/" + xmlFile.getName().replace(".xml", ""); + String parentDir = xmlFile.getParentFile().getName(); + String layoutName = "layout/" + xmlFile.getName().replace(".xml", ""); + String specificLayoutName = parentDir + "/" + xmlFile.getName().replace(".xml", ""); if (isSystem) { layoutName = "android:" + layoutName; + specificLayoutName = "android:" + specificLayoutName; } - viewNodesByLayoutName.put(layoutName, topLevelNode.getChildren().get(0)); + // Check to see if the generic "layout/foo" is already in the map. If not, add it. + if (!viewNodesByLayoutName.containsKey(layoutName)) { + viewNodesByLayoutName.put(layoutName, topLevelNode.getChildren().get(0)); + } + // Add the specific "layout-land/foo" to the map. If this happens to be "layout/foo", it's a no-op. + viewNodesByLayoutName.put(specificLayoutName, topLevelNode.getChildren().get(0)); } private void processChildren(NodeList childNodes, ViewNode parent) { @@ -68,7 +79,7 @@ public class ViewLoader extends XmlLoader { parent.requestFocusOverride = true; } else if (!name.startsWith("#")) { ViewNode viewNode = new ViewNode(name, attrMap, parent.isSystem); - if (parent != null) parent.addChild(viewNode); + parent.addChild(viewNode); processChildren(node.getChildNodes(), viewNode); } @@ -153,7 +164,9 @@ public class ViewLoader extends XmlLoader { child.inflate(context, view); } - invokeOnFinishInflate(view); + if (view != null) { + invokeOnFinishInflate(view); + } return view; } @@ -166,8 +179,7 @@ public class ViewLoader extends XmlLoader { private View create(Context context, ViewGroup parent) throws Exception { if (name.equals("include")) { String layout = attributes.get("layout"); - View view = inflateView(context, layout.substring(1), attributes, parent); - return view; + return inflateView(context, layout.substring(1), attributes, parent); } else if (name.equals("merge")) { return parent; } else if (name.equals("fragment")) { diff --git a/src/main/java/com/xtremelabs/robolectric/shadows/ShadowAbsoluteLayout.java b/src/main/java/com/xtremelabs/robolectric/shadows/ShadowAbsoluteLayout.java index 9460425a5..bc1beddab 100644 --- a/src/main/java/com/xtremelabs/robolectric/shadows/ShadowAbsoluteLayout.java +++ b/src/main/java/com/xtremelabs/robolectric/shadows/ShadowAbsoluteLayout.java @@ -1,10 +1,38 @@ package com.xtremelabs.robolectric.shadows; +import android.view.View; +import android.view.ViewGroup; import android.widget.AbsoluteLayout; +import com.xtremelabs.robolectric.internal.Implementation; import com.xtremelabs.robolectric.internal.Implements; +import com.xtremelabs.robolectric.internal.RealObject; @SuppressWarnings({"UnusedDeclaration"}) @Implements(AbsoluteLayout.class) public class ShadowAbsoluteLayout extends ShadowViewGroup { + private AbsoluteLayout.LayoutParams layoutParams = new AbsoluteLayout.LayoutParams(0, 0, 0, 0); + @Implementation + @Override + public ViewGroup.LayoutParams getLayoutParams() { + return layoutParams; + } + + @Implements(AbsoluteLayout.LayoutParams.class) + public static class ShadowLayoutParams { + @RealObject + AbsoluteLayout.LayoutParams realLayoutParams; + + public void __constructor__(int width, int height, int x, int y) { + realLayoutParams.width = width; + realLayoutParams.height = height; + realLayoutParams.x = x; + realLayoutParams.y = y; + } + } + + @Override + protected void setChildLayoutParams(View child) { + child.setLayoutParams(new AbsoluteLayout.LayoutParams(0, 0, 0, 0)); + } } diff --git a/src/main/java/com/xtremelabs/robolectric/shadows/ShadowActivity.java b/src/main/java/com/xtremelabs/robolectric/shadows/ShadowActivity.java index 76333d9b9..3bbcaf328 100644 --- a/src/main/java/com/xtremelabs/robolectric/shadows/ShadowActivity.java +++ b/src/main/java/com/xtremelabs/robolectric/shadows/ShadowActivity.java @@ -278,6 +278,14 @@ public class ShadowActivity extends ShadowContextWrapper { return parent; } + /** + * Allow setting of Parent fragmentActivity (for unit testing purposes only) + * @param parent Parent fragmentActivity to set on this fragmentActivity + */ + public void setParent(Activity parent){ + this.parent = parent; + } + @Implementation public void onBackPressed() { finish(); @@ -346,12 +354,20 @@ public class ShadowActivity extends ShadowContextWrapper { @Implementation public void setRequestedOrientation(int requestedOrientation) { - this.requestedOrientation = requestedOrientation; + if (getParent() != null){ + getParent().setRequestedOrientation(requestedOrientation); + } else { + this.requestedOrientation = requestedOrientation; + } } @Implementation public int getRequestedOrientation() { - return requestedOrientation; + if (getParent() != null){ + return getParent().getRequestedOrientation(); + } else { + return this.requestedOrientation; + } } @Implementation @@ -483,6 +499,11 @@ public class ShadowActivity extends ShadowContextWrapper { } @Implementation + public void startActivity(Intent intent) { + startActivityForResult(intent, -1); + } + + @Implementation public void startActivityForResult(Intent intent, int requestCode) { intentRequestCodeMap.put(intent, requestCode); startedActivitiesForResults.add(new IntentForResult(intent, requestCode)); diff --git a/src/main/java/com/xtremelabs/robolectric/shadows/ShadowAlertDialog.java b/src/main/java/com/xtremelabs/robolectric/shadows/ShadowAlertDialog.java index 53f20f67f..a23a37873 100644 --- a/src/main/java/com/xtremelabs/robolectric/shadows/ShadowAlertDialog.java +++ b/src/main/java/com/xtremelabs/robolectric/shadows/ShadowAlertDialog.java @@ -334,7 +334,7 @@ public class ShadowAlertDialog extends ShadowDialog { @Implementation(i18nSafe=false) public AlertDialog.Builder setTitle(CharSequence title) { - this.title = title.toString(); + this.title = title == null ? "" : title.toString(); return realBuilder; } @@ -352,7 +352,7 @@ public class ShadowAlertDialog extends ShadowDialog { @Implementation(i18nSafe=false) public AlertDialog.Builder setMessage(CharSequence message) { - this.message = message.toString(); + this.message = message == null ? "" : message.toString(); return realBuilder; } diff --git a/src/main/java/com/xtremelabs/robolectric/shadows/ShadowApplication.java b/src/main/java/com/xtremelabs/robolectric/shadows/ShadowApplication.java index 7b1471e75..057baee8d 100644 --- a/src/main/java/com/xtremelabs/robolectric/shadows/ShadowApplication.java +++ b/src/main/java/com/xtremelabs/robolectric/shadows/ShadowApplication.java @@ -137,6 +137,14 @@ public class ShadowApplication extends ShadowContextWrapper { return resources; } + /** + * Reset (set to null) resources instance, so they will be reloaded next time they are + * {@link #getResources gotten} + */ + public void resetResources(){ + resources = null; + } + @Implementation @Override public ContentResolver getContentResolver() { diff --git a/src/main/java/com/xtremelabs/robolectric/shadows/ShadowBitmapDrawable.java b/src/main/java/com/xtremelabs/robolectric/shadows/ShadowBitmapDrawable.java index c7cc9e774..c6eaaaa9f 100644 --- a/src/main/java/com/xtremelabs/robolectric/shadows/ShadowBitmapDrawable.java +++ b/src/main/java/com/xtremelabs/robolectric/shadows/ShadowBitmapDrawable.java @@ -1,15 +1,18 @@ package com.xtremelabs.robolectric.shadows; +import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.ColorFilter; import android.graphics.Paint; import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; import com.xtremelabs.robolectric.internal.Implementation; import com.xtremelabs.robolectric.internal.Implements; import com.xtremelabs.robolectric.internal.RealObject; import static android.graphics.Shader.TileMode; +import static com.xtremelabs.robolectric.Robolectric.newInstanceOf; import static com.xtremelabs.robolectric.Robolectric.shadowOf; @SuppressWarnings({"UnusedDeclaration"}) @@ -28,6 +31,10 @@ public class ShadowBitmapDrawable extends ShadowDrawable { this.bitmap = bitmap; } + public void __constructor__(Resources resources, Bitmap bitmap) { + this.bitmap = bitmap; + } + /** * Draws the contained bitmap onto the canvas at 0,0 with a default {@code Paint} * @@ -41,6 +48,16 @@ public class ShadowBitmapDrawable extends ShadowDrawable { } @Implementation + public Drawable mutate() { + BitmapDrawable real = newInstanceOf(BitmapDrawable.class); + ShadowBitmapDrawable shadow = shadowOf(real); + shadow.bitmap = this.bitmap; + shadow.colorFilter = this.colorFilter; + shadow.drawableCreateFromStreamSource = drawableCreateFromStreamSource; + return real; + } + + @Implementation public void setColorFilter(android.graphics.ColorFilter colorFilter) { this.colorFilter = colorFilter; } diff --git a/src/main/java/com/xtremelabs/robolectric/shadows/ShadowBitmapFactory.java b/src/main/java/com/xtremelabs/robolectric/shadows/ShadowBitmapFactory.java index 1fa74cd39..c84b4c280 100644 --- a/src/main/java/com/xtremelabs/robolectric/shadows/ShadowBitmapFactory.java +++ b/src/main/java/com/xtremelabs/robolectric/shadows/ShadowBitmapFactory.java @@ -11,7 +11,7 @@ import com.xtremelabs.robolectric.internal.Implementation; import com.xtremelabs.robolectric.internal.Implements; import com.xtremelabs.robolectric.util.Join; -import java.io.InputStream; +import java.io.*; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -27,48 +27,77 @@ public class ShadowBitmapFactory { private static Map<String, Point> widthAndHeightMap = new HashMap<String, Point>(); @Implementation - public static Bitmap decodeResource(Resources res, int id) { - Bitmap bitmap = create("resource:" + getResourceName(id)); - shadowOf(bitmap).setLoadedFromResourceId(id); - return bitmap; - } - - @Implementation public static Bitmap decodeResource(Resources res, int id, BitmapFactory.Options options) { Bitmap bitmap = create("resource:" + getResourceName(id), options); shadowOf(bitmap).setLoadedFromResourceId(id); return bitmap; } + @Implementation + public static Bitmap decodeResource(Resources res, int id) { + return decodeResource(res, id, null); + } + private static String getResourceName(int id) { return shadowOf(Robolectric.application).getResourceLoader().getNameForId(id); } @Implementation public static Bitmap decodeFile(String pathName) { - return create("file:" + pathName); + return decodeFile(pathName, null); } @Implementation public static Bitmap decodeFile(String pathName, BitmapFactory.Options options) { + File file = new File(pathName); + if (file.exists()){ + try { + BufferedReader bufferedReader = new BufferedReader(new FileReader(file)); + String line = bufferedReader.readLine(); + StringBuilder sb = new StringBuilder(); + while (line != null){ + sb.append(line); + line = bufferedReader.readLine(); + } + bufferedReader.close(); + + return create(sb.toString()); + + } catch (FileNotFoundException ignore) { + } catch (IOException ignore) { + } + } + return create("file:" + pathName, options); } @Implementation public static Bitmap decodeStream(InputStream is) { - return decodeStream(is, null, new BitmapFactory.Options()); + return decodeStream(is, null, null); } @Implementation public static Bitmap decodeStream(InputStream is, Rect outPadding, BitmapFactory.Options opts) { return create(is.toString().replaceFirst("stream for ", ""), opts); } - + @Implementation public static Bitmap decodeByteArray(byte[] data, int offset, int length) { return decodeByteArray( data, offset, length, new BitmapFactory.Options() ); } + /*@Implementation + public static Bitmap decodeByteArray(byte[] data, int offset, int length, BitmapFactory.Options options) { + if ((offset | length) < 0 || data.length < offset + length) { + throw new ArrayIndexOutOfBoundsException(); + } + String desc = new String(data); + if (offset != 0 && length != data.length) { + desc += " bytes " + offset + ".." + length; + } + return create(desc, options); + }*/ + @Implementation public static Bitmap decodeByteArray(byte[] data, int offset, int length, BitmapFactory.Options opts) { Checksum checksumEngine = new CRC32(); @@ -77,10 +106,12 @@ public class ShadowBitmapFactory { } static Bitmap create(String name) { - return create(name, new BitmapFactory.Options()); + return create(name, null); } public static Bitmap create(String name, BitmapFactory.Options options) { + if (options == null) options = new BitmapFactory.Options(); + Bitmap bitmap = Robolectric.newInstanceOf(Bitmap.class); ShadowBitmap shadowBitmap = shadowOf(bitmap); shadowBitmap.appendDescription("Bitmap for " + name); diff --git a/src/main/java/com/xtremelabs/robolectric/shadows/ShadowBluetoothAdapter.java b/src/main/java/com/xtremelabs/robolectric/shadows/ShadowBluetoothAdapter.java index 5932e3604..6cad80d72 100644 --- a/src/main/java/com/xtremelabs/robolectric/shadows/ShadowBluetoothAdapter.java +++ b/src/main/java/com/xtremelabs/robolectric/shadows/ShadowBluetoothAdapter.java @@ -18,6 +18,7 @@ public class ShadowBluetoothAdapter { private Set<BluetoothDevice> bondedDevices = new HashSet<BluetoothDevice>(); private boolean isDiscovering; private String address; + private boolean enabled; @Implementation public static BluetoothAdapter getDefaultAdapter() { @@ -51,6 +52,14 @@ public class ShadowBluetoothAdapter { } @Implementation + public boolean isEnabled() { + return enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + @Implementation public String getAddress() { return this.address; } diff --git a/src/main/java/com/xtremelabs/robolectric/shadows/ShadowCanvas.java b/src/main/java/com/xtremelabs/robolectric/shadows/ShadowCanvas.java index ae3405fb2..336c0bd9a 100644 --- a/src/main/java/com/xtremelabs/robolectric/shadows/ShadowCanvas.java +++ b/src/main/java/com/xtremelabs/robolectric/shadows/ShadowCanvas.java @@ -85,7 +85,7 @@ public class ShadowCanvas { int y = (int) (top + translateY); if (x != 0 || y != 0) { appendDescription(" at (" + x + "," + y + ")"); - } + } if (scaleX != 1 && scaleY != 1) { appendDescription(" scaled by (" + scaleX + "," + scaleY + ")"); diff --git a/src/main/java/com/xtremelabs/robolectric/shadows/ShadowCheckedTextView.java b/src/main/java/com/xtremelabs/robolectric/shadows/ShadowCheckedTextView.java index 2fc8141ca..c867cfc79 100644 --- a/src/main/java/com/xtremelabs/robolectric/shadows/ShadowCheckedTextView.java +++ b/src/main/java/com/xtremelabs/robolectric/shadows/ShadowCheckedTextView.java @@ -3,16 +3,17 @@ package com.xtremelabs.robolectric.shadows; import android.widget.CheckedTextView; import com.xtremelabs.robolectric.internal.Implementation; import com.xtremelabs.robolectric.internal.Implements; +import com.xtremelabs.robolectric.internal.RealObject; @SuppressWarnings({"UnusedDeclaration"}) @Implements(CheckedTextView.class) public class ShadowCheckedTextView extends ShadowTextView { - + @RealObject CheckedTextView realCheckedTextView; private boolean checked; @Implementation - public void toggle() { - checked = !checked; + public void setChecked(boolean checked) { + this.checked = checked; } @Implementation @@ -20,9 +21,14 @@ public class ShadowCheckedTextView extends ShadowTextView { return checked; } - @Implementation - public void setChecked(boolean checked) { - this.checked = checked; + @Implementation @Override + public boolean performClick() { + realCheckedTextView.toggle(); + return super.performClick(); } + @Implementation + public void toggle() { + realCheckedTextView.setChecked(!realCheckedTextView.isChecked()); + } } diff --git a/src/main/java/com/xtremelabs/robolectric/shadows/ShadowClipboardManager.java b/src/main/java/com/xtremelabs/robolectric/shadows/ShadowClipboardManager.java index de204fc25..b51ab8cfe 100644 --- a/src/main/java/com/xtremelabs/robolectric/shadows/ShadowClipboardManager.java +++ b/src/main/java/com/xtremelabs/robolectric/shadows/ShadowClipboardManager.java @@ -1,6 +1,6 @@ package com.xtremelabs.robolectric.shadows; -import android.text.ClipboardManager; +import android.content.ClipboardManager; import com.xtremelabs.robolectric.internal.Implementation; import com.xtremelabs.robolectric.internal.Implements; diff --git a/src/main/java/com/xtremelabs/robolectric/shadows/ShadowConfiguration.java b/src/main/java/com/xtremelabs/robolectric/shadows/ShadowConfiguration.java index e94e2e7b2..a26ee1ed5 100644 --- a/src/main/java/com/xtremelabs/robolectric/shadows/ShadowConfiguration.java +++ b/src/main/java/com/xtremelabs/robolectric/shadows/ShadowConfiguration.java @@ -1,13 +1,12 @@ package com.xtremelabs.robolectric.shadows; -import java.util.Locale; - import android.content.res.Configuration; - import com.xtremelabs.robolectric.internal.Implementation; import com.xtremelabs.robolectric.internal.Implements; import com.xtremelabs.robolectric.internal.RealObject; +import java.util.Locale; + @Implements(Configuration.class) public class ShadowConfiguration { @@ -18,11 +17,138 @@ public class ShadowConfiguration { public int touchscreen; public int orientation; + public void __constructor__(Configuration other) { + realConfiguration.setTo(other); + } + + @Implementation + public void setTo(Configuration o) { + // commented out lines are coming in a newer version of Android SDK + + realConfiguration.fontScale = o.fontScale; + realConfiguration.mcc = o.mcc; + realConfiguration.mnc = o.mnc; + if (o.locale != null) { + realConfiguration.locale = (Locale) o.locale.clone(); + // realConfiguration.textLayoutDirection = o.textLayoutDirection; + } + // realConfiguration.userSetLocale = o.userSetLocale; + realConfiguration.touchscreen = o.touchscreen; + realConfiguration.keyboard = o.keyboard; + realConfiguration.keyboardHidden = o.keyboardHidden; + realConfiguration.hardKeyboardHidden = o.hardKeyboardHidden; + realConfiguration.navigation = o.navigation; + realConfiguration.navigationHidden = o.navigationHidden; + realConfiguration.orientation = o.orientation; + realConfiguration.screenLayout = o.screenLayout; + realConfiguration.uiMode = o.uiMode; + realConfiguration.screenWidthDp = o.screenWidthDp; + realConfiguration.screenHeightDp = o.screenHeightDp; + realConfiguration.smallestScreenWidthDp = o.smallestScreenWidthDp; + // realConfiguration.compatScreenWidthDp = o.compatScreenWidthDp; + // realConfiguration.compatScreenHeightDp = o.compatScreenHeightDp; + // realConfiguration.compatSmallestScreenWidthDp = o.compatSmallestScreenWidthDp; + // realConfiguration.seq = o.seq; + } + + @Implementation + public int compareTo(Configuration that) { + int n; + float a = realConfiguration.fontScale; + float b = that.fontScale; + if (a < b) return -1; + if (a > b) return 1; + n = realConfiguration.mcc - that.mcc; + if (n != 0) return n; + n = realConfiguration.mnc - that.mnc; + if (n != 0) return n; + if (realConfiguration.locale == null) { + if (that.locale != null) return 1; + } else if (that.locale == null) { + return -1; + } else { + n = realConfiguration.locale.getLanguage().compareTo(that.locale.getLanguage()); + if (n != 0) return n; + n = realConfiguration.locale.getCountry().compareTo(that.locale.getCountry()); + if (n != 0) return n; + n = realConfiguration.locale.getVariant().compareTo(that.locale.getVariant()); + if (n != 0) return n; + } + n = realConfiguration.touchscreen - that.touchscreen; + if (n != 0) return n; + n = realConfiguration.keyboard - that.keyboard; + if (n != 0) return n; + n = realConfiguration.keyboardHidden - that.keyboardHidden; + if (n != 0) return n; + n = realConfiguration.hardKeyboardHidden - that.hardKeyboardHidden; + if (n != 0) return n; + n = realConfiguration.navigation - that.navigation; + if (n != 0) return n; + n = realConfiguration.navigationHidden - that.navigationHidden; + if (n != 0) return n; + n = realConfiguration.orientation - that.orientation; + if (n != 0) return n; + n = realConfiguration.screenLayout - that.screenLayout; + if (n != 0) return n; + n = realConfiguration.uiMode - that.uiMode; + if (n != 0) return n; + n = realConfiguration.screenWidthDp - that.screenWidthDp; + if (n != 0) return n; + n = realConfiguration.screenHeightDp - that.screenHeightDp; + if (n != 0) return n; + n = realConfiguration.smallestScreenWidthDp - that.smallestScreenWidthDp; + //if (n != 0) return n; + return n; + } + + @Implementation + public boolean equals(Configuration that) { + if (that == null) return false; + if (that == realConfiguration) return true; + return realConfiguration.compareTo(that) == 0; + } + + @Implementation + public boolean equals(Object that) { + try { + return equals((Configuration)that); + } catch (ClassCastException e) { + } + return false; + } + + @Implementation + public int hashCode() { + int result = 17; + result = 31 * result + Float.floatToIntBits(realConfiguration.fontScale); + result = 31 * result + realConfiguration.mcc; + result = 31 * result + realConfiguration.mnc; + result = 31 * result + (realConfiguration.locale != null ? realConfiguration.locale.hashCode() : 0); + result = 31 * result + touchscreen; + result = 31 * result + realConfiguration.keyboard; + result = 31 * result + realConfiguration.keyboardHidden; + result = 31 * result + realConfiguration.hardKeyboardHidden; + result = 31 * result + realConfiguration.navigation; + result = 31 * result + realConfiguration.navigationHidden; + result = 31 * result + orientation; + result = 31 * result + screenLayout; + result = 31 * result + realConfiguration.uiMode; + result = 31 * result + realConfiguration.screenWidthDp; + result = 31 * result + realConfiguration.screenHeightDp; + result = 31 * result + realConfiguration.smallestScreenWidthDp; + return result; + } + @Implementation public void setToDefaults() { realConfiguration.screenLayout = Configuration.SCREENLAYOUT_LONG_NO | Configuration.SCREENLAYOUT_SIZE_NORMAL; } + + @Implementation + public String toString() { + return realConfiguration.toString(); + } public void setLocale( Locale l ) { realConfiguration.locale = l; diff --git a/src/main/java/com/xtremelabs/robolectric/shadows/ShadowContentProviderOperationBuilder.java b/src/main/java/com/xtremelabs/robolectric/shadows/ShadowContentProviderOperationBuilder.java index 2f69a8cd9..917039ee6 100644 --- a/src/main/java/com/xtremelabs/robolectric/shadows/ShadowContentProviderOperationBuilder.java +++ b/src/main/java/com/xtremelabs/robolectric/shadows/ShadowContentProviderOperationBuilder.java @@ -1,23 +1,22 @@ package com.xtremelabs.robolectric.shadows; -import java.util.Map; - import android.content.ContentProviderOperation; import android.content.ContentProviderOperation.Builder; import android.net.Uri; - import com.xtremelabs.robolectric.Robolectric; import com.xtremelabs.robolectric.internal.Implementation; import com.xtremelabs.robolectric.internal.Implements; import com.xtremelabs.robolectric.internal.RealObject; +import java.util.Map; + @Implements(ContentProviderOperation.Builder.class) public class ShadowContentProviderOperationBuilder { @RealObject private Builder realBuilder; private ContentProviderOperation contentProviderOperation; private ShadowContentProviderOperation shadowContentProviderOperation; - public void __constructor__() { + public ShadowContentProviderOperationBuilder() { contentProviderOperation = Robolectric.newInstanceOf(ContentProviderOperation.class); shadowContentProviderOperation = Robolectric.shadowOf(contentProviderOperation); } diff --git a/src/main/java/com/xtremelabs/robolectric/shadows/ShadowContentResolver.java b/src/main/java/com/xtremelabs/robolectric/shadows/ShadowContentResolver.java index 8c5e4bde2..6f685a18b 100644 --- a/src/main/java/com/xtremelabs/robolectric/shadows/ShadowContentResolver.java +++ b/src/main/java/com/xtremelabs/robolectric/shadows/ShadowContentResolver.java @@ -6,6 +6,7 @@ import android.database.ContentObserver; import android.database.Cursor; import android.net.Uri; import android.os.Bundle; +import com.xtremelabs.robolectric.Robolectric; import com.xtremelabs.robolectric.internal.Implementation; import com.xtremelabs.robolectric.internal.Implements; import com.xtremelabs.robolectric.tester.android.database.TestCursor; @@ -62,6 +63,16 @@ public class ShadowContentResolver { @Implementation public final InputStream openInputStream(final Uri uri) { + + if (uri != null && ContentResolver.SCHEME_ANDROID_RESOURCE.equals(uri.getScheme())) { + String path = uri.getPath(); + // check that path is a numerical resource id + if (path != null && path.matches("/[0-9]+")) { + int resourceId = Integer.parseInt(path.substring(1)); + return Robolectric.application.getResources().openRawResource(resourceId); + } + } + return new InputStream() { @Override public int read() throws IOException { diff --git a/src/main/java/com/xtremelabs/robolectric/shadows/ShadowContext.java b/src/main/java/com/xtremelabs/robolectric/shadows/ShadowContext.java index 03da28612..7d648f199 100644 --- a/src/main/java/com/xtremelabs/robolectric/shadows/ShadowContext.java +++ b/src/main/java/com/xtremelabs/robolectric/shadows/ShadowContext.java @@ -11,11 +11,8 @@ import com.xtremelabs.robolectric.internal.Implements; import com.xtremelabs.robolectric.internal.RealObject; import com.xtremelabs.robolectric.res.ResourceLoader; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; +import java.io.*; +import java.util.UUID; import static com.xtremelabs.robolectric.Robolectric.shadowOf; @@ -81,7 +78,7 @@ abstract public class ShadowContext { if( set == null ){ return getTheme().obtainStyledAttributes( attrs ); } - + ShadowTypedArray styledAttributes = Robolectric.shadowOf(result); for(int attr : attrs) { styledAttributes.add(set.getAttributeValue(attr)); @@ -190,6 +187,7 @@ abstract public class ShadowContext { try { File tmp = File.createTempFile(name, "robolectric"); if (!tmp.delete()) throw new IOException("could not delete "+tmp); + tmp = new File(tmp, UUID.randomUUID().toString()); if (!tmp.mkdirs()) throw new IOException("could not create "+tmp); tmp.deleteOnExit(); diff --git a/src/main/java/com/xtremelabs/robolectric/shadows/ShadowContextWrapper.java b/src/main/java/com/xtremelabs/robolectric/shadows/ShadowContextWrapper.java index 9724e6b41..54f5332a6 100644 --- a/src/main/java/com/xtremelabs/robolectric/shadows/ShadowContextWrapper.java +++ b/src/main/java/com/xtremelabs/robolectric/shadows/ShadowContextWrapper.java @@ -1,24 +1,19 @@ package com.xtremelabs.robolectric.shadows; -import android.content.BroadcastReceiver; -import android.content.ComponentName; -import android.content.ContentResolver; -import android.content.Context; -import android.content.ContextWrapper; -import android.content.Intent; -import android.content.IntentFilter; -import android.content.ServiceConnection; -import android.content.SharedPreferences; +import android.content.*; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.res.AssetManager; import android.content.res.Resources; import android.os.Looper; +import com.xtremelabs.robolectric.RobolectricConfig; import com.xtremelabs.robolectric.internal.Implementation; import com.xtremelabs.robolectric.internal.Implements; import com.xtremelabs.robolectric.internal.RealObject; +import com.xtremelabs.robolectric.res.RobolectricPackageManager; import com.xtremelabs.robolectric.tester.android.content.TestSharedPreferences; +import java.io.File; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -122,7 +117,14 @@ public class ShadowContextWrapper extends ShadowContext { */ @Implementation public PackageManager getPackageManager() { - return realContextWrapper == getApplicationContext() ? packageManager : getApplicationContext().getPackageManager(); + return realContextWrapper == getApplicationContext() ? requirePackageManager() : getApplicationContext().getPackageManager(); + } + + private PackageManager requirePackageManager() { + if (packageManager == null) { + packageManager = new RobolectricPackageManager(realContextWrapper, new RobolectricConfig(new File("."))); + } + return packageManager; } @Implementation diff --git a/src/main/java/com/xtremelabs/robolectric/shadows/ShadowDateFormat.java b/src/main/java/com/xtremelabs/robolectric/shadows/ShadowDateFormat.java index 7a5a42ec2..4a00bb35d 100644 --- a/src/main/java/com/xtremelabs/robolectric/shadows/ShadowDateFormat.java +++ b/src/main/java/com/xtremelabs/robolectric/shadows/ShadowDateFormat.java @@ -6,11 +6,18 @@ import android.text.format.DateFormat; import com.xtremelabs.robolectric.internal.Implementation; import com.xtremelabs.robolectric.internal.Implements; +import java.util.Locale; + @Implements(DateFormat.class) public class ShadowDateFormat { @Implementation - public final static java.text.DateFormat getDateFormat(Context context) { - return java.text.DateFormat.getDateInstance(); + public static java.text.DateFormat getDateFormat(Context context) { + return new java.text.SimpleDateFormat("MMM-DD-yyyy", Locale.ROOT); + } + + @Implementation + public static java.text.DateFormat getTimeFormat(Context context) { + return new java.text.SimpleDateFormat("HH:mm:ss", Locale.ROOT); } } diff --git a/src/main/java/com/xtremelabs/robolectric/shadows/ShadowDefaultRequestDirector.java b/src/main/java/com/xtremelabs/robolectric/shadows/ShadowDefaultRequestDirector.java index 2b54e94c9..ac1e8df44 100644 --- a/src/main/java/com/xtremelabs/robolectric/shadows/ShadowDefaultRequestDirector.java +++ b/src/main/java/com/xtremelabs/robolectric/shadows/ShadowDefaultRequestDirector.java @@ -126,6 +126,9 @@ public class ShadowDefaultRequestDirector { params); } + /** + * @deprecated Use {@link Robolectric#getSentHttpRequestInfo(int)} instead.) + */ public static HttpRequest getSentHttpRequest(int index) { return getSentHttpRequestInfo(index).getHttpRequest(); } @@ -139,6 +142,9 @@ public class ShadowDefaultRequestDirector { return Robolectric.getFakeHttpLayer().getSentHttpRequestInfo(requestCount - 1); } + /** + * @deprecated Use {@link Robolectric#getSentHttpRequest(int)} instead.) + */ public static HttpRequestInfo getSentHttpRequestInfo(int index) { return Robolectric.getFakeHttpLayer().getSentHttpRequestInfo(index); } @@ -206,4 +212,4 @@ public class ShadowDefaultRequestDirector { public HttpParams getHttpParams() { return httpParams; } -}
\ No newline at end of file +} diff --git a/src/main/java/com/xtremelabs/robolectric/shadows/ShadowDialog.java b/src/main/java/com/xtremelabs/robolectric/shadows/ShadowDialog.java index 0f317a290..ccbcd58bc 100644 --- a/src/main/java/com/xtremelabs/robolectric/shadows/ShadowDialog.java +++ b/src/main/java/com/xtremelabs/robolectric/shadows/ShadowDialog.java @@ -14,7 +14,6 @@ import com.xtremelabs.robolectric.internal.Implementation; import com.xtremelabs.robolectric.internal.Implements; import com.xtremelabs.robolectric.internal.RealObject; import com.xtremelabs.robolectric.tester.android.view.TestWindow; - import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; @@ -149,12 +148,11 @@ public class ShadowDialog { @Implementation public View findViewById(int viewId) { - if (inflatedView != null) { - return inflatedView.findViewById(viewId); - } - if (layoutId > 0 && context != null) { - inflatedView = ShadowLayoutInflater.from(context).inflate(layoutId, null); - return inflatedView.findViewById(viewId); + if (context != null) { + if (inflatedView == null && layoutId > 0) { + inflatedView = ShadowLayoutInflater.from(context).inflate(layoutId, null); + } + if (inflatedView != null) return inflatedView.findViewById(viewId); } return null; } @@ -211,7 +209,6 @@ public class ShadowDialog { return window; } - @Implementation public LayoutInflater getLayoutInflater() { return LayoutInflater.from(realDialog.getContext()); @@ -243,6 +240,12 @@ public class ShadowDialog { } } + public void clickOnText(String text) { + if (!clickOnText(inflatedView, text)) { + throw new IllegalArgumentException("Text not found: " + text); + } + } + private boolean clickOnText(View view, String text) { if (text.equals(shadowOf(view).innerText())) { view.performClick(); diff --git a/src/main/java/com/xtremelabs/robolectric/shadows/ShadowDialogFragment.java b/src/main/java/com/xtremelabs/robolectric/shadows/ShadowDialogFragment.java index af245e794..2e99886cf 100644 --- a/src/main/java/com/xtremelabs/robolectric/shadows/ShadowDialogFragment.java +++ b/src/main/java/com/xtremelabs/robolectric/shadows/ShadowDialogFragment.java @@ -38,10 +38,10 @@ public class ShadowDialogFragment extends ShadowFragment { shadowOf(realDialogFragment).setActivity(activityFromManager); - realDialogFragment.onAttach(activity); + realDialogFragment.onAttach(fragmentActivity); realDialogFragment.onCreate(null); dialog = realDialogFragment.onCreateDialog(null); - view = realDialogFragment.onCreateView(ShadowLayoutInflater.from(activity), null, null); + view = realDialogFragment.onCreateView(ShadowLayoutInflater.from(fragmentActivity), null, null); if (dialog == null) { dialog = new Dialog(activityFromManager); dialog.setContentView(view); diff --git a/src/main/java/com/xtremelabs/robolectric/shadows/ShadowDisplay.java b/src/main/java/com/xtremelabs/robolectric/shadows/ShadowDisplay.java index fe0914fcb..dabf874cf 100644 --- a/src/main/java/com/xtremelabs/robolectric/shadows/ShadowDisplay.java +++ b/src/main/java/com/xtremelabs/robolectric/shadows/ShadowDisplay.java @@ -57,6 +57,11 @@ public class ShadowDisplay { } @Implementation + public int getOrientation() { + return getRotation(); + } + + @Implementation public int getRotation() { return rotation; } diff --git a/src/main/java/com/xtremelabs/robolectric/shadows/ShadowDownloadManager.java b/src/main/java/com/xtremelabs/robolectric/shadows/ShadowDownloadManager.java new file mode 100644 index 000000000..4f21db649 --- /dev/null +++ b/src/main/java/com/xtremelabs/robolectric/shadows/ShadowDownloadManager.java @@ -0,0 +1,223 @@ +package com.xtremelabs.robolectric.shadows; + +import android.app.DownloadManager; +import android.database.Cursor; +import com.xtremelabs.robolectric.internal.Implementation; +import com.xtremelabs.robolectric.internal.Implements; +import com.xtremelabs.robolectric.internal.RealObject; +import com.xtremelabs.robolectric.tester.android.database.TestCursor; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static com.xtremelabs.robolectric.Robolectric.shadowOf_; + +/** + * Shadows Androids DownloadManager + */ +@Implements(DownloadManager.class) +public class ShadowDownloadManager { + + private Map<Long, DownloadManager.Request> requestMap = new HashMap<Long, DownloadManager.Request>(); + private long queueCounter = -1; // First request starts at 0 just like in the real DownloadManager. + + @Implementation + public long enqueue(DownloadManager.Request request) { + queueCounter++; + requestMap.put(queueCounter, request); + + return queueCounter; + } + + @Implementation + public int remove(long... ids) { + int removeCount = 0; + for (long id : ids) { + if (requestMap.remove(id) != null) { + removeCount++; + } + } + return removeCount; + } + + @Implementation + public Cursor query(DownloadManager.Query query) { + ShadowQuery shadow = shadowOf_(query); + long[] ids = shadow.getIds(); + + ResultCursor result = new ResultCursor(); + for (long id : ids) { + DownloadManager.Request request = requestMap.get(id); + if (request != null) { + result.requests.add(request); + } + } + + return result; + } + + public DownloadManager.Request getRequest(long id) { + return requestMap.get(id); + } + + public int getRequestCount() { + return requestMap.size(); + } + + @Implements(DownloadManager.Request.class) + public static class ShadowRequest { + @RealObject + DownloadManager.Request realObject; + + private CharSequence description; + private int status; + + @Implementation + public DownloadManager.Request setAllowedNetworkTypes(int flags) { + return realObject; + } + + @Implementation + public DownloadManager.Request setMimeType(String mimeType) { + return realObject; + } + + @Implementation + public DownloadManager.Request setTitle(CharSequence title) { + return realObject; + } + + @Implementation + public DownloadManager.Request setDescription(CharSequence description) { + this.description = description; + return realObject; + } + + @Implementation + public DownloadManager.Request setAllowedOverRoaming(boolean allowed) { + return realObject; + } + + @Implementation + public DownloadManager.Request setDestinationInExternalPublicDir(String dirType, String subPath) { + return realObject; + } + + public CharSequence getDescription() { + return this.description; + } + + public void setStatus(int status) { + this.status = status; + } + + public int getStatus() { + return this.status; + } + } + + @Implements(DownloadManager.Query.class) + public static class ShadowQuery { + + @RealObject + private DownloadManager.Query realObject; + + private long[] ids = null; + + @Implementation + public DownloadManager.Query setFilterById(long... ids) { + this.ids = ids; + return realObject; + } + + public long[] getIds() { + return this.ids; + } + } + + private class ResultCursor extends TestCursor { + + private static final int COLUMN_INDEX_LOCAL_FILENAME = 0; + private static final int COLUMN_INDEX_DESCRIPTION = 1; + private static final int COLUMN_INDEX_REASON = 2; + private static final int COLUMN_INDEX_STATUS = 3; + + public List<DownloadManager.Request> requests = new ArrayList<DownloadManager.Request>(); + private int positionIndex; + private boolean closed; + + @Override + public int getCount() { + checkClosed(); + return requests.size(); + } + + @Override + public boolean moveToFirst() { + checkClosed(); + positionIndex = 0; + return !requests.isEmpty(); + } + + @Override + public int getColumnIndex(String columnName) { + checkClosed(); + + if (DownloadManager.COLUMN_LOCAL_FILENAME.equals(columnName)) { + return COLUMN_INDEX_LOCAL_FILENAME; + } else if (DownloadManager.COLUMN_DESCRIPTION.equals(columnName)) { + return COLUMN_INDEX_DESCRIPTION; + } else if (DownloadManager.COLUMN_REASON.equals(columnName)) { + return COLUMN_INDEX_REASON; + } else if (DownloadManager.COLUMN_STATUS.equals(columnName)) { + return COLUMN_INDEX_STATUS; + } + + return 0; + } + + @Override + public void close() { + this.closed = true; + } + + @Override + public boolean isClosed() { + return closed; + } + + @Override + public String getString(int columnIndex) { + checkClosed(); + ShadowRequest request = shadowOf_(requests.get(positionIndex)); + switch (columnIndex) { + case COLUMN_INDEX_LOCAL_FILENAME: + return "local file name not implemented"; + case COLUMN_INDEX_DESCRIPTION: + return request.getDescription().toString(); + case COLUMN_INDEX_REASON: + return "reason not implemented"; + } + + return "Unknown ColumnIndex " + columnIndex; + } + + @Override + public int getInt(int columnIndex) { + checkClosed(); + ShadowRequest request = shadowOf_(requests.get(positionIndex)); + if (columnIndex == COLUMN_INDEX_STATUS) { + return request.getStatus(); + } + return 0; + } + + private void checkClosed() { + if (closed) { + throw new IllegalStateException("Cursor is already closed."); + } + } + } +}
\ No newline at end of file diff --git a/src/main/java/com/xtremelabs/robolectric/shadows/ShadowEnvironment.java b/src/main/java/com/xtremelabs/robolectric/shadows/ShadowEnvironment.java index 27d49b7ab..d938b1673 100644 --- a/src/main/java/com/xtremelabs/robolectric/shadows/ShadowEnvironment.java +++ b/src/main/java/com/xtremelabs/robolectric/shadows/ShadowEnvironment.java @@ -1,11 +1,11 @@ package com.xtremelabs.robolectric.shadows; -import java.io.File; - import android.os.Environment; import com.xtremelabs.robolectric.internal.Implementation; import com.xtremelabs.robolectric.internal.Implements; +import java.io.File; + @Implements(Environment.class) public class ShadowEnvironment { @@ -27,7 +27,7 @@ public class ShadowEnvironment { ShadowContext.EXTERNAL_CACHE_DIR.mkdirs(); return ShadowContext.EXTERNAL_CACHE_DIR; } - + @Implementation public static File getExternalStoragePublicDirectory(String type) { File f = (type == null) ? ShadowContext.EXTERNAL_FILES_DIR : new File( ShadowContext.EXTERNAL_FILES_DIR, type ); diff --git a/src/main/java/com/xtremelabs/robolectric/shadows/ShadowFragment.java b/src/main/java/com/xtremelabs/robolectric/shadows/ShadowFragment.java index e70f70b78..f39a03052 100644 --- a/src/main/java/com/xtremelabs/robolectric/shadows/ShadowFragment.java +++ b/src/main/java/com/xtremelabs/robolectric/shadows/ShadowFragment.java @@ -9,11 +9,16 @@ import android.support.v4.app.FragmentManager; import android.view.View; import com.xtremelabs.robolectric.internal.Implementation; import com.xtremelabs.robolectric.internal.Implements; +import com.xtremelabs.robolectric.internal.RealObject; + +import java.lang.reflect.Field; @Implements(Fragment.class) public class ShadowFragment { + @RealObject Fragment realFragment; + protected View view; - protected FragmentActivity activity; + protected FragmentActivity fragmentActivity; private String tag; private Bundle savedInstanceState; private int containerViewId; @@ -21,12 +26,26 @@ public class ShadowFragment { private Bundle arguments; private boolean attached; + private int fragmentId; + + private Fragment targetFragment; + private boolean resumed; + public void setView(View view) { this.view = view; } public void setActivity(FragmentActivity activity) { - this.activity = activity; + if (fragmentActivity != null) realFragment.onDetach(); + fragmentActivity = activity; + if (activity != null) realFragment.onAttach(activity); + try { + Field field = Fragment.class.getDeclaredField("mActivity"); + field.setAccessible(true); + field.set(realFragment, activity); + } catch (Exception e) { + throw new IllegalArgumentException("Unable to set mActivity field"); + } } @Implementation @@ -36,22 +55,28 @@ public class ShadowFragment { @Implementation public FragmentActivity getActivity() { - return activity; + return fragmentActivity; } @Implementation public void startActivity(Intent intent) { - new FragmentActivity().startActivity(intent); + if (fragmentActivity == null) { + throw new IllegalStateException("Fragment " + this + " not attached to Activity"); + } + fragmentActivity.startActivity(intent); } @Implementation public void startActivityForResult(Intent intent, int requestCode) { - activity.startActivityForResult(intent, requestCode); + if (fragmentActivity == null) { + throw new IllegalStateException("Fragment " + this + " not attached to Activity"); + } + fragmentActivity.startActivityForResult(intent, requestCode); } @Implementation final public FragmentManager getFragmentManager() { - return activity.getSupportFragmentManager(); + return fragmentActivity.getSupportFragmentManager(); } @Implementation @@ -61,17 +86,14 @@ public class ShadowFragment { @Implementation public Resources getResources() { - if (activity == null) { + if (fragmentActivity == null) { throw new IllegalStateException("Fragment " + this + " not attached to Activity"); } - return activity.getResources(); + return fragmentActivity.getResources(); } @Implementation public String getString(int id) { - if (activity == null) { - throw new IllegalStateException("Fragment " + this + " not attached to Activity"); - } return getResources().getString(id); } @@ -120,4 +142,72 @@ public class ShadowFragment { public boolean isAttached() { return attached; } + + @Implementation + public final CharSequence getText(int resId) { + return getResources().getText(resId); + } + + @Implementation + public final String getString(int resId, Object... formatArgs) { + return getResources().getString(resId, formatArgs); + } + + @Implementation + public int getId() { + return fragmentId; + } + + @Implementation + public boolean isAdded() { + return fragmentActivity != null; + } + + @Implementation + public boolean isVisible() { + return fragmentActivity != null; + } + + @Implementation + public Fragment getTargetFragment() { + return targetFragment; + } + + @Implementation + public void setTargetFragment(Fragment targetFragment, int requestCode) { + this.targetFragment = targetFragment; + } + + @Implementation + public void onResume() { + this.resumed = true; + } + + public void resume() { + realFragment.onResume(); + } + + @Implementation + public void onPause() { + this.resumed = false; + } + + public void pause() { + realFragment.onPause(); + } + + @Implementation + public boolean isResumed() { + return resumed; + } + + public void createView() { + final FragmentActivity activity = getActivity(); + view = realFragment.onCreateView(activity.getLayoutInflater(), null, null); + realFragment.onViewCreated(view, null); + } + + public void setFragmentId(int fragmentId) { + this.fragmentId = fragmentId; + } } diff --git a/src/main/java/com/xtremelabs/robolectric/shadows/ShadowFrameLayout.java b/src/main/java/com/xtremelabs/robolectric/shadows/ShadowFrameLayout.java index d96ca345f..0b57a3393 100644 --- a/src/main/java/com/xtremelabs/robolectric/shadows/ShadowFrameLayout.java +++ b/src/main/java/com/xtremelabs/robolectric/shadows/ShadowFrameLayout.java @@ -2,10 +2,10 @@ package com.xtremelabs.robolectric.shadows; import android.content.Context; import android.util.AttributeSet; +import android.view.View; import android.view.View.MeasureSpec; import android.view.ViewGroup; import android.widget.FrameLayout; - import com.xtremelabs.robolectric.internal.Implementation; import com.xtremelabs.robolectric.internal.Implements; @@ -15,6 +15,7 @@ import com.xtremelabs.robolectric.internal.Implements; @SuppressWarnings("UnusedDeclaration") @Implements(FrameLayout.class) public class ShadowFrameLayout extends ShadowViewGroup { + private FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(0, 0); public void __constructor__(Context context, AttributeSet attributeSet, int defStyle) { setLayoutParams(new ViewGroup.MarginLayoutParams(0, 0)); @@ -27,5 +28,16 @@ public class ShadowFrameLayout extends ShadowViewGroup { int height = MeasureSpec.getSize(heightMeasureSpec); layout(right, top, right + width, top + height); - } + } + + @Implementation + @Override + public ViewGroup.LayoutParams getLayoutParams() { + return layoutParams; + } + + @Override + protected void setChildLayoutParams(View child) { + child.setLayoutParams(new FrameLayout.LayoutParams(0, 0)); + } } diff --git a/src/main/java/com/xtremelabs/robolectric/shadows/ShadowImageButton.java b/src/main/java/com/xtremelabs/robolectric/shadows/ShadowImageButton.java new file mode 100644 index 000000000..5dce34a47 --- /dev/null +++ b/src/main/java/com/xtremelabs/robolectric/shadows/ShadowImageButton.java @@ -0,0 +1,15 @@ +package com.xtremelabs.robolectric.shadows; + +import android.widget.ImageButton; +import com.xtremelabs.robolectric.internal.Implements; + +@Implements(ImageButton.class) +public class ShadowImageButton extends ShadowImageView { + @Override + public void applyAttributes() { + super.applyAttributes(); + if (getBackground() == null) { + setBackgroundColor(android.R.color.transparent); + } + } +} diff --git a/src/main/java/com/xtremelabs/robolectric/shadows/ShadowImageView.java b/src/main/java/com/xtremelabs/robolectric/shadows/ShadowImageView.java index dfa685b8b..16360ddee 100644 --- a/src/main/java/com/xtremelabs/robolectric/shadows/ShadowImageView.java +++ b/src/main/java/com/xtremelabs/robolectric/shadows/ShadowImageView.java @@ -19,7 +19,6 @@ import static com.xtremelabs.robolectric.Robolectric.shadowOf; @Implements(ImageView.class) public class ShadowImageView extends ShadowView { private Drawable imageDrawable; - private int alpha; private int resourceId; private Bitmap imageBitmap; private ImageView.ScaleType scaleType; @@ -92,17 +91,12 @@ public class ShadowImageView extends ShadowView { * @param resourceId Resource id * @return Boolean */ - private boolean isDrawableXml(int resourceId) { + protected boolean isDrawableXml(int resourceId) { return shadowOf(Robolectric.application).getResourceLoader() .isDrawableXml(resourceId); } @Implementation - public void setAlpha(int alpha) { - this.alpha = alpha; - } - - @Implementation public ImageView.ScaleType getScaleType() { return scaleType; } @@ -144,7 +138,9 @@ public class ShadowImageView extends ShadowView { canvas.scale(shadowOf(matrix).getScaleX(), shadowOf(matrix) .getScaleY()); } - imageDrawable.draw(canvas); + if (imageDrawable != null) { + imageDrawable.draw(canvas); + } } private void applyImageAttribute() { diff --git a/src/main/java/com/xtremelabs/robolectric/shadows/ShadowLinearLayout.java b/src/main/java/com/xtremelabs/robolectric/shadows/ShadowLinearLayout.java index 2a4ab572d..bedf86bda 100644 --- a/src/main/java/com/xtremelabs/robolectric/shadows/ShadowLinearLayout.java +++ b/src/main/java/com/xtremelabs/robolectric/shadows/ShadowLinearLayout.java @@ -1,13 +1,18 @@ package com.xtremelabs.robolectric.shadows; +import android.view.View; import android.widget.LinearLayout; import com.xtremelabs.robolectric.internal.Implements; @Implements(LinearLayout.class) public class ShadowLinearLayout extends ShadowViewGroup { - public ShadowLinearLayout() { setLayoutParams(new LinearLayout.LayoutParams(0, 0)); } + @Override + protected void setChildLayoutParams(View child) { + child.setLayoutParams(new LinearLayout.LayoutParams(0, 0)); + } + } diff --git a/src/main/java/com/xtremelabs/robolectric/shadows/ShadowListActivity.java b/src/main/java/com/xtremelabs/robolectric/shadows/ShadowListActivity.java index f57ab6ec4..c2fc1a02c 100644 --- a/src/main/java/com/xtremelabs/robolectric/shadows/ShadowListActivity.java +++ b/src/main/java/com/xtremelabs/robolectric/shadows/ShadowListActivity.java @@ -2,7 +2,6 @@ package com.xtremelabs.robolectric.shadows; import android.app.ListActivity; import android.view.View; -import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.ListAdapter; import android.widget.ListView; @@ -64,18 +63,15 @@ public class ShadowListActivity extends ShadowActivity { return listAdapter; } + /** + * Copied wholesale from Android ListActivity source. + */ private ListView findListView(View parent) { - if (parent instanceof ListView) { - return (ListView) parent; - } else if (parent instanceof ViewGroup) { - ViewGroup viewGroup = (ViewGroup) parent; - for (int i = 0; i < viewGroup.getChildCount(); i++) { - ListView listView = findListView(viewGroup.getChildAt(i)); - if (listView != null) { - return listView; - } - } + listView = (ListView)findViewById(android.R.id.list); + if (listView == null) { + throw new RuntimeException("Your content must have a ListView whose id attribute is 'android.R.id.list'"); } - return null; + + return listView; } } diff --git a/src/main/java/com/xtremelabs/robolectric/shadows/ShadowLocalActivityManager.java b/src/main/java/com/xtremelabs/robolectric/shadows/ShadowLocalActivityManager.java new file mode 100644 index 000000000..5a72ce4e6 --- /dev/null +++ b/src/main/java/com/xtremelabs/robolectric/shadows/ShadowLocalActivityManager.java @@ -0,0 +1,36 @@ +package com.xtremelabs.robolectric.shadows; + +import android.app.Activity; +import android.app.LocalActivityManager; +import android.content.Intent; +import android.os.Bundle; +import android.view.Window; +import com.xtremelabs.robolectric.internal.Implementation; +import com.xtremelabs.robolectric.internal.Implements; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; + +/** + * + */ +@Implements(LocalActivityManager.class) +public class ShadowLocalActivityManager { + + @Implementation + public Window startActivity(String id, Intent intent) { + try { + final String clazz = intent.getComponent().getClassName(); + final Class<? extends Activity> aClass = (Class<? extends Activity>) Class.forName(clazz); + final Constructor<? extends Activity> ctor = aClass.getConstructor(); + Activity activity = ctor.newInstance(); + final Method onCreateMethod = aClass.getDeclaredMethod("onCreate", Bundle.class); + onCreateMethod.setAccessible(true); + onCreateMethod.invoke(activity, (Bundle) null); + return activity.getWindow(); + } catch (Exception e) { + throw new RuntimeException("Unable to create class", e); + } + } + +} diff --git a/src/main/java/com/xtremelabs/robolectric/shadows/ShadowLocation.java b/src/main/java/com/xtremelabs/robolectric/shadows/ShadowLocation.java index 07a48126c..81b5b8a51 100644 --- a/src/main/java/com/xtremelabs/robolectric/shadows/ShadowLocation.java +++ b/src/main/java/com/xtremelabs/robolectric/shadows/ShadowLocation.java @@ -341,8 +341,16 @@ public class ShadowLocation { } } + private static float[] distanceBetween; + + public static void setDistanceBetween(float[] distanceBetween) { + ShadowLocation.distanceBetween = distanceBetween; + } + /** - * Computes the approximate distance in meters between two + * If it is non-null, returns the mock distance last set with + * {@link #setDistanceBetween}. + * Otherwise computes the approximate distance in meters between two * locations, and optionally the initial and final bearings of the * shortest path between them. Distance and bearing are defined using the * WGS84 ellipsoid. @@ -362,6 +370,11 @@ public class ShadowLocation { @Implementation public static void distanceBetween(double startLatitude, double startLongitude, double endLatitude, double endLongitude, float[] results) { + if (distanceBetween != null && results.length == distanceBetween.length){ + System.arraycopy(distanceBetween, 0, results, 0, results.length); + return; + } + if (results == null || results.length < 1) { throw new IllegalArgumentException("results is null or has length < 1"); } diff --git a/src/main/java/com/xtremelabs/robolectric/shadows/ShadowLocationManager.java b/src/main/java/com/xtremelabs/robolectric/shadows/ShadowLocationManager.java index 4c4fdf509..088647f69 100644 --- a/src/main/java/com/xtremelabs/robolectric/shadows/ShadowLocationManager.java +++ b/src/main/java/com/xtremelabs/robolectric/shadows/ShadowLocationManager.java @@ -30,7 +30,30 @@ public class ShadowLocationManager { private Criteria lastBestProviderCriteria; private boolean lastBestProviderEnabled; private String bestEnabledProvider, bestDisabledProvider; - private final Map<LocationListener, Set<String>> requestLocationUpdateListenersMap = new LinkedHashMap<LocationListener, Set<String>>(); + + /** Location listeners along with metadata on when they should be fired. */ + private static final class ListenerRegistration { + final long minTime; + final float minDistance; + final LocationListener listener; + final String provider; + Location lastSeenLocation; + long lastSeenTime; + + ListenerRegistration(String provider, long minTime, float minDistance, Location locationAtCreation, + LocationListener listener) { + this.provider = provider; + this.minTime = minTime; + this.minDistance = minDistance; + this.lastSeenTime = locationAtCreation == null ? 0 : locationAtCreation.getTime(); + this.lastSeenLocation = locationAtCreation; + this.listener = listener; + } + } + + /** Mapped by provider. */ + private final Map<String, List<ListenerRegistration>> locationListeners = + new HashMap<String, List<ListenerRegistration>>(); @Implementation public boolean isProviderEnabled(String provider) { @@ -72,7 +95,7 @@ public class ShadowLocationManager { providerEntry.enabled = isEnabled; providerEntry.criteria = criteria; providersEnabled.put(provider, providerEntry); - List<LocationListener> locationUpdateListeners = new ArrayList<LocationListener>(requestLocationUpdateListenersMap.keySet()); + List<LocationListener> locationUpdateListeners = new ArrayList<LocationListener>(getRequestLocationUpdateListeners()); for (LocationListener locationUpdateListener : locationUpdateListeners) { if (isEnabled) { locationUpdateListener.onProviderEnabled(provider); @@ -204,20 +227,24 @@ public class ShadowLocationManager { @Implementation public void requestLocationUpdates(String provider, long minTime, float minDistance, LocationListener listener) { - addLocationListener(provider, listener); + addLocationListener(provider, listener, minTime, minDistance); } - private void addLocationListener(String provider, LocationListener listener) { - if (!requestLocationUpdateListenersMap.containsKey(listener)) { - requestLocationUpdateListenersMap.put(listener, new HashSet<String>()); + private void addLocationListener(String provider, LocationListener listener, long minTime, float minDistance) { + List<ListenerRegistration> providerListeners = locationListeners.get(provider); + if (providerListeners == null) { + providerListeners = new ArrayList<ListenerRegistration>(); + locationListeners.put(provider, providerListeners); } - requestLocationUpdateListenersMap.get(listener).add(provider); + providerListeners.add(new ListenerRegistration(provider, + minTime, minDistance, copyOf(getLastKnownLocation(provider)), listener)); + } @Implementation public void requestLocationUpdates(String provider, long minTime, float minDistance, LocationListener listener, Looper looper) { - addLocationListener(provider, listener); + addLocationListener(provider, listener, minTime, minDistance); } @Implementation @@ -246,7 +273,12 @@ public class ShadowLocationManager { @Implementation public void removeUpdates(LocationListener listener) { - requestLocationUpdateListenersMap.remove(listener); + for (Map.Entry<String, List<ListenerRegistration>> entry : locationListeners.entrySet()) { + List<ListenerRegistration> listenerRegistrations = entry.getValue(); + for (int i = listenerRegistrations.size() - 1; i >= 0; i--) { + if (listenerRegistrations.get(i).listener.equals(listener)) listenerRegistrations.remove(i); + } + } } @Implementation @@ -350,7 +382,68 @@ public class ShadowLocationManager { * @return lastRequestedLocationUpdatesLocationListener */ public List<LocationListener> getRequestLocationUpdateListeners() { - return new ArrayList<LocationListener>(requestLocationUpdateListenersMap.keySet()); + List<LocationListener> all = new ArrayList<LocationListener>(); + for (Map.Entry<String, List<ListenerRegistration>> entry : locationListeners.entrySet()) { + for (ListenerRegistration reg : entry.getValue()) { + all.add(reg.listener); + } + } + + return all; + } + + public void simulateLocation(Location location) { + setLastKnownLocation(location.getProvider(), location); + + List<ListenerRegistration> providerListeners = locationListeners.get( + location.getProvider()); + if (providerListeners == null) return; + + for (ListenerRegistration listenerReg : providerListeners) { + if(listenerReg.lastSeenLocation != null && location != null) { + float distanceChange = distanceBetween(location, listenerReg.lastSeenLocation); + boolean withinMinDistance = distanceChange < listenerReg.minDistance; + boolean exceededMinTime = location.getTime() - listenerReg.lastSeenTime > listenerReg.minTime; + if (withinMinDistance && !exceededMinTime) continue; + } + listenerReg.lastSeenLocation = copyOf(location); + listenerReg.lastSeenTime = location == null ? 0 : location.getTime(); + listenerReg.listener.onLocationChanged(copyOf(location)); + } + } + + private Location copyOf(Location location) { + if (location == null) return null; + Location copy = new Location(location); + copy.setAccuracy(location.getAccuracy()); + copy.setAltitude(location.getAltitude()); + copy.setBearing(location.getBearing()); + copy.setExtras(location.getExtras()); + copy.setLatitude(location.getLatitude()); + copy.setLongitude(location.getLongitude()); + copy.setProvider(location.getProvider()); + copy.setSpeed(location.getSpeed()); + copy.setTime(location.getTime()); + return copy; + } + + /** + * Returns the distance between the two locations in meters. + * Adapted from: http://stackoverflow.com/questions/837872/calculate-distance-in-meters-when-you-know-longitude-and-latitude-in-java + */ + private static float distanceBetween(Location location1, Location location2) { + double earthRadius = 3958.75; + double latDifference = Math.toRadians(location2.getLatitude() - location1.getLatitude()); + double lonDifference = Math.toRadians(location2.getLongitude() - location2.getLongitude()); + double a = Math.sin(latDifference/2) * Math.sin(latDifference/2) + + Math.cos(Math.toRadians(location1.getLatitude())) * Math.cos(Math.toRadians(location2.getLatitude())) * + Math.sin(lonDifference/2) * Math.sin(lonDifference/2); + double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a)); + double dist = Math.abs(earthRadius * c); + + int meterConversion = 1609; + + return new Float(dist * meterConversion); } public Map<PendingIntent, Criteria> getRequestLocationUdpateCriteriaPendingIntents() { @@ -362,8 +455,15 @@ public class ShadowLocationManager { } public Collection<String> getProvidersForListener(LocationListener listener) { - Set<String> providers = requestLocationUpdateListenersMap.get(listener); - return providers == null ? Collections.<String>emptyList() : new ArrayList<String>(providers); + Set<String> providers = new HashSet<String>(); + for (List<ListenerRegistration> listenerRegistrations : locationListeners.values()) { + for (ListenerRegistration listenerRegistration : listenerRegistrations) { + if (listenerRegistration.listener == listener) { + providers.add(listenerRegistration.provider); + } + } + } + return providers; } final private class LocationProviderEntry implements Map.Entry<Boolean, List<Criteria>> { diff --git a/src/main/java/com/xtremelabs/robolectric/shadows/ShadowMeasureSpec.java b/src/main/java/com/xtremelabs/robolectric/shadows/ShadowMeasureSpec.java new file mode 100644 index 000000000..8c6956aac --- /dev/null +++ b/src/main/java/com/xtremelabs/robolectric/shadows/ShadowMeasureSpec.java @@ -0,0 +1,50 @@ +package com.xtremelabs.robolectric.shadows; + +import android.view.View; +import com.xtremelabs.robolectric.internal.Implementation; +import com.xtremelabs.robolectric.internal.Implements; + +@SuppressWarnings({"UnusedDeclaration"}) +@Implements(View.MeasureSpec.class) +public class ShadowMeasureSpec { + private static final int MODE_SHIFT = 30; + private static final int MODE_MASK = 0x3 << MODE_SHIFT; + public static final int UNSPECIFIED = 0 << MODE_SHIFT; + public static final int EXACTLY = 1 << MODE_SHIFT; + public static final int AT_MOST = 2 << MODE_SHIFT; + + @Implementation + public static int makeMeasureSpec(int size, int mode) { + return size + mode; + } + + @Implementation + public static int getMode(int measureSpec) { + return (measureSpec & MODE_MASK); + } + + @Implementation + public static int getSize(int measureSpec) { + return (measureSpec & ~MODE_MASK); + } + + @Implementation + public static String toString(int measureSpec) { + int mode = getMode(measureSpec); + int size = getSize(measureSpec); + + StringBuilder sb = new StringBuilder("MeasureSpec: "); + + if (mode == UNSPECIFIED) + sb.append("UNSPECIFIED "); + else if (mode == EXACTLY) + sb.append("EXACTLY "); + else if (mode == AT_MOST) + sb.append("AT_MOST "); + else + sb.append(mode).append(" "); + + sb.append(size); + return sb.toString(); + } +} diff --git a/src/main/java/com/xtremelabs/robolectric/shadows/ShadowNotificationManager.java b/src/main/java/com/xtremelabs/robolectric/shadows/ShadowNotificationManager.java index e06065f9b..9959dfb04 100644 --- a/src/main/java/com/xtremelabs/robolectric/shadows/ShadowNotificationManager.java +++ b/src/main/java/com/xtremelabs/robolectric/shadows/ShadowNotificationManager.java @@ -14,8 +14,7 @@ import java.util.Map; @Implements(NotificationManager.class) public class ShadowNotificationManager { - private Map<Integer, Notification> notifications = new HashMap<Integer, Notification>(); - private Map<String, Integer> idForTag = new HashMap<String, Integer>(); + private Map<Key, Notification> notifications = new HashMap<Key, Notification>(); @Implementation public void notify(int id, Notification notification) @@ -25,10 +24,7 @@ public class ShadowNotificationManager { @Implementation public void notify(String tag, int id, Notification notification) { - if (tag != null) { - idForTag.put(tag, id); - } - notifications.put(id, notification); + notifications.put(new Key(tag, id), notification); } @Implementation @@ -39,35 +35,53 @@ public class ShadowNotificationManager { @Implementation public void cancel(String tag, int id) { - // I can't make sense of this method signature. I'm guessing that the id is optional and if it's bogus you are supposed to use the tag instead, but that references to both should be gone. PG - Integer tagId = idForTag.remove(tag); - if (notifications.containsKey(id)) { - notifications.remove(id); - } else { - notifications.remove(tagId); + Key key = new Key(tag, id); + if (notifications.containsKey(key)) { + notifications.remove(key); } } @Implementation public void cancelAll() { notifications.clear(); - idForTag.clear(); } public int size() { return notifications.size(); } - public Notification getNotification(int id) { - return notifications.get(id); - } - - public Notification getNotification(String tag) { - Integer id = idForTag.get(tag); - return notifications.get(id); + public Notification getNotification(String tag, int id) { + return notifications.get(new Key(tag, id)); } public List<Notification> getAllNotifications() { return new ArrayList<Notification>(notifications.values()); } + + private static final class Key { + public final String tag; + public final int id; + + private Key(String tag, int id) { + this.tag = tag; + this.id = id; + } + + @Override + public int hashCode() { + int hashCode = 17; + hashCode = 37 * hashCode + (tag == null ? 0 : tag.hashCode()); + hashCode = 37 * hashCode + id; + return hashCode; + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof Key)) return false; + Key other = (Key) o; + return (this.tag == null ? other.tag == null : this.tag.equals(other.tag)) + && this.id == other.id; + + } + } } diff --git a/src/main/java/com/xtremelabs/robolectric/shadows/ShadowPointF.java b/src/main/java/com/xtremelabs/robolectric/shadows/ShadowPointF.java index 38dd7416f..581b5f631 100644 --- a/src/main/java/com/xtremelabs/robolectric/shadows/ShadowPointF.java +++ b/src/main/java/com/xtremelabs/robolectric/shadows/ShadowPointF.java @@ -9,7 +9,7 @@ import com.xtremelabs.robolectric.internal.RealObject; import static com.xtremelabs.robolectric.Robolectric.shadowOf_; /** - * Shadow implementation of {@code Point} + * Shadow implementation of {@code PointF} */ @SuppressWarnings({"UnusedDeclaration"}) @Implements(PointF.class) diff --git a/src/main/java/com/xtremelabs/robolectric/shadows/ShadowPreferenceActivity.java b/src/main/java/com/xtremelabs/robolectric/shadows/ShadowPreferenceActivity.java index 44bd0cf59..952632ad5 100644 --- a/src/main/java/com/xtremelabs/robolectric/shadows/ShadowPreferenceActivity.java +++ b/src/main/java/com/xtremelabs/robolectric/shadows/ShadowPreferenceActivity.java @@ -1,30 +1,40 @@ package com.xtremelabs.robolectric.shadows; +import android.os.Bundle; +import android.preference.Preference; import android.preference.PreferenceActivity; import android.preference.PreferenceScreen; -import android.widget.ListView; import com.xtremelabs.robolectric.internal.Implementation; import com.xtremelabs.robolectric.internal.Implements; @Implements(PreferenceActivity.class) public class ShadowPreferenceActivity extends ShadowListActivity { - private int preferencesResId = -1; private PreferenceScreen preferenceScreen; @Implementation + public void onCreate(Bundle savedInstanceState) { + setContentView(android.R.layout.list_content); + } + + @Implementation public void addPreferencesFromResource(int preferencesResId) { this.preferencesResId = preferencesResId; preferenceScreen = getResourceLoader().inflatePreferences(getApplicationContext(), preferencesResId); } - + public int getPreferencesResId() { return preferencesResId; - } - + } + @Implementation public PreferenceScreen getPreferenceScreen() { return preferenceScreen; } + + @Implementation + public Preference findPreference(CharSequence key) { + return preferenceScreen.findPreference(key); + } } diff --git a/src/main/java/com/xtremelabs/robolectric/shadows/ShadowRect.java b/src/main/java/com/xtremelabs/robolectric/shadows/ShadowRect.java index 7a34d0d14..37bba4f0e 100644 --- a/src/main/java/com/xtremelabs/robolectric/shadows/ShadowRect.java +++ b/src/main/java/com/xtremelabs/robolectric/shadows/ShadowRect.java @@ -5,12 +5,19 @@ import com.xtremelabs.robolectric.internal.Implementation; import com.xtremelabs.robolectric.internal.Implements; import com.xtremelabs.robolectric.internal.RealObject; +import java.io.PrintWriter; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + import static com.xtremelabs.robolectric.Robolectric.shadowOf_; @Implements(Rect.class) public class ShadowRect { @RealObject Rect realRect; + private static final Pattern FLATTENED_PATTERN = Pattern.compile( + "(-?\\d+) (-?\\d+) (-?\\d+) (-?\\d+)"); + public void __constructor__(int left, int top, int right, int bottom) { realRect.left = left; realRect.top = top; @@ -59,6 +66,7 @@ public class ShadowRect { } @Implementation + @Override public boolean equals(Object obj) { if (obj == null) return false; Object o = shadowOf_(obj); @@ -72,54 +80,222 @@ public class ShadowRect { } @Implementation + @Override public String toString() { StringBuilder sb = new StringBuilder(32); - sb.append("Rect("); + sb.append("Rect("); sb.append(realRect.left); sb.append(", "); + sb.append(realRect.top); sb.append(" - "); sb.append(realRect.right); + sb.append(", "); sb.append(realRect.bottom); sb.append(")"); + return sb.toString(); + } + + @Implementation + public String toShortString() { + return toShortString(new StringBuilder(32)); + } + + public String toShortString(StringBuilder sb) { + sb.setLength(0); + sb.append('['); sb.append(realRect.left); sb.append(','); + sb.append(realRect.top); sb.append("]["); sb.append(realRect.right); + sb.append(','); sb.append(realRect.bottom); sb.append(']'); + return sb.toString(); + } + + @Implementation + public String flattenToString() { + StringBuilder sb = new StringBuilder(32); + // WARNING: Do not change the format of this string, it must be + // preserved because Rects are saved in this flattened format. sb.append(realRect.left); - sb.append(", "); + sb.append(' '); sb.append(realRect.top); - sb.append(" - "); + sb.append(' '); sb.append(realRect.right); - sb.append(", "); + sb.append(' '); sb.append(realRect.bottom); - sb.append(")"); return sb.toString(); } + + @Implementation + public static Rect unflattenFromString(String str) { + Matcher matcher = FLATTENED_PATTERN.matcher(str); + if (!matcher.matches()) { + return null; + } + return new Rect(Integer.parseInt(matcher.group(1)), + Integer.parseInt(matcher.group(2)), + Integer.parseInt(matcher.group(3)), + Integer.parseInt(matcher.group(4))); + } + + public void printShortString(PrintWriter pw) { + pw.print('['); pw.print(realRect.left); pw.print(','); + pw.print(realRect.top); pw.print("]["); pw.print(realRect.right); + pw.print(','); pw.print(realRect.bottom); pw.print(']'); + } @Implementation + public final boolean isEmpty() { + return realRect.left >= realRect.right || realRect.top >= realRect.bottom; + } + + @Implementation + public final float exactCenterX() { + return (realRect.left + realRect.right) * 0.5f; + } + + @Implementation + public final float exactCenterY() { + return (realRect.top + realRect.bottom) * 0.5f; + } + + @Implementation + public void setEmpty() { + realRect.left = realRect.right = realRect.top = realRect.bottom = 0; + } + + @Implementation + public void offset(int dx, int dy) { + realRect.left += dx; + realRect.top += dy; + realRect.right += dx; + realRect.bottom += dy; + } + + @Implementation + public void offsetTo(int newLeft, int newTop) { + realRect.right += newLeft - realRect.left; + realRect.bottom += newTop - realRect.top; + realRect.left = newLeft; + realRect.top = newTop; + } + + @Implementation + public void inset(int dx, int dy) { + realRect.left += dx; + realRect.top += dy; + realRect.right -= dx; + realRect.bottom -= dy; + } + + @Implementation public boolean contains(int x, int y) { - return x > realRect.left && x < realRect.right - && y >= realRect.top && y <= realRect.bottom; - } + return x > realRect.left && x < realRect.right + && y >= realRect.top && y <= realRect.bottom; + } @Implementation public boolean contains(Rect r) { - return equals(r) - || (contains(r.left, r.top) && contains(r.right, r.top) - && contains(r.left, r.bottom) && contains(r.right, r.bottom)); + return equals(r) + || (contains(r.left, r.top) && contains(r.right, r.top) + && contains(r.left, r.bottom) && contains(r.right, r.bottom)); } - + @Implementation - public static boolean intersects(Rect a, Rect b) { - return a.left < b.right && b.left < a.right - && a.top < b.bottom && b.top < a.bottom; + public static boolean intersects(Rect a, Rect b) { + return a.left < b.right && b.left < a.right + && a.top < b.bottom && b.top < a.bottom; } - + @Implementation public boolean intersect(Rect r) { - return intersects(realRect, r); + return intersects(realRect, r); } - + @Implementation public boolean intersect(int left, int top, int right, int bottom) { - return intersect(new Rect(left, top, right, bottom)); + return intersect(new Rect(left, top, right, bottom)); + } + + @Implementation + public boolean contains(int left, int top, int right, int bottom) { + // check for empty first + return realRect.left < realRect.right && realRect.top < realRect.bottom + // now check for containment + && realRect.left <= left && realRect.top <= top + && realRect.right >= right && realRect.bottom >= bottom; + } + + @Implementation + public boolean setIntersect(Rect a, Rect b) { + if (a.left < b.right && b.left < a.right + && a.top < b.bottom && b.top < a.bottom) { + realRect.left = Math.max(a.left, b.left); + realRect.top = Math.max(a.top, b.top); + realRect.right = Math.min(a.right, b.right); + realRect.bottom = Math.min(a.bottom, b.bottom); + return true; + } + return false; + } + + @Implementation + public boolean intersects(int left, int top, int right, int bottom) { + return realRect.left < right && left < realRect.right + && realRect.top < bottom && top < realRect.bottom; + } + + @Implementation + public void union(int left, int top, int right, int bottom) { + if ((left < right) && (top < bottom)) { + if ((realRect.left < realRect.right) && (realRect.top < realRect.bottom)) { + if (realRect.left > left) + realRect.left = left; + if (realRect.top > top) + realRect.top = top; + if (realRect.right < right) + realRect.right = right; + if (realRect.bottom < bottom) + realRect.bottom = bottom; + } else { + realRect.left = left; + realRect.top = top; + realRect.right = right; + realRect.bottom = bottom; + } + } + } + + @Implementation + public void union(Rect r) { + union(r.left, r.top, r.right, r.bottom); } @Implementation - public void offset(int dx, int dy) { - realRect.left += dx; - realRect.right += dx; - realRect.top += dy; - realRect.bottom += dy; + public void union(int x, int y) { + if (x < realRect.left) { + realRect.left = x; + } else if (x > realRect.right) { + realRect.right = x; + } + if (y < realRect.top) { + realRect.top = y; + } else if (y > realRect.bottom) { + realRect.bottom = y; + } + } + + @Implementation + public void sort() { + if (realRect.left > realRect.right) { + int temp = realRect.left; + realRect.left = realRect.right; + realRect.right = temp; + } + if (realRect.top > realRect.bottom) { + int temp = realRect.top; + realRect.top = realRect.bottom; + realRect.bottom = temp; + } + } + + public void scale(float scale) { + if (scale != 1.0f) { + realRect.left = (int) (realRect.left * scale + 0.5f); + realRect.top = (int) (realRect.top * scale + 0.5f); + realRect.right = (int) (realRect.right * scale + 0.5f); + realRect.bottom = (int) (realRect.bottom * scale + 0.5f); + } } } diff --git a/src/main/java/com/xtremelabs/robolectric/shadows/ShadowRectF.java b/src/main/java/com/xtremelabs/robolectric/shadows/ShadowRectF.java new file mode 100644 index 000000000..4a37affaa --- /dev/null +++ b/src/main/java/com/xtremelabs/robolectric/shadows/ShadowRectF.java @@ -0,0 +1,261 @@ +package com.xtremelabs.robolectric.shadows; + +import android.graphics.Rect; +import android.graphics.RectF; +import android.util.FloatMath; +import com.xtremelabs.robolectric.internal.Implementation; +import com.xtremelabs.robolectric.internal.Implements; +import com.xtremelabs.robolectric.internal.RealObject; + +/** + * Shadow implementation of {@code RectF} + * + * (lifted from Android RectF.) + */ +@SuppressWarnings({"UnusedDeclaration"}) +@Implements(RectF.class) +public class ShadowRectF { + @RealObject private RectF realRectF; + + public void __constructor__(float left, float top, float right, float bottom) { + realRectF.left = left; + realRectF.top = top; + realRectF.right = right; + realRectF.bottom = bottom; + } + + public void __constructor__(RectF r) { + realRectF.left = r.left; + realRectF.top = r.top; + realRectF.right = r.right; + realRectF.bottom = r.bottom; + } + + public void __constructor__(Rect r) { + realRectF.left = r.left; + realRectF.top = r.top; + realRectF.right = r.right; + realRectF.bottom = r.bottom; + } + + @Implementation + public String toString() { + return "RectF(" + realRectF.left + ", " + realRectF.top + ", " + + realRectF.right + ", " + realRectF.bottom + ")"; + } + + @Implementation + public final boolean isEmpty() { + return realRectF.left >= realRectF.right || realRectF.top >= realRectF.bottom; + } + + @Implementation + public final float width() { + return realRectF.right - realRectF.left; + } + + @Implementation + public final float height() { + return realRectF.bottom - realRectF.top; + } + + @Implementation + public final float centerX() { + return (realRectF.left + realRectF.right) * 0.5f; + } + + @Implementation + public final float centerY() { + return (realRectF.top + realRectF.bottom) * 0.5f; + } + + @Implementation + public void setEmpty() { + realRectF.left = realRectF.right = realRectF.top = realRectF.bottom = 0; + } + + @Implementation + public void set(float left, float top, float right, float bottom) { + realRectF.left = left; + realRectF.top = top; + realRectF.right = right; + realRectF.bottom = bottom; + } + + @Implementation + public void set(RectF src) { + realRectF.left = src.left; + realRectF.top = src.top; + realRectF.right = src.right; + realRectF.bottom = src.bottom; + } + + @Implementation + public void set(Rect src) { + realRectF.left = src.left; + realRectF.top = src.top; + realRectF.right = src.right; + realRectF.bottom = src.bottom; + } + + @Implementation + public void offset(float dx, float dy) { + realRectF.left += dx; + realRectF.top += dy; + realRectF.right += dx; + realRectF.bottom += dy; + } + + @Implementation + public void offsetTo(float newLeft, float newTop) { + realRectF.right += newLeft - realRectF.left; + realRectF.bottom += newTop - realRectF.top; + realRectF.left = newLeft; + realRectF.top = newTop; + } + + @Implementation + public void inset(float dx, float dy) { + realRectF.left += dx; + realRectF.top += dy; + realRectF.right -= dx; + realRectF.bottom -= dy; + } + + @Implementation + public boolean contains(float x, float y) { + return realRectF.left < realRectF.right && realRectF.top < realRectF.bottom // check for empty first + && x >= realRectF.left && x < realRectF.right && y >= realRectF.top && y < realRectF.bottom; + } + + @Implementation + public boolean contains(float left, float top, float right, float bottom) { + return realRectF.left < realRectF.right && realRectF.top < realRectF.bottom + && realRectF.left <= left && realRectF.top <= top + && realRectF.right >= right && realRectF.bottom >= bottom; + } + + @Implementation + public boolean contains(RectF r) { + return realRectF.left < realRectF.right && realRectF.top < realRectF.bottom + && realRectF.left <= r.left && realRectF.top <= r.top + && realRectF.right >= r.right && realRectF.bottom >= r.bottom; + } + + @Implementation + public boolean intersect(float left, float top, float right, float bottom) { + if (realRectF.left < right && left < realRectF.right + && realRectF.top < bottom && top < realRectF.bottom) { + if (realRectF.left < left) { + realRectF.left = left; + } + if (realRectF.top < top) { + realRectF.top = top; + } + if (realRectF.right > right) { + realRectF.right = right; + } + if (realRectF.bottom > bottom) { + realRectF.bottom = bottom; + } + return true; + } + return false; + } + + @Implementation + public boolean intersect(RectF r) { + return intersect(r.left, r.top, r.right, r.bottom); + } + + @Implementation + public boolean setIntersect(RectF a, RectF b) { + if (a.left < b.right && b.left < a.right + && a.top < b.bottom && b.top < a.bottom) { + realRectF.left = Math.max(a.left, b.left); + realRectF.top = Math.max(a.top, b.top); + realRectF.right = Math.min(a.right, b.right); + realRectF.bottom = Math.min(a.bottom, b.bottom); + return true; + } + return false; + } + + @Implementation + public boolean intersects(float left, float top, float right, float bottom) { + return realRectF.left < right && left < realRectF.right + && realRectF.top < bottom && top < realRectF.bottom; + } + + @Implementation + public static boolean intersects(RectF a, RectF b) { + return a.left < b.right && b.left < a.right + && a.top < b.bottom && b.top < a.bottom; + } + + @Implementation + public void round(Rect dst) { + dst.set(Math.round(realRectF.left), Math.round(realRectF.top), + Math.round(realRectF.right), Math.round(realRectF.bottom)); + } + + @Implementation + public void roundOut(Rect dst) { + dst.set((int) FloatMath.floor(realRectF.left), (int) FloatMath.floor(realRectF.top), + (int) FloatMath.ceil(realRectF.right), (int) FloatMath.ceil(realRectF.bottom)); + } + + @Implementation + public void union(float left, float top, float right, float bottom) { + if ((left < right) && (top < bottom)) { + if ((realRectF.left < realRectF.right) && (realRectF.top < realRectF.bottom)) { + if (realRectF.left > left) + realRectF.left = left; + if (realRectF.top > top) + realRectF.top = top; + if (realRectF.right < right) + realRectF.right = right; + if (realRectF.bottom < bottom) + realRectF.bottom = bottom; + } else { + realRectF.left = left; + realRectF.top = top; + realRectF.right = right; + realRectF.bottom = bottom; + } + } + } + + @Implementation + public void union(RectF r) { + union(r.left, r.top, r.right, r.bottom); + } + + @Implementation + public void union(float x, float y) { + if (x < realRectF.left) { + realRectF.left = x; + } else if (x > realRectF.right) { + realRectF.right = x; + } + if (y < realRectF.top) { + realRectF.top = y; + } else if (y > realRectF.bottom) { + realRectF.bottom = y; + } + } + + @Implementation + public void sort() { + if (realRectF.left > realRectF.right) { + float temp = realRectF.left; + realRectF.left = realRectF.right; + realRectF.right = temp; + } + if (realRectF.top > realRectF.bottom) { + float temp = realRectF.top; + realRectF.top = realRectF.bottom; + realRectF.bottom = temp; + } + } +} diff --git a/src/main/java/com/xtremelabs/robolectric/shadows/ShadowResources.java b/src/main/java/com/xtremelabs/robolectric/shadows/ShadowResources.java index 5e75d45f6..82f9e87db 100644 --- a/src/main/java/com/xtremelabs/robolectric/shadows/ShadowResources.java +++ b/src/main/java/com/xtremelabs/robolectric/shadows/ShadowResources.java @@ -1,6 +1,7 @@ package com.xtremelabs.robolectric.shadows; import android.content.res.AssetManager; +import android.content.res.ColorStateList; import android.content.res.Configuration; import android.content.res.Resources; import android.content.res.TypedArray; @@ -18,17 +19,17 @@ import com.xtremelabs.robolectric.internal.Implements; import com.xtremelabs.robolectric.internal.RealObject; import com.xtremelabs.robolectric.res.ResourceExtractor; import com.xtremelabs.robolectric.res.ResourceLoader; - import java.io.InputStream; import java.util.Locale; +import static com.xtremelabs.robolectric.Robolectric.getShadowApplication; import static com.xtremelabs.robolectric.Robolectric.newInstanceOf; import static com.xtremelabs.robolectric.Robolectric.shadowOf; /** * Shadow of {@code Resources} that simulates the loading of resources * - * @see com.xtremelabs.robolectric.RobolectricTestRunner#RobolectricTestRunner(Class, String, String) + * @see com.xtremelabs.robolectric.RobolectricTestRunner#RobolectricTestRunner(Class) */ @SuppressWarnings({"UnusedDeclaration"}) @Implements(Resources.class) @@ -38,6 +39,8 @@ public class ShadowResources { private DisplayMetrics displayMetrics; private Display display; + private static Resources system = null; + static Resources bind(Resources resources, ResourceLoader resourceLoader) { ShadowResources shadowResources = shadowOf(resources); if (shadowResources.resourceLoader != null) throw new RuntimeException("ResourceLoader already set!"); @@ -49,6 +52,21 @@ public class ShadowResources { Resources realResources; private ResourceLoader resourceLoader; + public ShadowResources() { + Configuration configuration = new Configuration(); + configuration.setToDefaults(); + setConfiguration(configuration); + } + + /** + * Non-Android accessor that sets the value to be returned by {@link #getConfiguration()} + * + * @param configuration Configuration instance to set on this Resources obj + */ + public void setConfiguration(Configuration configuration) { + this.configuration = configuration; + } + @Implementation public int getIdentifier(String name, String defType, String defPackage) { Integer index = 0; @@ -68,6 +86,11 @@ public class ShadowResources { } @Implementation + public ColorStateList getColorStateList(int id) { + return new ColorStateList(null, null); + } + + @Implementation public Configuration getConfiguration() { if (configuration == null) { configuration = new Configuration(); @@ -221,11 +244,17 @@ public class ShadowResources { @Implementation public final android.content.res.Resources.Theme newTheme() { - return newInstanceOf(Resources.Theme.class); + return inject(realResources, newInstanceOf(Resources.Theme.class)); } @Implements(Resources.Theme.class) - public static class ShadowTheme { + public static class ShadowTheme implements UsesResources { + protected Resources resources; + + public void injectResources(Resources resources) { + this.resources = resources; + } + @Implementation public TypedArray obtainStyledAttributes(int[] attrs) { return obtainStyledAttributes(0, attrs); @@ -238,8 +267,41 @@ public class ShadowResources { @Implementation public TypedArray obtainStyledAttributes(AttributeSet set, int[] attrs, int defStyleAttr, int defStyleRes) { - return newInstanceOf(TypedArray.class); + return inject(resources, newInstanceOf(TypedArray.class)); } } + @Implementation + public static Resources getSystem() { + if (system == null) { + try { + initSystemResources(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + return system; + } + + public static <T> T inject(Resources resources, T instance) { + Object shadow = Robolectric.shadowOf_(instance); + if (shadow instanceof UsesResources) { + ((UsesResources) shadow).injectResources(resources); + } + return instance; + } + + + /** + * Creates system resource loader from a copy of the application resource loader. Sets + * a flag to exclude local resources on initialization. + */ + private static void initSystemResources() throws Exception { + final ResourceLoader appResourceLoader = getShadowApplication().getResourceLoader(); + final ResourceLoader systemResourceLoader = new ResourceLoader(appResourceLoader); + systemResourceLoader.setSystem(true); + system = ShadowResources.bind(new Resources(null, null, null), systemResourceLoader); + } + } diff --git a/src/main/java/com/xtremelabs/robolectric/shadows/ShadowSQLiteCloseable.java b/src/main/java/com/xtremelabs/robolectric/shadows/ShadowSQLiteCloseable.java index 52a0df5bd..9992b1078 100644 --- a/src/main/java/com/xtremelabs/robolectric/shadows/ShadowSQLiteCloseable.java +++ b/src/main/java/com/xtremelabs/robolectric/shadows/ShadowSQLiteCloseable.java @@ -13,8 +13,8 @@ import com.xtremelabs.robolectric.internal.Implements; @Implements(SQLiteClosable.class) public class ShadowSQLiteCloseable { - @Implementation - public void close() { - } - -} + @Implementation + public void close() { + } + +}
\ No newline at end of file diff --git a/src/main/java/com/xtremelabs/robolectric/shadows/ShadowSensorManager.java b/src/main/java/com/xtremelabs/robolectric/shadows/ShadowSensorManager.java index 99bba883a..6ce7b9ddb 100644 --- a/src/main/java/com/xtremelabs/robolectric/shadows/ShadowSensorManager.java +++ b/src/main/java/com/xtremelabs/robolectric/shadows/ShadowSensorManager.java @@ -4,14 +4,13 @@ import android.hardware.Sensor; import android.hardware.SensorEvent; import android.hardware.SensorEventListener; import android.hardware.SensorManager; -import android.os.Handler; - import com.xtremelabs.robolectric.Robolectric; import com.xtremelabs.robolectric.internal.Implementation; import com.xtremelabs.robolectric.internal.Implements; import java.util.ArrayList; -import java.util.List; +import java.util.HashMap; +import java.util.Map; @Implements(SensorManager.class) @@ -20,7 +19,23 @@ public class ShadowSensorManager { private ArrayList<SensorEventListener> listeners = new ArrayList<SensorEventListener>(); public boolean forceListenersToFail = false; - + + private final Map<Integer, Sensor> sensorMap = new HashMap<Integer, Sensor>(); + + /** + * Provide a Sensor for the indicated sensor type. + * @param sensorType from Sensor constants + * @param sensor Sensor instance + */ + public void addSensor(int sensorType, Sensor sensor) { + sensorMap.put(sensorType, sensor); + } + + @Implementation + public Sensor getDefaultSensor(int type) { + return sensorMap.get(type); + } + @Implementation public boolean registerListener(SensorEventListener listener, Sensor sensor, int rate) { diff --git a/src/main/java/com/xtremelabs/robolectric/shadows/ShadowService.java b/src/main/java/com/xtremelabs/robolectric/shadows/ShadowService.java index 74a9cc1ca..b428d35bd 100644 --- a/src/main/java/com/xtremelabs/robolectric/shadows/ShadowService.java +++ b/src/main/java/com/xtremelabs/robolectric/shadows/ShadowService.java @@ -63,6 +63,9 @@ public class ShadowService extends ShadowContextWrapper { public void stopForeground(boolean removeNotification) { foregroundStopped = true; notificationShouldRemoved = removeNotification; + if (removeNotification) { + lastForegroundNotification = null; + } } public Notification getLastForegroundNotification() { diff --git a/src/main/java/com/xtremelabs/robolectric/shadows/ShadowSpannableStringBuilder.java b/src/main/java/com/xtremelabs/robolectric/shadows/ShadowSpannableStringBuilder.java index 2a28c9e6f..dc9532ada 100644 --- a/src/main/java/com/xtremelabs/robolectric/shadows/ShadowSpannableStringBuilder.java +++ b/src/main/java/com/xtremelabs/robolectric/shadows/ShadowSpannableStringBuilder.java @@ -27,8 +27,13 @@ public class ShadowSpannableStringBuilder implements CharSequence { } @Implementation - public Editable replace(int st, int en, CharSequence text) { - builder.replace(st, en, text.toString()); + public SpannableStringBuilder replace(int start, int end, CharSequence tb) { + return replace(start, end, tb, 0, tb.length()); + } + + @Implementation + public SpannableStringBuilder replace(int start, int end, CharSequence tb, int tbStart, int tbEnd) { + builder.replace(start, end, tb.subSequence(tbStart, tbEnd).toString()); return realSpannableStringBuilder; } diff --git a/src/main/java/com/xtremelabs/robolectric/shadows/ShadowSpannedString.java b/src/main/java/com/xtremelabs/robolectric/shadows/ShadowSpannedString.java new file mode 100644 index 000000000..f18e69600 --- /dev/null +++ b/src/main/java/com/xtremelabs/robolectric/shadows/ShadowSpannedString.java @@ -0,0 +1,29 @@ +package com.xtremelabs.robolectric.shadows; + +import android.text.SpannedString; +import com.xtremelabs.robolectric.internal.Implementation; +import com.xtremelabs.robolectric.internal.Implements; + +@Implements(SpannedString.class) +public class ShadowSpannedString { + + private CharSequence charSequence; + + public void __constructor__(CharSequence charSequence) { + this.charSequence = charSequence; + } + + @Override @Implementation + public String toString() { + return charSequence.toString(); + } + + @Implementation + public static SpannedString valueOf(CharSequence charSequence) { + if (charSequence instanceof SpannedString) { + return (SpannedString) charSequence; + } + return new SpannedString(charSequence); + } + +} diff --git a/src/main/java/com/xtremelabs/robolectric/shadows/ShadowTextUtils.java b/src/main/java/com/xtremelabs/robolectric/shadows/ShadowTextUtils.java index cd74ae54b..cb5e1f85a 100644 --- a/src/main/java/com/xtremelabs/robolectric/shadows/ShadowTextUtils.java +++ b/src/main/java/com/xtremelabs/robolectric/shadows/ShadowTextUtils.java @@ -1,5 +1,6 @@ package com.xtremelabs.robolectric.shadows; +import android.text.TextPaint; import android.text.TextUtils; import com.xtremelabs.robolectric.internal.Implementation; import com.xtremelabs.robolectric.internal.Implements; @@ -54,11 +55,18 @@ public class ShadowTextUtils { @Implementation public static String[] split(String text, String expression) { - if(text.length() == 0) { - return new String[]{}; - } - - return text.split(expression); + if(text.length() == 0) { + return new String[]{}; + } + + return text.split(expression); + } + + @Implementation + public static CharSequence ellipsize(CharSequence text, + TextPaint p, + float avail, TextUtils.TruncateAt where) { + return text; } @Implementation diff --git a/src/main/java/com/xtremelabs/robolectric/shadows/ShadowTextView.java b/src/main/java/com/xtremelabs/robolectric/shadows/ShadowTextView.java index 3260fe0c3..afb28685d 100644 --- a/src/main/java/com/xtremelabs/robolectric/shadows/ShadowTextView.java +++ b/src/main/java/com/xtremelabs/robolectric/shadows/ShadowTextView.java @@ -58,6 +58,7 @@ public class ShadowTextView extends ShadowView { @Override public void applyAttributes() { super.applyAttributes(); + applyHintAttribute(); applyTextAttribute(); applyTextColorAttribute(); applyHintAttribute(); @@ -256,12 +257,23 @@ public class ShadowTextView extends ShadowView { if (compoundDrawablesImpl == null) { return new Drawable[]{null, null, null, null}; } - return new Drawable[]{ - compoundDrawablesImpl.leftDrawable, - compoundDrawablesImpl.topDrawable, - compoundDrawablesImpl.rightDrawable, - compoundDrawablesImpl.bottomDrawable - }; + + if(compoundDrawablesImpl.left == 0 && compoundDrawablesImpl.right == 0 && + compoundDrawablesImpl.top == 0 && compoundDrawablesImpl.bottom == 0) { + return new Drawable[]{ + compoundDrawablesImpl.leftDrawable, + compoundDrawablesImpl.topDrawable, + compoundDrawablesImpl.rightDrawable, + compoundDrawablesImpl.bottomDrawable + }; + } else { + return new Drawable[]{ + buildDrawable(compoundDrawablesImpl.left), + buildDrawable(compoundDrawablesImpl.top), + buildDrawable(compoundDrawablesImpl.right), + buildDrawable(compoundDrawablesImpl.bottom) + }; + } } @Implementation @@ -314,7 +326,6 @@ public class ShadowTextView extends ShadowView { this.gravity = gravity; } - @Implementation public int getImeOptions() { return imeOptions; @@ -555,6 +566,11 @@ public class ShadowTextView extends ShadowView { } public static class CompoundDrawables { + public int left; + public int top; + public int right; + public int bottom; + public Drawable leftDrawable; public Drawable topDrawable; public Drawable rightDrawable; diff --git a/src/main/java/com/xtremelabs/robolectric/shadows/ShadowTypedArray.java b/src/main/java/com/xtremelabs/robolectric/shadows/ShadowTypedArray.java index 2781ac0c7..74054be26 100644 --- a/src/main/java/com/xtremelabs/robolectric/shadows/ShadowTypedArray.java +++ b/src/main/java/com/xtremelabs/robolectric/shadows/ShadowTypedArray.java @@ -2,7 +2,6 @@ package com.xtremelabs.robolectric.shadows; import android.content.res.Resources; import android.content.res.TypedArray; -import com.xtremelabs.robolectric.Robolectric; import com.xtremelabs.robolectric.internal.Implementation; import com.xtremelabs.robolectric.internal.Implements; @@ -11,12 +10,17 @@ import java.util.List; @SuppressWarnings({"UnusedDeclaration"}) @Implements(TypedArray.class) -public class ShadowTypedArray { +public class ShadowTypedArray implements UsesResources { + private Resources resources; private List<Object> values = new ArrayList<Object>(); + public void injectResources(Resources resources) { + this.resources = resources; + } + @Implementation public Resources getResources() { - return Robolectric.application.getResources(); + return resources; } public void add(Object attributeValue) { @@ -27,4 +31,24 @@ public class ShadowTypedArray { public java.lang.String getString(int index) { return (String) values.get(index); } + + @Implementation + public int getInt(int index, int defValue) { + return defValue; + } + + @Implementation + public int getInteger(int index, int defValue) { + return defValue; + } + + @Implementation + public int getResourceId(int index, int defValue) { + return defValue; + } + + @Implementation + public float getDimension(int index, float defValue) { + return defValue; + } } diff --git a/src/main/java/com/xtremelabs/robolectric/shadows/ShadowView.java b/src/main/java/com/xtremelabs/robolectric/shadows/ShadowView.java index cd3972a96..9b1f828fb 100644 --- a/src/main/java/com/xtremelabs/robolectric/shadows/ShadowView.java +++ b/src/main/java/com/xtremelabs/robolectric/shadows/ShadowView.java @@ -3,9 +3,12 @@ package com.xtremelabs.robolectric.shadows; import android.content.Context; import android.content.res.Resources; import android.graphics.Bitmap; +import android.graphics.BitmapFactory; import android.graphics.Point; +import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; +import android.graphics.drawable.LayerDrawable; import android.util.AttributeSet; import android.view.*; import android.view.View.MeasureSpec; @@ -14,7 +17,7 @@ import com.xtremelabs.robolectric.Robolectric; import com.xtremelabs.robolectric.internal.Implementation; import com.xtremelabs.robolectric.internal.Implements; import com.xtremelabs.robolectric.internal.RealObject; - +import com.xtremelabs.robolectric.util.ReflectionUtil; import java.io.PrintStream; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; @@ -34,6 +37,9 @@ import static com.xtremelabs.robolectric.Robolectric.shadowOf; @SuppressWarnings({"UnusedDeclaration"}) @Implements(View.class) public class ShadowView { + // This is dumb, we should have a Robolectric-wide way of warning about weird states. todo [xw] + public static boolean strict = false; + @RealObject protected View realView; @@ -41,6 +47,7 @@ public class ShadowView { ShadowView parent; protected Context context; private boolean selected; + private boolean pressed; private View.OnClickListener onClickListener; private View.OnLongClickListener onLongClickListener; private Object tag; @@ -83,6 +90,7 @@ public class ShadowView { private float translationX = 0.0f; private float translationY = 0.0f; private float alpha = 1.0f; + private boolean attachedToWindow; public void __constructor__(Context context) { __constructor__(context, null); @@ -248,6 +256,40 @@ public class ShadowView { return context.getResources(); } + /** + * Build drawable, either LayerDrawable or BitmapDrawable. + * + * @param resourceId Resource id + * @return Drawable + */ + protected Drawable buildDrawable(int resourceId) { + if (isDrawableXml(resourceId)) { + int[] resourceIds = shadowOf(Robolectric.application) + .getResourceLoader().getDrawableIds(resourceId); + + Drawable[] drawables = new Drawable[resourceIds.length]; + + for (int i = 0; i < resourceIds.length; i++) { + drawables[i] = buildDrawable(resourceIds[i]); + } + + return new LayerDrawable(drawables); + } else { + return new BitmapDrawable(BitmapFactory.decodeResource(getResources(), resourceId)); + } + } + + /** + * Does the resource id point to xml resource. + * + * @param resourceId Resource id + * @return Boolean + */ + protected boolean isDrawableXml(int resourceId) { + return shadowOf(Robolectric.application).getResourceLoader() + .isDrawableXml(resourceId); + } + @Implementation public void setBackgroundResource(int backgroundResourceId) { this.backgroundResourceId = backgroundResourceId; @@ -309,6 +351,16 @@ public class ShadowView { } @Implementation + public void setPressed(boolean pressed) { + this.pressed = pressed; + } + + @Implementation + public boolean isPressed() { + return this.pressed; + } + + @Implementation public boolean isEnabled() { return this.enabled; } @@ -420,14 +472,28 @@ public class ShadowView { throw new RuntimeException(e); } } - + + @Implementation + public void draw(android.graphics.Canvas canvas) { + if (background != null) { + shadowOf(canvas).appendDescription("background:"); + background.draw(canvas); + } + } + @Implementation public final void layout(int l, int t, int r, int b) { - left = l; - top = t; - right = r; - bottom = b; -// todo: realView.onLayout(); + if (l != left || r != right || t != top || b != bottom) { + left = l; + top = t; + right = r; + bottom = b; + + realView.invalidate(); + ReflectionUtil.invoke(realView, "onLayout", + new Class<?>[]{Boolean.TYPE, Integer.TYPE, Integer.TYPE, Integer.TYPE, Integer.TYPE}, + true, l, t, r, b); + } } @Implementation @@ -1045,4 +1111,42 @@ public class ShadowView { public TouchDelegate getTouchDelegate() { return touchDelegate; } + + @Implementation + public void onAttachedToWindow() { + if (strict && attachedToWindow) throw new IllegalStateException("already attached!"); + attachedToWindow = true; + } + + @Implementation + public void onDetachedFromWindow() { + if (strict && !attachedToWindow) throw new IllegalStateException("not attached!"); + attachedToWindow = false; + } + + public boolean isAttachedToWindow() { + return attachedToWindow; + } + + public void callOnAttachedToWindow() { + invokeReflectively("onAttachedToWindow"); + } + + public void callOnDetachedFromWindow() { + invokeReflectively("onDetachedFromWindow"); + } + + private void invokeReflectively(String methodName) { + try { + Method method = View.class.getDeclaredMethod(methodName); + method.setAccessible(true); + method.invoke(realView); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } catch (InvocationTargetException e) { + throw new RuntimeException(e); + } catch (NoSuchMethodException e) { + throw new RuntimeException(e); + } + } } diff --git a/src/main/java/com/xtremelabs/robolectric/shadows/ShadowViewAnimator.java b/src/main/java/com/xtremelabs/robolectric/shadows/ShadowViewAnimator.java index 52a56d3b8..d52db9fd8 100644 --- a/src/main/java/com/xtremelabs/robolectric/shadows/ShadowViewAnimator.java +++ b/src/main/java/com/xtremelabs/robolectric/shadows/ShadowViewAnimator.java @@ -2,41 +2,43 @@ package com.xtremelabs.robolectric.shadows; import android.view.View; import android.widget.ViewAnimator; - import com.xtremelabs.robolectric.internal.Implementation; import com.xtremelabs.robolectric.internal.Implements; +/** + * Shadow of {@link android.widget.ViewAnimator} + */ @Implements(ViewAnimator.class) -public class ShadowViewAnimator extends ShadowFrameLayout { - private int mWhichChild = 0; +public class ShadowViewAnimator extends ShadowViewGroup { - @Implementation - public void showNext() { - setDisplayedChild(mWhichChild + 1); - } + private int currentChild = 0; @Implementation - public void showPrevious() { - setDisplayedChild(mWhichChild - 1); + public int getDisplayedChild() { + return currentChild; } @Implementation public void setDisplayedChild(int whichChild) { - mWhichChild = whichChild; - if (whichChild >= getChildCount()) { - mWhichChild = 0; - } else if (whichChild < 0) { - mWhichChild = getChildCount() - 1; + currentChild = whichChild; + for (int i = getChildCount() - 1; i >= 0; i--) { + View child = getChildAt(i); + child.setVisibility(i == whichChild ? View.VISIBLE : View.GONE); } } @Implementation - public int getDisplayedChild() { - return mWhichChild; + public View getCurrentView() { + return getChildAt(getDisplayedChild()); } @Implementation - public View getCurrentView() { - return getChildAt(mWhichChild); + public void showNext() { + setDisplayedChild((getDisplayedChild() + 1) % getChildCount()); + } + + @Implementation + public void showPrevious() { + setDisplayedChild(getDisplayedChild() == 0 ? getChildCount() - 1 : getDisplayedChild() - 1); } } diff --git a/src/main/java/com/xtremelabs/robolectric/shadows/ShadowViewGroup.java b/src/main/java/com/xtremelabs/robolectric/shadows/ShadowViewGroup.java index 93669fecc..35d3294f2 100644 --- a/src/main/java/com/xtremelabs/robolectric/shadows/ShadowViewGroup.java +++ b/src/main/java/com/xtremelabs/robolectric/shadows/ShadowViewGroup.java @@ -71,6 +71,7 @@ public class ShadowViewGroup extends ShadowView { children.add(index, child); } shadowOf(child).parent = this; + addedChild(child); } @Implementation @@ -90,6 +91,15 @@ public class ShadowViewGroup extends ShadowView { } @Implementation + public void removeView(View child) { + // Android's ViewGroup ignores the child when it is null. Do the same here. + if (child == null) return; + shadowOf(child).parent = null; + children.remove(child); + removedChild(child); + } + + @Implementation public int indexOfChild(View child) { int count = getChildCount(); for (int i = 0; i < count; i++) { @@ -107,21 +117,23 @@ public class ShadowViewGroup extends ShadowView { @Implementation public View getChildAt(int index) { - if( index >= children.size() ){ return null; } - return children.get(index); + return isValidIndex(index) ? children.get(index) : null; } @Implementation public void removeAllViews() { for (View child : children) { shadowOf(child).parent = null; + removedChild(child); } children.clear(); } @Implementation public void removeViewAt(int position) { - shadowOf(children.remove(position)).parent = null; + View child = children.remove(position); + shadowOf(child).parent = null; + removedChild(child); } @Override @@ -200,15 +212,31 @@ public class ShadowViewGroup extends ShadowView { public AnimationListener getLayoutAnimationListener() { return animListener; } - + + @Override + public void onAttachedToWindow() { + super.onAttachedToWindow(); + for (int i = 0; i < getChildCount(); i++) { + shadowOf(getChildAt(i)).callOnAttachedToWindow(); + } + } + + @Override + public void onDetachedFromWindow() { + for (int i = 0; i < getChildCount(); i++) { + shadowOf(getChildAt(i)).callOnDetachedFromWindow(); + } + super.onDetachedFromWindow(); + } + @Implementation public void setLayoutAnimation(LayoutAnimationController layoutAnim) { - this.layoutAnim = layoutAnim; + this.layoutAnim = layoutAnim; } - + @Implementation public LayoutAnimationController getLayoutAnimation() { - return layoutAnim; + return layoutAnim; } @Implementation @@ -219,4 +247,21 @@ public class ShadowViewGroup extends ShadowView { public boolean getDisallowInterceptTouchEvent() { return disallowInterceptTouchEvent; } + + protected void addedChild(View child) { + if (isAttachedToWindow()) shadowOf(child).callOnAttachedToWindow(); + setChildLayoutParams(child); + } + + protected void removedChild(View child) { + if (isAttachedToWindow()) shadowOf(child).callOnDetachedFromWindow(); + } + + protected void setChildLayoutParams(View child) { + shadowOf(child).setLayoutParams(new ViewGroup.LayoutParams(0, 0)); + } + + private boolean isValidIndex(int i) { + return i >= 0 && i < children.size(); + } } diff --git a/src/main/java/com/xtremelabs/robolectric/shadows/UsesResources.java b/src/main/java/com/xtremelabs/robolectric/shadows/UsesResources.java new file mode 100644 index 000000000..f44383287 --- /dev/null +++ b/src/main/java/com/xtremelabs/robolectric/shadows/UsesResources.java @@ -0,0 +1,10 @@ +package com.xtremelabs.robolectric.shadows; + +import android.content.res.Resources; + +/** + * Indicates that this shadow class would like to have a Resources instance injected at creation. + */ +public interface UsesResources { + void injectResources(Resources resources); +} diff --git a/src/main/java/com/xtremelabs/robolectric/tester/android/content/TestSharedPreferences.java b/src/main/java/com/xtremelabs/robolectric/tester/android/content/TestSharedPreferences.java index 21f2e3ade..ef0e7a9f4 100644 --- a/src/main/java/com/xtremelabs/robolectric/tester/android/content/TestSharedPreferences.java +++ b/src/main/java/com/xtremelabs/robolectric/tester/android/content/TestSharedPreferences.java @@ -2,7 +2,11 @@ package com.xtremelabs.robolectric.tester.android.content; import android.content.SharedPreferences; -import java.util.*; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; public class TestSharedPreferences implements SharedPreferences { @@ -172,14 +176,16 @@ public class TestSharedPreferences implements SharedPreferences { commit(); } - @Override - public Editor putStringSet(String key, Set<String> values) { - return this; - } + @Override + public Editor putStringSet(String key, Set<String> strings) { + editsThatNeedCommit.put(key, strings); + editsThatNeedRemove.remove(key); + return this; + } } - @Override - public Set<String> getStringSet(String key, Set<String> defValues) { - return null; - } + @Override + public Set<String> getStringSet(String key, Set<String> strings) { + return (Set<String>) getValue(key, strings); + } } diff --git a/src/main/java/com/xtremelabs/robolectric/tester/android/content/pm/StubPackageManager.java b/src/main/java/com/xtremelabs/robolectric/tester/android/content/pm/StubPackageManager.java index 7f4928285..884191910 100644 --- a/src/main/java/com/xtremelabs/robolectric/tester/android/content/pm/StubPackageManager.java +++ b/src/main/java/com/xtremelabs/robolectric/tester/android/content/pm/StubPackageManager.java @@ -3,17 +3,7 @@ package com.xtremelabs.robolectric.tester.android.content.pm; import android.content.ComponentName; import android.content.Intent; import android.content.IntentFilter; -import android.content.pm.ActivityInfo; -import android.content.pm.ApplicationInfo; -import android.content.pm.FeatureInfo; -import android.content.pm.InstrumentationInfo; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager; -import android.content.pm.PermissionGroupInfo; -import android.content.pm.PermissionInfo; -import android.content.pm.ProviderInfo; -import android.content.pm.ResolveInfo; -import android.content.pm.ServiceInfo; +import android.content.pm.*; import android.content.res.Resources; import android.content.res.XmlResourceParser; import android.graphics.drawable.Drawable; @@ -291,8 +281,8 @@ public class StubPackageManager extends PackageManager { } @Override public void verifyPendingInstall(int id, int verificationCode) { - } + } @Override public void setInstallerPackageName(String targetPackage, String installerPackageName) { - } + } } diff --git a/src/main/java/com/xtremelabs/robolectric/tester/android/database/TestCursor.java b/src/main/java/com/xtremelabs/robolectric/tester/android/database/TestCursor.java index 84a3d85b1..b1af0daf0 100644 --- a/src/main/java/com/xtremelabs/robolectric/tester/android/database/TestCursor.java +++ b/src/main/java/com/xtremelabs/robolectric/tester/android/database/TestCursor.java @@ -199,10 +199,10 @@ public class TestCursor implements Cursor { throw new UnsupportedOperationException(); } - @Override + @Override public int getType(int columnIndex) { throw new UnsupportedOperationException(); - } + } /** * Mimics ContentResolver.query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) diff --git a/src/main/java/com/xtremelabs/robolectric/tester/android/util/TestAttributeSet.java b/src/main/java/com/xtremelabs/robolectric/tester/android/util/TestAttributeSet.java index e59ab4ef1..cd617d33b 100644 --- a/src/main/java/com/xtremelabs/robolectric/tester/android/util/TestAttributeSet.java +++ b/src/main/java/com/xtremelabs/robolectric/tester/android/util/TestAttributeSet.java @@ -72,10 +72,12 @@ public class TestAttributeSet implements AttributeSet { public int getAttributeIntValue(String namespace, String attribute, int defaultValue) { String value = getAttributeValueInMap(namespace, attribute); - if (attrResourceLoader.hasAttributeFor(viewClass, "xxx", attribute)) { - value = attrResourceLoader.convertValueToEnum(viewClass, "xxx", attribute, value); + if (attrResourceLoader.hasAttributeFor(viewClass, namespace, attribute)) { + value = attrResourceLoader.convertValueToEnum(viewClass, namespace, attribute, value); } + if (value != null && value.startsWith("0x")) value = value.substring(2); + return (value != null) ? Integer.valueOf(value) : defaultValue; } @@ -123,11 +125,6 @@ public class TestAttributeSet implements AttributeSet { @Override public float getAttributeFloatValue(String namespace, String attribute, float defaultValue) { String value = getAttributeValueInMap(namespace, attribute); - - if (attrResourceLoader.hasAttributeFor(viewClass, "xxx", attribute)) { - value = attrResourceLoader.convertValueToEnum(viewClass, "xxx", attribute, value); - } - return (value != null) ? Float.valueOf(value) : defaultValue; } @@ -141,8 +138,7 @@ public class TestAttributeSet implements AttributeSet { throw new UnsupportedOperationException(); } - @Override - public int getAttributeResourceValue(String namespace, String attribute, int defaultValue) { + @Override public int getAttributeResourceValue(String namespace, String attribute, int defaultValue) { String value = getAttributeValueInMap(namespace, attribute); Integer resourceId = defaultValue; if (value != null) { @@ -155,7 +151,9 @@ public class TestAttributeSet implements AttributeSet { public int getAttributeResourceValue(int resourceId, int defaultValue) { String attrName = resourceExtractor.getResourceName(resourceId); String value = getAttributeValueInMap(null, attrName); - return (value == null) ? defaultValue : resourceExtractor.getResourceId(value); + Integer extracted = null; + if (value != null) extracted = resourceExtractor.getResourceId(value); + return (extracted == null) ? defaultValue : extracted; } @Override @@ -188,9 +186,17 @@ public class TestAttributeSet implements AttributeSet { throw new UnsupportedOperationException(); } - @Override - public int getStyleAttribute() { - throw new UnsupportedOperationException(); + @Override public int getStyleAttribute() { + String value = attributes.get("style"); + if (value == null) { + // Per Android specifications, return 0 if there is no style. + return 0; + } + if (resourceExtractor != null) { + Integer i = resourceExtractor.getResourceId(value); + if (i != null) return i; + } + return 0; } public void validateStrictI18n() { diff --git a/src/main/java/com/xtremelabs/robolectric/tester/android/view/TestMenuItem.java b/src/main/java/com/xtremelabs/robolectric/tester/android/view/TestMenuItem.java index 4afc9fb78..d1ad2daaa 100644 --- a/src/main/java/com/xtremelabs/robolectric/tester/android/view/TestMenuItem.java +++ b/src/main/java/com/xtremelabs/robolectric/tester/android/view/TestMenuItem.java @@ -1,11 +1,9 @@ package com.xtremelabs.robolectric.tester.android.view; -import com.xtremelabs.robolectric.Robolectric; - -import android.app.Application; import android.content.Intent; import android.graphics.drawable.Drawable; import android.view.*; +import com.xtremelabs.robolectric.Robolectric; public class TestMenuItem implements MenuItem { diff --git a/src/main/java/com/xtremelabs/robolectric/tester/android/view/TestWindow.java b/src/main/java/com/xtremelabs/robolectric/tester/android/view/TestWindow.java index d222fe7e9..fa1b1a067 100644 --- a/src/main/java/com/xtremelabs/robolectric/tester/android/view/TestWindow.java +++ b/src/main/java/com/xtremelabs/robolectric/tester/android/view/TestWindow.java @@ -1,21 +1,17 @@ package com.xtremelabs.robolectric.tester.android.view; +import android.R; import android.content.Context; import android.content.res.Configuration; import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Bundle; -import android.view.InputQueue; -import android.view.KeyEvent; -import android.view.LayoutInflater; -import android.view.MotionEvent; -import android.view.SurfaceHolder; -import android.view.View; -import android.view.ViewGroup; -import android.view.Window; -import android.view.WindowManager; +import android.view.*; +import android.widget.FrameLayout; import com.xtremelabs.robolectric.Robolectric; +import static com.xtremelabs.robolectric.Robolectric.shadowOf; + public class TestWindow extends Window { public int flags; public int requestedFeatureId; @@ -23,6 +19,7 @@ public class TestWindow extends Window { public int featureDrawableResourceResId; public int softInputMode; private TestWindowManager windowManager; + private View contentView; public TestWindow(Context context) { super(context); @@ -58,15 +55,21 @@ public class TestWindow extends Window { } @Override public void setContentView(int layoutResID) { + setContentView(getLayoutInflater().inflate(layoutResID, null)); } @Override public void setContentView(View view) { + if (contentView != null) shadowOf(contentView).callOnDetachedFromWindow(); + contentView = view; + if (contentView != null) shadowOf(contentView).callOnAttachedToWindow(); } @Override public void setContentView(View view, ViewGroup.LayoutParams params) { + setContentView(view); } @Override public void addContentView(View view, ViewGroup.LayoutParams params) { + setContentView(view); } @Override public View getCurrentFocus() { @@ -74,7 +77,7 @@ public class TestWindow extends Window { } @Override public LayoutInflater getLayoutInflater() { - return null; + return LayoutInflater.from(Robolectric.application); } @Override public void setTitle(CharSequence title) { @@ -92,6 +95,10 @@ public class TestWindow extends Window { @Override public void togglePanel(int featureId, KeyEvent event) { } + @Override + public void invalidatePanelMenu(int i) { + } + @Override public boolean performPanelShortcut(int featureId, int keyCode, KeyEvent event, int flags) { return false; } @@ -145,14 +152,35 @@ public class TestWindow extends Window { return false; } + @Override + public boolean superDispatchGenericMotionEvent(MotionEvent motionEvent) { + return false; + } + @Override public View getDecorView() { - return new View(Robolectric.application); + final FrameLayout decorView = new FrameLayout(Robolectric.application); + + // On a typical Android device you can call: + // myWindow.getDecorView().findViewById(android.R.content) + final FrameLayout contentWrapper = new FrameLayout(Robolectric.application); + contentWrapper.setId(R.id.content); + + decorView.addView(contentWrapper); + if (contentView != null) { + contentWrapper.addView(contentView); + } + return decorView; } @Override public View peekDecorView() { return null; } + @Override + public View findViewById(int id) { + return getDecorView().findViewById(id); + } + @Override public Bundle saveHierarchyState() { return null; } @@ -184,14 +212,7 @@ public class TestWindow extends Window { this.softInputMode = softInputMode; } - @Override public void invalidatePanelMenu(int featureId) { - } - @Override public boolean superDispatchKeyShortcutEvent(KeyEvent event) { return false; } - - @Override public boolean superDispatchGenericMotionEvent(MotionEvent event) { - return false; - } } diff --git a/src/main/java/com/xtremelabs/robolectric/tester/org/apache/http/FakeHttpLayer.java b/src/main/java/com/xtremelabs/robolectric/tester/org/apache/http/FakeHttpLayer.java index 67e5441dc..257e4e749 100644 --- a/src/main/java/com/xtremelabs/robolectric/tester/org/apache/http/FakeHttpLayer.java +++ b/src/main/java/com/xtremelabs/robolectric/tester/org/apache/http/FakeHttpLayer.java @@ -26,6 +26,7 @@ public class FakeHttpLayer { HttpResponse defaultHttpResponse; private HttpResponse defaultResponse; private boolean interceptHttpRequests = true; + private boolean logHttpRequests = false; public HttpRequestInfo getLastSentHttpRequestInfo() { List<HttpRequestInfo> requestInfos = Robolectric.getFakeHttpLayer().getSentHttpRequestInfos(); @@ -101,12 +102,20 @@ public class FakeHttpLayer { } } + System.err.println("Unexpected HTTP call " + httpRequest.getRequestLine()); + return defaultHttpResponse; } public HttpResponse emulateRequest(HttpHost httpHost, HttpRequest httpRequest, HttpContext httpContext, RequestDirector requestDirector) throws HttpException, IOException { + if (logHttpRequests) { + System.out.println(" <-- " + httpRequest.getRequestLine()); + } HttpResponse httpResponse = findResponse(httpRequest); - + if (logHttpRequests) { + System.out.println(" --> " + (httpResponse == null ? null : httpResponse.getStatusLine().getStatusCode())); + } + if (httpResponse == null) { throw new RuntimeException("Unexpected call to execute, no pending responses are available. See Robolectric.addPendingResponse(). Request was: " + httpRequest.getRequestLine().getMethod() + " " + httpRequest.getRequestLine().getUri()); @@ -124,7 +133,6 @@ public class FakeHttpLayer { addHttpResponse(httpResponse); return httpResponse; } - public boolean hasPendingResponses() { return !pendingHttpResponses.isEmpty(); } @@ -171,6 +179,18 @@ public class FakeHttpLayer { return httpRequestInfos.get(index); } + public HttpRequestInfo getNextSentHttpRequestInfo() { + return httpRequestInfos.size() > 0 ? httpRequestInfos.remove(0) : null; + } + + public void logHttpRequests() { + logHttpRequests = true; + } + + public void silence() { + logHttpRequests = false; + } + public List<HttpRequestInfo> getSentHttpRequestInfos() { return new ArrayList<HttpRequestInfo>(httpRequestInfos); } diff --git a/src/main/java/com/xtremelabs/robolectric/tester/org/apache/http/HttpEntityStub.java b/src/main/java/com/xtremelabs/robolectric/tester/org/apache/http/HttpEntityStub.java index 3d3c05c6d..cb861377a 100644 --- a/src/main/java/com/xtremelabs/robolectric/tester/org/apache/http/HttpEntityStub.java +++ b/src/main/java/com/xtremelabs/robolectric/tester/org/apache/http/HttpEntityStub.java @@ -12,7 +12,7 @@ import java.io.OutputStream; public class HttpEntityStub implements HttpEntity { @Override public boolean isRepeatable() { - throw new UnsupportedOperationException(); + return true; } @Override public boolean isChunked() { diff --git a/src/main/java/com/xtremelabs/robolectric/util/ReflectionUtil.java b/src/main/java/com/xtremelabs/robolectric/util/ReflectionUtil.java new file mode 100644 index 000000000..ca71ecf61 --- /dev/null +++ b/src/main/java/com/xtremelabs/robolectric/util/ReflectionUtil.java @@ -0,0 +1,20 @@ +package com.xtremelabs.robolectric.util; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +public class ReflectionUtil { + public static Object invoke(Object object, String methodName, Class<?>[] argTypes, Object... args) { + try { + Method onLayout = object.getClass().getDeclaredMethod(methodName, argTypes); + onLayout.setAccessible(true); + return onLayout.invoke(object, args); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } catch (InvocationTargetException e) { + throw new RuntimeException(e); + } catch (NoSuchMethodException e) { + throw new RuntimeException(e); + } + } +} diff --git a/src/main/java/com/xtremelabs/robolectric/util/Transcript.java b/src/main/java/com/xtremelabs/robolectric/util/Transcript.java index ef21ae551..0745fb49c 100644 --- a/src/main/java/com/xtremelabs/robolectric/util/Transcript.java +++ b/src/main/java/com/xtremelabs/robolectric/util/Transcript.java @@ -17,11 +17,36 @@ public class Transcript { assertEquals("Expected no events but got " + events + ".", 0, events.size()); } + /** + * Assert that the transcript contains the expected events, exactly. All events are cleared + * from the transcript. + * + * @param expectedEvents + */ public void assertEventsSoFar(String... expectedEvents) { assertEquals(Arrays.asList(expectedEvents), events); events.clear(); } + /** + * Assert that the transcript contains the expected events in order, but possibly ignoring + * some actual events. For example, if the transcript contains {A, B, C, D, E}, asserting + * on {A, C, E} would pass, {A, D, B} would fail, {E} would pass, and {F} would fail. Events + * up to and including the last expected event are cleared from the transcript. + * + * @param expectedEvents + */ + public void assertEventsInclude(String... expectedEvents) { + List<String> original = new ArrayList<String>(events); + for (String expectedEvent : expectedEvents) { + int index = events.indexOf(expectedEvent); + if (index == -1) { + assertEquals(Arrays.asList(expectedEvents), original); + } + events.subList(0, index + 1).clear(); + } + } + public void clear() { events.clear(); } diff --git a/src/test/java/com/xtremelabs/robolectric/R.java b/src/test/java/com/xtremelabs/robolectric/R.java index 7f328ad03..e5bcfd28d 100644 --- a/src/test/java/com/xtremelabs/robolectric/R.java +++ b/src/test/java/com/xtremelabs/robolectric/R.java @@ -54,6 +54,15 @@ public final class R { public static final int fragment = nextId++; public static final int dynamic_fragment_container = nextId++; public static final int content_view = nextId++; + + public static final int portrait = nextId++; + public static final int landscape = nextId++; + public static final int tacos = nextId++; + public static final int burritos = nextId++; + public static int lam_container = nextId++; + public static int lam_inner_contents = nextId++; + public static int my_fragment = nextId++; + public static int my_landscape_fragment = nextId++; } public static final class string { @@ -87,6 +96,7 @@ public final class R { public static final int clear = nextId++; public static final int color_with_alpha = nextId++; public static final int android_namespaced_black = nextId++; + public static final int android_namespaced_transparent = nextId++; public static final int android_red = nextId++; } @@ -115,6 +125,7 @@ public final class R { public static final int media = nextId++; public static final int inner_merge = nextId++; public static final int included_linear_layout = nextId++; + public static final int edit_text = nextId++; public static final int with_invalid_onclick = nextId++; public static final int text_views = nextId++; public static final int text_views_hints = nextId++; @@ -122,6 +133,9 @@ public final class R { public static final int tab_activity = nextId++; public static final int different_screen_sizes = nextId++; public static final int fragment_activity = nextId++; + public static final int fragment_contents = nextId++; + public static final int lam_outer = nextId++; + public static final int lam_inner = nextId++; } public static final class raw { @@ -129,6 +143,10 @@ public final class R { public static final int raw_no_ext = nextId++; } + public static final class attr { + public static final int isSugary = nextId++; + } + public static final class menu { public static final int test = nextId++; public static final int test_withchilds = nextId++; @@ -160,12 +178,20 @@ public final class R { public static final int test_integer1 = nextId++; public static final int test_integer2 = nextId++; public static final int test_large_hex = nextId++; + public static final int meaning_of_life = nextId++; + public static final int loneliest_number = nextId++; + public static final int there_can_be_only = nextId++; + public static final int hex_int = nextId++; } - + public static final class bool { public static final int false_bool_value = nextId++; public static final int true_bool_value = nextId++; public static final int zero_is_false = nextId++; public static final int integers_are_true = nextId++; } + + public static final class style { + public static int FancyStyle = nextId++; + } } diff --git a/src/test/java/com/xtremelabs/robolectric/RobolectricConfigTest.java b/src/test/java/com/xtremelabs/robolectric/RobolectricConfigTest.java index 2639cb607..d2e6059de 100644 --- a/src/test/java/com/xtremelabs/robolectric/RobolectricConfigTest.java +++ b/src/test/java/com/xtremelabs/robolectric/RobolectricConfigTest.java @@ -1,14 +1,18 @@ package com.xtremelabs.robolectric; -import org.junit.Test; -import org.junit.runner.RunWith; - import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.io.File; +import java.util.List; import static android.content.pm.ApplicationInfo.*; +import static com.xtremelabs.robolectric.RobolectricConfig.fromBaseDirWithLibraries; import static com.xtremelabs.robolectric.util.TestUtil.newConfig; +import static com.xtremelabs.robolectric.util.TestUtil.resourcesBaseDir; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; @@ -63,6 +67,19 @@ public class RobolectricConfigTest { public void shouldReturnPackageNameWhenNoProcessIsSpecifiedInTheManifest() { assertEquals("com.xtremelabs.robolectric", newConfig("TestAndroidManifestWithNoProcess.xml").getProcessName()); } + + @Test public void shouldLoadAllResourcesForLibraries() { + // This intentionally loads from the non standard resources/project.properties + RobolectricConfig config = fromBaseDirWithLibraries(resourcesBaseDir()); + + List<File> resourceFileDirs = config.getResourcePath(); + assertEquals("there should be 5 resource locations", 5, resourceFileDirs.size()); + assertEquals("./src/test/resources/res", resourceFileDirs.get(0).getPath()); + assertEquals("./src/test/resources/../lib1/res", resourceFileDirs.get(1).getPath()); + assertEquals("./src/test/resources/../lib2/res", resourceFileDirs.get(2).getPath()); + assertEquals("./src/test/resources/../lib3/res", resourceFileDirs.get(3).getPath()); + assertEquals("./src/test/resources/../lib4/res", resourceFileDirs.get(4).getPath()); + } @Test public void shouldReadFlagsFromAndroidManifest() throws Exception { diff --git a/src/test/java/com/xtremelabs/robolectric/matchers/ViewVisibilityMatcherTest.java b/src/test/java/com/xtremelabs/robolectric/matchers/ViewVisibilityMatcherTest.java new file mode 100644 index 000000000..208728fa9 --- /dev/null +++ b/src/test/java/com/xtremelabs/robolectric/matchers/ViewVisibilityMatcherTest.java @@ -0,0 +1,73 @@ +package com.xtremelabs.robolectric.matchers; + +import android.view.View; +import com.xtremelabs.robolectric.TestRunners; +import org.hamcrest.Matcher; +import org.hamcrest.StringDescription; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static com.xtremelabs.robolectric.matchers.ViewVisibilityMatcher.*; +import static org.hamcrest.CoreMatchers.not; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; + +@RunWith(TestRunners.WithDefaults.class) +public class ViewVisibilityMatcherTest { + + private View visibleView; + private View invisibleView; + private View goneView; + + @Before public void setUp() throws Exception { + visibleView = new View(null); + visibleView.setVisibility(View.VISIBLE); + invisibleView = new View(null); + invisibleView.setVisibility(View.INVISIBLE); + goneView = new View(null); + goneView.setVisibility(View.GONE); + } + + @Test + public void visibleMatch() throws Exception { + assertThat(visibleView, isVisible()); + assertThat(invisibleView, not(isVisible())); + assertThat(goneView, not(isVisible())); + assertThat(null, not(isVisible())); + } + + @Test + public void invisibleMatch() throws Exception { + assertThat(visibleView, not(isInvisible())); + assertThat(invisibleView, isInvisible()); + assertThat(goneView, not(isInvisible())); + assertThat(null, not(isInvisible())); + } + + @Test + public void goneMatch() throws Exception { + assertThat(visibleView, not(isGone())); + assertThat(invisibleView, not(isGone())); + assertThat(goneView, isGone()); + assertThat(null, not(isGone())); + } + + @Test + public void descriptionShouldIndicateExpectedAndActual() { + Matcher<View> goneMatcher = isGone(); + goneMatcher.matches(visibleView); + StringDescription description = new StringDescription(); + goneMatcher.describeTo(description); + assertEquals("'Visible' to be 'Gone'", description.toString()); + } + + @Test + public void descriptionShouldIndicateNullView() { + Matcher<View> goneMatcher = isGone(); + goneMatcher.matches(null); + StringDescription description = new StringDescription(); + goneMatcher.describeTo(description); + assertEquals("View to be non-null.", description.toString()); + } +} diff --git a/src/test/java/com/xtremelabs/robolectric/res/AttrResourceLoaderTest.java b/src/test/java/com/xtremelabs/robolectric/res/AttrResourceLoaderTest.java index c1b91e75e..73d4a044d 100644 --- a/src/test/java/com/xtremelabs/robolectric/res/AttrResourceLoaderTest.java +++ b/src/test/java/com/xtremelabs/robolectric/res/AttrResourceLoaderTest.java @@ -14,20 +14,30 @@ import static org.hamcrest.CoreMatchers.equalTo; import static org.junit.Assert.assertThat; public class AttrResourceLoaderTest { + public static final String SYSTEM_NAMESPACE = "http://schemas.android.com/apk/res/android"; private AttrResourceLoader attrResourceLoader; @Before public void setUp() throws Exception { + attrResourceLoader = makeResourceLoader(); + new DocumentLoader(attrResourceLoader).loadResourceXmlDir(resourceFile("res", "values")); + new DocumentLoader(attrResourceLoader).loadSystemResourceXmlDir(getSystemResourceDir("values")); + } + + private AttrResourceLoader makeResourceLoader() throws Exception { ResourceExtractor resourceExtractor = new ResourceExtractor(); resourceExtractor.addLocalRClass(R.class); resourceExtractor.addSystemRClass(android.R.class); - attrResourceLoader = new AttrResourceLoader(resourceExtractor); - new DocumentLoader(attrResourceLoader).loadResourceXmlDir(resourceFile("res", "values")); - new DocumentLoader(attrResourceLoader).loadSystemResourceXmlDir(getSystemResourceDir("values")); + return new AttrResourceLoader(resourceExtractor); } @Test - public void testAttributesAreResolved() throws Exception { + public void testUnknownAttributesAreUnknown() throws Exception { + assertThat(attrResourceLoader.hasAttributeFor(CustomView.class, "xxx", "otherItemType"), equalTo(false)); + } + + @Test + public void testInlineEnumAttributesAreResolved() throws Exception { assertThat(attrResourceLoader.convertValueToEnum(CustomView.class, "xxx", "itemType", "integer"), equalTo("0")); assertThat(attrResourceLoader.hasAttributeFor(CustomView.class, "xxx", "itemType"), equalTo(true)); @@ -38,8 +48,32 @@ public class AttrResourceLoaderTest { @Test public void testAttributesAreResolvedForSystemAttrs() throws Exception { String expected = "" + ImageView.ScaleType.FIT_CENTER.ordinal(); - assertThat(attrResourceLoader.convertValueToEnum(ImageView.class, "android", "scaleType", "fitCenter"), equalTo(expected)); - assertThat(attrResourceLoader.hasAttributeFor(ImageView.class, "android", "scaleType"), equalTo(true)); + assertThat(attrResourceLoader.convertValueToEnum(ImageView.class, SYSTEM_NAMESPACE, "scaleType", "fitCenter"), equalTo(expected)); + assertThat(attrResourceLoader.hasAttributeFor(ImageView.class, SYSTEM_NAMESPACE, "scaleType"), equalTo(true)); + } + + @Test + public void testGlobalEnumAttributesAreResolved() throws Exception { + assertThat(attrResourceLoader.convertValueToEnum(CustomView.class, "xxx", "keycode", "KEYCODE_SOFT_RIGHT"), equalTo("2")); + assertThat(attrResourceLoader.hasAttributeFor(CustomView.class, "xxx", "keycode"), equalTo(true)); + + assertThat(attrResourceLoader.convertValueToEnum(CustomView.class, "xxx", "keycode", "KEYCODE_HOME"), equalTo("3")); + } + + @Test + public void testInlineFlagAttributesAreResolved() throws Exception { + assertThat(attrResourceLoader.convertValueToEnum(CustomView.class, "xxx", "scrollbars", "horizontal"), equalTo("0x00000100")); + assertThat(attrResourceLoader.hasAttributeFor(CustomView.class, "xxx", "scrollbars"), equalTo(true)); + + assertThat(attrResourceLoader.convertValueToEnum(CustomView.class, "xxx", "scrollbars", "vertical"), equalTo("0x00000200")); + } + + @Test + public void testGlobalFlagAttributesAreResolved() throws Exception { + assertThat(attrResourceLoader.convertValueToEnum(CustomView.class, "xxx", "gravity", "center"), equalTo("0x11")); + assertThat(attrResourceLoader.hasAttributeFor(CustomView.class, "xxx", "gravity"), equalTo(true)); + + assertThat(attrResourceLoader.convertValueToEnum(CustomView.class, "xxx", "gravity", "fill_vertical"), equalTo("0x70")); } @Test @@ -47,6 +81,20 @@ public class AttrResourceLoaderTest { assertThat(attrResourceLoader.convertValueToEnum(SubCustomView.class, "xxx", "itemType", "integer"), equalTo("0")); assertThat(attrResourceLoader.hasAttributeFor(SubCustomView.class, "xxx", "itemType"), equalTo(true)); } + + @Test + public void systemResourcesArePrefixedAndroid() throws Exception { + attrResourceLoader = makeResourceLoader(); + new DocumentLoader(attrResourceLoader).loadSystemResourceXmlDir(resourceFile("res", "values")); + + assertThat(attrResourceLoader.convertValueToEnum(CustomView.class, SYSTEM_NAMESPACE, + "gravity", "center"), equalTo("0x11")); + assertThat(attrResourceLoader.hasAttributeFor(CustomView.class, SYSTEM_NAMESPACE, + "gravity"), equalTo(true)); + + assertThat(attrResourceLoader.convertValueToEnum(CustomView.class, SYSTEM_NAMESPACE, + "gravity", "fill_vertical"), equalTo("0x70")); + } private class SubCustomView extends CustomView { public SubCustomView(Context context, AttributeSet attrs) { diff --git a/src/test/java/com/xtremelabs/robolectric/res/ColorResourceLoaderTest.java b/src/test/java/com/xtremelabs/robolectric/res/ColorResourceLoaderTest.java index 60f183212..d3d893df5 100644 --- a/src/test/java/com/xtremelabs/robolectric/res/ColorResourceLoaderTest.java +++ b/src/test/java/com/xtremelabs/robolectric/res/ColorResourceLoaderTest.java @@ -59,6 +59,11 @@ public class ColorResourceLoaderTest { } @Test + public void shouldReturnAndroidTransparent() throws Exception { + assertThat(colorResourceLoader.getValue(android.R.color.transparent), equalTo(Color.TRANSPARENT)); + } + + @Test public void shouldSupportCarrierDefinedColors() throws Exception { assertThat(colorResourceLoader.getValue(android.R.color.background_dark), equalTo(0xFF000000)); } @@ -66,6 +71,7 @@ public class ColorResourceLoaderTest { @Test public void shouldParseAndroidColorReferences() throws Exception { assertThat(colorResourceLoader.getValue(R.color.android_namespaced_black), equalTo(Color.BLACK)); + assertThat(colorResourceLoader.getValue(R.color.android_namespaced_transparent), equalTo(Color.TRANSPARENT)); } @Test diff --git a/src/test/java/com/xtremelabs/robolectric/res/DrawableResourceLoaderTest.java b/src/test/java/com/xtremelabs/robolectric/res/DrawableResourceLoaderTest.java index 031a6c427..fdb9d5bfb 100644 --- a/src/test/java/com/xtremelabs/robolectric/res/DrawableResourceLoaderTest.java +++ b/src/test/java/com/xtremelabs/robolectric/res/DrawableResourceLoaderTest.java @@ -34,7 +34,7 @@ public class DrawableResourceLoaderTest { extractor.addLocalRClass(R.class); extractor.addSystemRClass(android.R.class); - resourceLoader = new DrawableResourceLoader(extractor, resourceFile("res")); + resourceLoader = new DrawableResourceLoader(extractor); DocumentLoader documentLoader = new DocumentLoader(resourceLoader); documentLoader.loadResourceXmlDir(resourceFile("res", "drawable")); diff --git a/src/test/java/com/xtremelabs/robolectric/res/IntegerResourceLoaderTest.java b/src/test/java/com/xtremelabs/robolectric/res/IntegerResourceLoaderTest.java index 5c683e3d7..04785b361 100644 --- a/src/test/java/com/xtremelabs/robolectric/res/IntegerResourceLoaderTest.java +++ b/src/test/java/com/xtremelabs/robolectric/res/IntegerResourceLoaderTest.java @@ -1,13 +1,12 @@ package com.xtremelabs.robolectric.res; -import static com.xtremelabs.robolectric.util.TestUtil.resourceFile; -import static org.hamcrest.CoreMatchers.equalTo; -import static org.junit.Assert.assertThat; - +import com.xtremelabs.robolectric.R; import org.junit.Before; import org.junit.Test; -import com.xtremelabs.robolectric.R; +import static com.xtremelabs.robolectric.util.TestUtil.resourceFile; +import static org.hamcrest.CoreMatchers.equalTo; +import static org.junit.Assert.assertThat; public class IntegerResourceLoaderTest { @@ -30,6 +29,17 @@ public class IntegerResourceLoaderTest { assertThat( resourceLoader.getValue( R.integer.test_integer1 ), equalTo( 2000 ) ); assertThat( resourceLoader.getValue( R.integer.test_integer2 ), equalTo( 9 ) ); assertThat( resourceLoader.getValue( R.integer.test_large_hex), equalTo( 0xFFFF0000 ) ); + assertThat(resourceLoader.getValue(R.integer.meaning_of_life), equalTo(42)); + assertThat(resourceLoader.getValue(R.integer.loneliest_number), equalTo(1)); } + @Test + public void testHexValuesAreResolved() throws Exception { + assertThat(resourceLoader.getValue(R.integer.hex_int), equalTo((int)Long.parseLong("FFFF0000", 16))); + } + + @Test + public void shouldResolveStringReferences() throws Exception { + assertThat(resourceLoader.getValue(R.integer.there_can_be_only), equalTo(1)); + } } diff --git a/src/test/java/com/xtremelabs/robolectric/res/PreferenceLoaderTest.java b/src/test/java/com/xtremelabs/robolectric/res/PreferenceLoaderTest.java index 06b9e073a..e49dc155f 100644 --- a/src/test/java/com/xtremelabs/robolectric/res/PreferenceLoaderTest.java +++ b/src/test/java/com/xtremelabs/robolectric/res/PreferenceLoaderTest.java @@ -20,39 +20,38 @@ import com.xtremelabs.robolectric.R; import com.xtremelabs.robolectric.Robolectric; import com.xtremelabs.robolectric.util.I18nException; -import static org.hamcrest.CoreMatchers.equalTo; -import static org.hamcrest.CoreMatchers.instanceOf; +import static org.hamcrest.CoreMatchers.*; import static org.junit.Assert.assertThat; @RunWith(TestRunners.WithDefaults.class) public class PreferenceLoaderTest { - private PreferenceLoader prefLoader; - + private PreferenceLoader prefLoader; + @Before public void setUp() throws Exception { Robolectric.bindDefaultShadowClasses(); - + ResourceExtractor resourceExtractor = new ResourceExtractor(); resourceExtractor.addLocalRClass(R.class); StringResourceLoader stringResourceLoader = new StringResourceLoader(resourceExtractor); new DocumentLoader(stringResourceLoader).loadResourceXmlDir(resourceFile("res", "values")); prefLoader = new PreferenceLoader(resourceExtractor); - new DocumentLoader(prefLoader).loadResourceXmlDir(resourceFile("res", "xml")); + new DocumentLoader(prefLoader).loadResourceXmlDir(resourceFile("res", "xml")); } - + @Test public void shouldCreateCorrectClasses() { - PreferenceScreen screen = prefLoader.inflatePreferences(new Activity(), "xml/preferences"); - assertThatScreenMatchesExpected(screen); + PreferenceScreen screen = prefLoader.inflatePreferences(new Activity(), "xml/preferences"); + assertThatScreenMatchesExpected(screen); } - + @Test public void shouldLoadByResourceId() { - PreferenceScreen screen = prefLoader.inflatePreferences(new Activity(), R.xml.preferences); - assertThatScreenMatchesExpected(screen); + PreferenceScreen screen = prefLoader.inflatePreferences(new Activity(), R.xml.preferences); + assertThatScreenMatchesExpected(screen); } - - @Test(expected=I18nException.class) + + @Test(expected=I18nException.class) public void shouldThrowI18nExceptionOnPrefsWithBareStrings() throws Exception { ResourceExtractor resourceExtractor = new ResourceExtractor(); resourceExtractor.addLocalRClass(R.class); @@ -60,21 +59,28 @@ public class PreferenceLoaderTest { new DocumentLoader(stringResourceLoader).loadResourceXmlDir(resourceFile("res", "values")); prefLoader = new PreferenceLoader(resourceExtractor); prefLoader.setStrictI18n(true); - new DocumentLoader(prefLoader).loadResourceXmlDir(resourceFile("res", "xml")); + new DocumentLoader(prefLoader).loadResourceXmlDir(resourceFile("res", "xml")); prefLoader.inflatePreferences(Robolectric.application, R.xml.preferences); } - + protected void assertThatScreenMatchesExpected(PreferenceScreen screen) { - assertThat(screen.getPreferenceCount(), equalTo(6)); - - assertThat(screen.getPreference(0), instanceOf(PreferenceCategory.class)); - assertThat(((PreferenceCategory)screen.getPreference(0)).getPreference(0), instanceOf(Preference.class)); - - assertThat(screen.getPreference(1), instanceOf(CheckBoxPreference.class)); - assertThat(screen.getPreference(2), instanceOf(EditTextPreference.class)); - assertThat(screen.getPreference(3), instanceOf(ListPreference.class)); - assertThat(screen.getPreference(4), instanceOf(Preference.class)); - assertThat(screen.getPreference(5), instanceOf(RingtonePreference.class)); + assertThat(screen.getPreferenceCount(), equalTo(7)); + + assertThat(screen.getPreference(0), instanceOf(PreferenceCategory.class)); + assertThat(((PreferenceCategory)screen.getPreference(0)).getPreference(0), instanceOf(Preference.class)); + + PreferenceScreen innerScreen = (PreferenceScreen) screen.getPreference(1); + assertThat(innerScreen, instanceOf(PreferenceScreen.class)); + assertThat(innerScreen.getKey().toString(), is("screen")); + assertThat(innerScreen.getTitle().toString(), is("Screen Test")); + assertThat(innerScreen.getSummary(), nullValue()); + assertThat(innerScreen.getPreference(0), instanceOf(Preference.class)); + + assertThat(screen.getPreference(2), instanceOf(CheckBoxPreference.class)); + assertThat(screen.getPreference(3), instanceOf(EditTextPreference.class)); + assertThat(screen.getPreference(4), instanceOf(ListPreference.class)); + assertThat(screen.getPreference(5), instanceOf(Preference.class)); + assertThat(screen.getPreference(6), instanceOf(RingtonePreference.class)); } } diff --git a/src/test/java/com/xtremelabs/robolectric/res/RawResourceLoaderTest.java b/src/test/java/com/xtremelabs/robolectric/res/RawResourceLoaderTest.java index d1662afa8..ffbb7b588 100644 --- a/src/test/java/com/xtremelabs/robolectric/res/RawResourceLoaderTest.java +++ b/src/test/java/com/xtremelabs/robolectric/res/RawResourceLoaderTest.java @@ -2,17 +2,12 @@ package com.xtremelabs.robolectric.res; import com.xtremelabs.robolectric.R; import com.xtremelabs.robolectric.TestRunners; +import com.xtremelabs.robolectric.util.TestUtil; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import java.io.BufferedReader; -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 static com.xtremelabs.robolectric.util.TestUtil.resourceFile; import static org.junit.Assert.assertEquals; @@ -31,27 +26,12 @@ public class RawResourceLoaderTest { @Test public void shouldReturnRawResourcesWithExtensions() throws Exception { InputStream is = rawResourceLoader.getValue(R.raw.raw_resource); - assertEquals("raw txt file contents", readString(is)); + assertEquals("raw txt file contents", TestUtil.readString(is)); } @Test public void shouldReturnRawResourcesWithoutExtensions() throws Exception { InputStream is = rawResourceLoader.getValue(R.raw.raw_no_ext); - assertEquals("no ext file contents", readString(is)); - } - - private static String readString(InputStream is) throws IOException { - Writer writer = new StringWriter(); - char[] buffer = new char[1024]; - try { - Reader reader = new BufferedReader(new InputStreamReader(is, "UTF-8")); - int n; - while ((n = reader.read(buffer)) != -1) { - writer.write(buffer, 0, n); - } - } finally { - is.close(); - } - return writer.toString(); + assertEquals("no ext file contents", TestUtil.readString(is)); } } diff --git a/src/test/java/com/xtremelabs/robolectric/res/ResourceLoaderTest.java b/src/test/java/com/xtremelabs/robolectric/res/ResourceLoaderTest.java index dcb155d37..9302f21e9 100644 --- a/src/test/java/com/xtremelabs/robolectric/res/ResourceLoaderTest.java +++ b/src/test/java/com/xtremelabs/robolectric/res/ResourceLoaderTest.java @@ -49,14 +49,14 @@ public class ResourceLoaderTest { ViewGroup vg = new FrameLayout(Robolectric.application); resourceLoader.inflateView(Robolectric.application, R.layout.text_views, vg); } - + @Test(expected=I18nException.class) public void shouldThrowExceptionOnI18nStrictModeInflateMenu() throws Exception { ResourceLoader resourceLoader = new ResourceLoader(DEFAULT_SDK_VERSION, R.class, resourceFile("res"), resourceFile("menu")); resourceLoader.setStrictI18n(true); resourceLoader.inflateMenu(Robolectric.application, R.menu.test, null); } - + @Test(expected=I18nException.class) public void shouldThrowExceptionOnI18nStrictModeInflatePreferences() throws Exception { ResourceLoader resourceLoader = new ResourceLoader(DEFAULT_SDK_VERSION, R.class, resourceFile("res"), resourceFile("xml")); diff --git a/src/test/java/com/xtremelabs/robolectric/res/TestAttributeSetTest.java b/src/test/java/com/xtremelabs/robolectric/res/TestAttributeSetTest.java index 73391c271..5afa7c49f 100644 --- a/src/test/java/com/xtremelabs/robolectric/res/TestAttributeSetTest.java +++ b/src/test/java/com/xtremelabs/robolectric/res/TestAttributeSetTest.java @@ -113,7 +113,7 @@ public class TestAttributeSetTest { } @Test - public void getAttributeValue_shouldReturnValueFromAttribute() throws Exception { + public void getAttributeValue_byName_shouldReturnValueFromAttribute() throws Exception { attributes.put("isSugary", "oh heck yeah"); TestAttributeSet testAttributeSet = new TestAttributeSet(attributes, null, null, null, false); @@ -121,6 +121,14 @@ public class TestAttributeSetTest { } @Test + public void getAttributeValue_byId_shouldReturnValueFromAttribute() throws Exception { + attributes.put("isSugary", "oh heck yeah"); + + TestAttributeSet testAttributeSet = new TestAttributeSet(attributes, resourceExtractor, null, null, false); + assertThat(testAttributeSet.getAttributeValue(R.attr.isSugary), equalTo("oh heck yeah")); + } + + @Test public void getAttributeIntValue_shouldReturnValueFromAttribute() throws Exception { attributes.put("sugarinessPercent", "100"); @@ -152,4 +160,66 @@ public class TestAttributeSetTest { int nonExistantResource = 12345; assertThat(testAttributeSet.getAttributeValue(nonExistantResource), nullValue()); } + + @Test + public void getAttributeIntValue_shouldReturnValueFromAttributeWhenNotInAttributeSet() throws Exception { + AttrResourceLoader resourceLoader = new AttrResourceLoader(resourceExtractor); + TestAttributeSet testAttributeSet = new TestAttributeSet(attributes, null, resourceLoader, null, false); + assertThat(testAttributeSet.getAttributeIntValue("some namespace", "sugarinessPercent", 42), equalTo(42)); + } + + @Test + public void getAttributeIntValue_shouldReturnEnumValuesForEnumAttributesWhenNotInAttributeSet() throws Exception { + AttrResourceLoader resourceLoader = new AttrResourceLoader(resourceExtractor); + TestAttributeSet testAttributeSet = new TestAttributeSet(attributes, null, resourceLoader, null, false); + assertThat(testAttributeSet.getAttributeIntValue("some namespace", "itemType", 24), equalTo(24)); + } + + @Test + public void getAttributeFloatValue_shouldGetFloatValuesFromAttributes() throws Exception { + attributes.put("sugaryScale", "1234.456"); + + TestAttributeSet testAttributeSet = new TestAttributeSet(attributes, null, null, null, false); + assertThat(testAttributeSet.getAttributeFloatValue("some namespace", "sugaryScale", 78.9f), equalTo(1234.456f)); + } + + @Test + public void getAttributeFloatValue_withNamespace_shouldGetFloatValuesFromAttributes() throws Exception { + attributes.put("xxx:sugaryScale", "1234.456"); + + TestAttributeSet testAttributeSet = new TestAttributeSet(attributes, null, null, null, false); + assertThat(testAttributeSet.getAttributeFloatValue("some namespace", "sugaryScale", 78.9f), equalTo(1234.456f)); + } + + @Test + public void getAttributeFloatValue_shouldReturnDefaultFloatValueWhenNotInAttributeSet() throws Exception { + TestAttributeSet testAttributeSet = new TestAttributeSet(attributes, null, null, null, false); + assertThat(testAttributeSet.getAttributeFloatValue("some namespace", "sugaryScale", 78.9f), equalTo(78.9f)); + } + + @Test + public void getStyleAttribute_doesNotThrowException() throws Exception { + TestAttributeSet testAttributeSet = new TestAttributeSet(attributes, null, null, null, false); + testAttributeSet.getStyleAttribute(); + } + + @Test + public void getStyleAttribute_returnsZeroWhenNoStyle() throws Exception { + TestAttributeSet testAttributeSet = new TestAttributeSet(attributes, null, null, null, false); + assertThat(testAttributeSet.getStyleAttribute(), equalTo(0)); + } + + @Test + public void getStyleAttribute_returnsCorrectValue() throws Exception { + attributes.put("style", "@style/FancyStyle"); + TestAttributeSet testAttributeSet = new TestAttributeSet(attributes, resourceExtractor, null, null, false); + assertThat(testAttributeSet.getStyleAttribute(), equalTo(R.style.FancyStyle)); + } + + @Test + public void getStyleAttribute_doesNotThrowException_whenStyleIsBogus() throws Exception { + attributes.put("style", "@style/bogus_style"); + TestAttributeSet testAttributeSet = new TestAttributeSet(attributes, resourceExtractor, null, null, false); + assertThat(testAttributeSet.getStyleAttribute(), equalTo(0)); + } } diff --git a/src/test/java/com/xtremelabs/robolectric/res/ViewLoaderTest.java b/src/test/java/com/xtremelabs/robolectric/res/ViewLoaderTest.java index 8f8a361b3..8efb96b06 100644 --- a/src/test/java/com/xtremelabs/robolectric/res/ViewLoaderTest.java +++ b/src/test/java/com/xtremelabs/robolectric/res/ViewLoaderTest.java @@ -1,8 +1,11 @@ package com.xtremelabs.robolectric.res; import android.app.Activity; -import android.content.Context; +import android.content.pm.ActivityInfo; import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentActivity; +import android.support.v4.app.FragmentManager; import android.view.View; import android.view.ViewGroup; import android.webkit.WebView; @@ -30,7 +33,7 @@ import static org.junit.Assert.*; @RunWith(TestRunners.WithDefaults.class) public class ViewLoaderTest { private ViewLoader viewLoader; - private Context context; + private FragmentActivity context; @Before public void setUp() throws Exception { @@ -50,7 +53,7 @@ public class ViewLoaderTest { new DocumentLoader(viewLoader).loadResourceXmlDir(resourceFile("res", "layout-land")); new DocumentLoader(viewLoader).loadSystemResourceXmlDir(getSystemResourceDir("layout")); - context = new Activity(); + context = new FragmentActivity(); } @Test @@ -262,6 +265,37 @@ public class ViewLoaderTest { } @Test + public void testFragment() throws Exception { + View v = viewLoader.inflateView(context, "layout/fragment"); + TestUtil.assertInstanceOf(TextView.class, v); + final FragmentManager fragmentManager = context.getSupportFragmentManager(); + Fragment fragment = fragmentManager.findFragmentById(R.id.my_fragment); + assertNotNull(fragment); + } + + @Test + public void testMultiOrientation() throws Exception { + // Default screen orientation should be portrait. + ViewGroup view = (ViewGroup) viewLoader.inflateView(context, "layout/multi_orientation"); + TestUtil.assertInstanceOf(LinearLayout.class, view); + assertEquals(view.getId(), R.id.portrait); + assertSame(context, view.getContext()); + + // Confirm explicit "orientation = portrait" works. + context.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); + view = (ViewGroup) viewLoader.inflateView(context, "layout/multi_orientation"); + TestUtil.assertInstanceOf(LinearLayout.class, view); + assertEquals(view.getId(), R.id.portrait); + assertSame(context, view.getContext()); + + // Confirm explicit "orientation = landscape" works. + context.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); + view = (ViewGroup) viewLoader.inflateView(context, "layout/multi_orientation"); + assertEquals(view.getId(), R.id.landscape); + TestUtil.assertInstanceOf(LinearLayout.class, view); + } + + @Test public void testViewEnabled() throws Exception { View mediaView = viewLoader.inflateView(context, "layout/main"); assertThat(mediaView.findViewById(R.id.time).isEnabled(), equalTo(false)); @@ -340,7 +374,7 @@ public class ViewLoaderTest { viewLoader.inflateView(context,"layout/text_views"); } - public static class ClickActivity extends Activity { + public static class ClickActivity extends FragmentActivity { public boolean clicked = false; @Override protected void onCreate(Bundle savedInstanceState) { diff --git a/src/test/java/com/xtremelabs/robolectric/shadows/AbsoluteLayoutTest.java b/src/test/java/com/xtremelabs/robolectric/shadows/AbsoluteLayoutTest.java new file mode 100644 index 000000000..a8cba2555 --- /dev/null +++ b/src/test/java/com/xtremelabs/robolectric/shadows/AbsoluteLayoutTest.java @@ -0,0 +1,20 @@ +package com.xtremelabs.robolectric.shadows; + +import android.view.ViewGroup; +import android.widget.AbsoluteLayout; +import com.xtremelabs.robolectric.TestRunners; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.hamcrest.CoreMatchers.instanceOf; +import static org.hamcrest.MatcherAssert.assertThat; + +@RunWith(TestRunners.WithDefaults.class) +public class AbsoluteLayoutTest { + @Test + public void getLayoutParams_shouldReturnAbsoluteLayoutParams() throws Exception { + ViewGroup.LayoutParams layoutParams = new AbsoluteLayout(null).getLayoutParams(); + + assertThat(layoutParams, instanceOf(AbsoluteLayout.LayoutParams.class)); + } +} diff --git a/src/test/java/com/xtremelabs/robolectric/shadows/ActivityTest.java b/src/test/java/com/xtremelabs/robolectric/shadows/ActivityTest.java index eb6018197..ec3cece5b 100644 --- a/src/test/java/com/xtremelabs/robolectric/shadows/ActivityTest.java +++ b/src/test/java/com/xtremelabs/robolectric/shadows/ActivityTest.java @@ -60,6 +60,21 @@ public class ActivityTest { } @Test + public void startActivity_shouldDelegateToStartActivityForResult() { + final Transcript transcript = new Transcript(); + Activity activity = new Activity() { + @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { + transcript.add("onActivityResult called with requestCode " + requestCode + ", resultCode " + resultCode + ", intent data " + data.getData()); + } + }; + activity.startActivity(new Intent().setType("image/*")); + + shadowOf(activity).receiveResult(new Intent().setType("image/*"), Activity.RESULT_OK, + new Intent().setData(Uri.parse("content:foo"))); + transcript.assertEventsSoFar("onActivityResult called with requestCode -1, resultCode -1, intent data content:foo"); + } + + @Test public void startActivityForResultAndReceiveResult_shouldSendResponsesBackToActivity() throws Exception { final Transcript transcript = new Transcript(); Activity activity = new Activity() { @@ -340,7 +355,7 @@ public class ActivityTest { int id = activity.getResources().getIdentifier("just_alot_of_crap", "string", "com.xtremelabs.robolectric"); assertTrue(id == 0); } - + @Test public void setDefaultKeyMode_shouldSetKeyMode() { int[] modes = { @@ -628,6 +643,32 @@ public class ActivityTest { shadowOf(activity).callOnStart(); } + @Test + public void getAndSetParentActivity_shouldWorkForTestingPurposes() throws Exception { + Activity parentActivity = new Activity(){}; + Activity activity = new Activity(){}; + shadowOf(activity).setParent(parentActivity); + assertSame(parentActivity, activity.getParent()); + } + + @Test + public void getAndSetRequestedOrientation_shouldRemember() throws Exception { + Activity activity = new Activity(){}; + activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); + assertEquals(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT, activity.getRequestedOrientation()); + } + + @Test + public void getAndSetRequestedOrientation_shouldDelegateToParentIfPresent() throws Exception { + Activity parentActivity = new Activity(){}; + Activity activity = new Activity(){}; + shadowOf(activity).setParent(parentActivity); + parentActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); + assertEquals(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT, activity.getRequestedOrientation()); + activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE); + assertEquals(ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE, parentActivity.getRequestedOrientation()); + } + private static class MyActivity extends Activity { @Override protected void onDestroy() { super.onDestroy(); diff --git a/src/test/java/com/xtremelabs/robolectric/shadows/AlertDialogTest.java b/src/test/java/com/xtremelabs/robolectric/shadows/AlertDialogTest.java index b1443c71b..7a962b374 100644 --- a/src/test/java/com/xtremelabs/robolectric/shadows/AlertDialogTest.java +++ b/src/test/java/com/xtremelabs/robolectric/shadows/AlertDialogTest.java @@ -47,6 +47,16 @@ public class AlertDialogTest { } @Test + public void nullTitleAndMessageAreOkay() throws Exception { + AlertDialog.Builder builder = new AlertDialog.Builder(new ContextWrapper(null)) // + .setTitle(null) // + .setMessage(null); + ShadowAlertDialog shadowAlertDialog = shadowOf(builder.create()); + assertThat(shadowAlertDialog.getTitle().toString(), equalTo("")); + assertThat(shadowAlertDialog.getMessage(), equalTo("")); + } + + @Test public void getLatestAlertDialog_shouldReturnARealAlertDialog() throws Exception { assertThat(ShadowAlertDialog.getLatestAlertDialog(), nullValue()); diff --git a/src/test/java/com/xtremelabs/robolectric/shadows/ApplicationTest.java b/src/test/java/com/xtremelabs/robolectric/shadows/ApplicationTest.java index 7a71f1a5b..20fae2753 100644 --- a/src/test/java/com/xtremelabs/robolectric/shadows/ApplicationTest.java +++ b/src/test/java/com/xtremelabs/robolectric/shadows/ApplicationTest.java @@ -8,6 +8,7 @@ import android.content.Context; import android.content.ContextWrapper; import android.content.Intent; import android.content.IntentFilter; +import android.content.res.Resources; import android.os.IBinder; import android.os.IInterface; import android.os.Parcel; @@ -29,9 +30,7 @@ import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertTrue; import static org.hamcrest.CoreMatchers.sameInstance; import static org.hamcrest.core.IsInstanceOf.instanceOf; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertThat; +import static org.junit.Assert.*; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -288,6 +287,10 @@ public class ApplicationTest { } @Override + public void dumpAsync(FileDescriptor fileDescriptor, String[] strings) throws RemoteException { + } + + @Override public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException { return false; } @@ -300,9 +303,19 @@ public class ApplicationTest { public boolean unlinkToDeath(DeathRecipient recipient, int flags) { return false; } + } - @Override - public void dumpAsync(FileDescriptor fd, String[] args) throws RemoteException { - } + @Test + public void shouldRememberResourcesAfterLazilyLoading() throws Exception { + Application application = new ApplicationResolver(newConfig("TestAndroidManifestWithPackageName.xml")).resolveApplication(); + assertSame(application.getResources(), application.getResources()); + } + + @Test + public void shouldBeAbleToResetResources() throws Exception { + Application application = new ApplicationResolver(newConfig("TestAndroidManifestWithPackageName.xml")).resolveApplication(); + Resources res = application.getResources(); + shadowOf(application).resetResources(); + assertFalse(res == application.getResources()); } } diff --git a/src/test/java/com/xtremelabs/robolectric/shadows/BitmapDrawableTest.java b/src/test/java/com/xtremelabs/robolectric/shadows/BitmapDrawableTest.java index 36a3652c3..ee9712300 100644 --- a/src/test/java/com/xtremelabs/robolectric/shadows/BitmapDrawableTest.java +++ b/src/test/java/com/xtremelabs/robolectric/shadows/BitmapDrawableTest.java @@ -1,6 +1,7 @@ package com.xtremelabs.robolectric.shadows; import android.content.res.Resources; +import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.ColorMatrix; import android.graphics.ColorMatrixColorFilter; @@ -20,6 +21,8 @@ import java.io.InputStream; import static com.xtremelabs.robolectric.Robolectric.shadowOf; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotSame; +import static org.junit.Assert.assertTrue; @RunWith(TestRunners.WithDefaults.class) public class BitmapDrawableTest { @@ -31,12 +34,31 @@ public class BitmapDrawableTest { } @Test + public void constructors_shouldSetBitmap() throws Exception { + Bitmap bitmap = Robolectric.newInstanceOf(Bitmap.class); + BitmapDrawable drawable = new BitmapDrawable(bitmap); + assertEquals(bitmap, drawable.getBitmap()); + + drawable = new BitmapDrawable(resources, bitmap); + assertEquals(bitmap, drawable.getBitmap()); + } + + @Test public void getBitmap_shouldReturnBitmapUsedToDraw() throws Exception { BitmapDrawable drawable = (BitmapDrawable) resources.getDrawable(R.drawable.an_image); assertEquals("Bitmap for resource:drawable/an_image", shadowOf(drawable.getBitmap()).getDescription()); } @Test + public void mutate_createsDeepCopy() throws Exception { + BitmapDrawable original = (BitmapDrawable) resources.getDrawable(R.drawable.an_image); + Drawable mutated = original.mutate(); + assertNotSame(original, mutated); + assertTrue(mutated instanceof BitmapDrawable); + assertEquals(original, mutated); + } + + @Test public void draw_shouldCopyDescriptionToCanvas() throws Exception { BitmapDrawable drawable = (BitmapDrawable) resources.getDrawable(R.drawable.an_image); Canvas canvas = new Canvas(); diff --git a/src/test/java/com/xtremelabs/robolectric/shadows/BitmapFactoryTest.java b/src/test/java/com/xtremelabs/robolectric/shadows/BitmapFactoryTest.java index 354aa4065..2bb90661e 100644 --- a/src/test/java/com/xtremelabs/robolectric/shadows/BitmapFactoryTest.java +++ b/src/test/java/com/xtremelabs/robolectric/shadows/BitmapFactoryTest.java @@ -10,6 +10,9 @@ import com.xtremelabs.robolectric.TestRunners; import org.junit.Test; import org.junit.runner.RunWith; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; import java.io.InputStream; import static com.xtremelabs.robolectric.Robolectric.shadowOf; @@ -39,6 +42,28 @@ public class BitmapFactoryTest { } @Test + public void decodeFile_ifFileExists_shouldSetDescriptionToContentsOfFile() throws Exception { + File tempFile = File.createTempFile("temp-image", ".jpg"); + writeTo(tempFile, "image bytes", " more image bytes"); + + Bitmap bitmap = BitmapFactory.decodeFile(tempFile.getPath()); + assertEquals("Bitmap for image bytes more image bytes", shadowOf(bitmap).getDescription()); + assertEquals(100, bitmap.getWidth()); + assertEquals(100, bitmap.getHeight()); + } + + @Test + public void decodeFile_ifFileExists_shouldSetDescriptionToContentsOfFile_UsingOptions() throws Exception { + File tempFile = File.createTempFile("temp-image", ".jpg"); + writeTo(tempFile, "image bytes", " more image bytes"); + + Bitmap bitmap = BitmapFactory.decodeFile(tempFile.getPath(), new BitmapFactory.Options()); + assertEquals("Bitmap for image bytes more image bytes", shadowOf(bitmap).getDescription()); + assertEquals(100, bitmap.getWidth()); + assertEquals(100, bitmap.getHeight()); + } + + @Test public void decodeStream_shouldSetDescription() throws Exception { InputStream inputStream = Robolectric.application.getContentResolver().openInputStream(Uri.parse("content:/path")); Bitmap bitmap = BitmapFactory.decodeStream(inputStream); @@ -105,6 +130,28 @@ public class BitmapFactoryTest { } @Test + public void decodeByteArray_shouldGetWidthAndHeightFromHints() throws Exception { + String data = "arbitrary bytes"; + ShadowBitmapFactory.provideWidthAndHeightHints(Uri.parse(data), 123, 456); + + byte[] bytes = data.getBytes(); + Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length); + assertEquals("Bitmap for " + data, shadowOf(bitmap).getDescription()); + assertEquals(123, bitmap.getWidth()); + assertEquals(456, bitmap.getHeight()); + } + + @Test + public void decodeByteArray_shouldIncludeOffsets() throws Exception { + String data = "arbitrary bytes"; + ShadowBitmapFactory.provideWidthAndHeightHints(Uri.parse(data), 123, 456); + + byte[] bytes = data.getBytes(); + Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 1, bytes.length - 2); + assertEquals("Bitmap for " + data + " bytes 1..13", shadowOf(bitmap).getDescription()); + } + + @Test public void decodeStream_shouldGetWidthAndHeightFromHints() throws Exception { ShadowBitmapFactory.provideWidthAndHeightHints(Uri.parse("content:/path"), 123, 456); @@ -117,46 +164,56 @@ public class BitmapFactoryTest { @Test public void decodeByteArray_shouldSetDataChecksum() throws Exception { - byte[] data = { 23, 100, 23, 52, 23, 18, 76, 43 }; - - Bitmap bitmap = ShadowBitmapFactory.decodeByteArray(data, 0, data.length); - assertThat( bitmap, notNullValue() ); - assertThat( shadowOf(bitmap).getDescription(), equalTo( "Bitmap for byte array, checksum:80429753 offset: 0 length: 8" ) ); - assertThat( bitmap.getWidth(), equalTo(100) ); - assertThat( bitmap.getHeight(), equalTo(100) ); - + byte[] data = { 23, 100, 23, 52, 23, 18, 76, 43 }; + + Bitmap bitmap = ShadowBitmapFactory.decodeByteArray(data, 0, data.length); + assertThat( bitmap, notNullValue() ); + assertThat( shadowOf(bitmap).getDescription(), equalTo( "Bitmap for byte array, checksum:80429753 offset: 0 length: 8" ) ); + assertThat( bitmap.getWidth(), equalTo(100) ); + assertThat( bitmap.getHeight(), equalTo(100) ); + } @Test public void decodeByteArray_withOptionsShouldSetDataChecksum() throws Exception { - byte[] data = { 23, 100, 23, 52, 23, 18, 76, 43 }; - - BitmapFactory.Options options = new BitmapFactory.Options(); - options.inSampleSize = 4; - Bitmap bitmap = ShadowBitmapFactory.decodeByteArray(data, 0, data.length, options); - assertThat( shadowOf(bitmap).getDescription(), equalTo( "Bitmap for byte array, checksum:80429753 offset: 0 length: 8 with options inSampleSize=4" ) ); - assertThat( bitmap.getWidth(), equalTo(25) ); - assertThat( bitmap.getHeight(), equalTo(25) ); + byte[] data = { 23, 100, 23, 52, 23, 18, 76, 43 }; + + BitmapFactory.Options options = new BitmapFactory.Options(); + options.inSampleSize = 4; + Bitmap bitmap = ShadowBitmapFactory.decodeByteArray(data, 0, data.length, options); + assertThat( shadowOf(bitmap).getDescription(), equalTo( "Bitmap for byte array, checksum:80429753 offset: 0 length: 8 with options inSampleSize=4" ) ); + assertThat( bitmap.getWidth(), equalTo(25) ); + assertThat( bitmap.getHeight(), equalTo(25) ); } @Test public void decodeWithDifferentSampleSize() { - String name = "test"; - BitmapFactory.Options options = new BitmapFactory.Options(); - - options.inSampleSize = 0; - Bitmap bm = ShadowBitmapFactory.create(name, options); - assertThat( bm.getWidth(), equalTo(100) ); - assertThat( bm.getHeight(), equalTo(100) ); - - options.inSampleSize = 2; - bm = ShadowBitmapFactory.create(name, options); - assertThat( bm.getWidth(), equalTo(50) ); - assertThat( bm.getHeight(), equalTo(50) ); - - options.inSampleSize = 101; - bm = ShadowBitmapFactory.create(name, options); - assertThat( bm.getWidth(), equalTo(1) ); - assertThat( bm.getHeight(), equalTo(1) ); + String name = "test"; + BitmapFactory.Options options = new BitmapFactory.Options(); + + options.inSampleSize = 0; + Bitmap bm = ShadowBitmapFactory.create(name, options); + assertThat( bm.getWidth(), equalTo(100) ); + assertThat( bm.getHeight(), equalTo(100) ); + + options.inSampleSize = 2; + bm = ShadowBitmapFactory.create(name, options); + assertThat( bm.getWidth(), equalTo(50) ); + assertThat( bm.getHeight(), equalTo(50) ); + + options.inSampleSize = 101; + bm = ShadowBitmapFactory.create(name, options); + assertThat( bm.getWidth(), equalTo(1) ); + assertThat( bm.getHeight(), equalTo(1) ); + } + + ////////////////// + + private void writeTo(File tempFile, String... strings) throws IOException { + FileWriter fileWriter = new FileWriter(tempFile); + for (String s : strings) { + fileWriter.write(s); + } + fileWriter.close(); } } diff --git a/src/test/java/com/xtremelabs/robolectric/shadows/BluetoothAdapterTest.java b/src/test/java/com/xtremelabs/robolectric/shadows/BluetoothAdapterTest.java index da87ac3b2..0c2a223a4 100644 --- a/src/test/java/com/xtremelabs/robolectric/shadows/BluetoothAdapterTest.java +++ b/src/test/java/com/xtremelabs/robolectric/shadows/BluetoothAdapterTest.java @@ -1,15 +1,40 @@ package com.xtremelabs.robolectric.shadows; + import android.bluetooth.BluetoothAdapter; +import com.xtremelabs.robolectric.Robolectric; import com.xtremelabs.robolectric.TestRunners; +import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import static com.xtremelabs.robolectric.Robolectric.shadowOf; -import static junit.framework.Assert.assertEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; @RunWith(TestRunners.WithDefaults.class) public class BluetoothAdapterTest { + private BluetoothAdapter bluetoothAdapter; + private ShadowBluetoothAdapter shadowBluetoothAdapter; + + @Before + public void setUp() throws Exception { + bluetoothAdapter = Robolectric.newInstanceOf(BluetoothAdapter.class); + shadowBluetoothAdapter = shadowOf(bluetoothAdapter); + } + + @Test + public void testAdapterDefaultsDisabled() { + assertFalse(bluetoothAdapter.isEnabled()); + } + + @Test + public void testAdapterCanBeEnabled() { + shadowBluetoothAdapter.setEnabled(true); + assertTrue(bluetoothAdapter.isEnabled()); + } + @Test public void canGetAndSetAddress() throws Exception { BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); diff --git a/src/test/java/com/xtremelabs/robolectric/shadows/CheckedTextViewTest.java b/src/test/java/com/xtremelabs/robolectric/shadows/CheckedTextViewTest.java index f8b5fc6d9..af7640adf 100644 --- a/src/test/java/com/xtremelabs/robolectric/shadows/CheckedTextViewTest.java +++ b/src/test/java/com/xtremelabs/robolectric/shadows/CheckedTextViewTest.java @@ -38,4 +38,12 @@ public class CheckedTextViewTest { assertTrue(checkedTextView.isChecked()); } + @Test public void toggle_shouldChangeCheckedness() throws Exception { + CheckedTextView view = new CheckedTextView(null); + assertFalse(view.isChecked()); + view.toggle(); + assertTrue(view.isChecked()); + view.performClick(); + assertFalse(view.isChecked()); + } } diff --git a/src/test/java/com/xtremelabs/robolectric/shadows/ClipboardManagerTest.java b/src/test/java/com/xtremelabs/robolectric/shadows/ClipboardManagerTest.java index ec5bfed89..bdeeefdac 100644 --- a/src/test/java/com/xtremelabs/robolectric/shadows/ClipboardManagerTest.java +++ b/src/test/java/com/xtremelabs/robolectric/shadows/ClipboardManagerTest.java @@ -1,7 +1,7 @@ package com.xtremelabs.robolectric.shadows; +import android.content.ClipboardManager; import android.content.Context; -import android.text.ClipboardManager; import com.xtremelabs.robolectric.Robolectric; import com.xtremelabs.robolectric.TestRunners; import org.junit.Before; @@ -9,9 +9,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import static org.hamcrest.core.IsEqual.equalTo; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.*; @RunWith(TestRunners.WithDefaults.class) public class ClipboardManagerTest { diff --git a/src/test/java/com/xtremelabs/robolectric/shadows/ConfigurationTest.java b/src/test/java/com/xtremelabs/robolectric/shadows/ConfigurationTest.java index 41ffe4947..d89e5d7ce 100644 --- a/src/test/java/com/xtremelabs/robolectric/shadows/ConfigurationTest.java +++ b/src/test/java/com/xtremelabs/robolectric/shadows/ConfigurationTest.java @@ -39,6 +39,13 @@ public class ConfigurationTest { shConfiguration.setLocale( Locale.FRANCE); assertThat( configuration.locale, equalTo( Locale.FRANCE ) ); -} + } + + @Test + public void testConstructCopy() { + configuration.setToDefaults(); + Configuration clone = new Configuration(configuration); + assertThat( configuration, equalTo( clone ) ); + } } diff --git a/src/test/java/com/xtremelabs/robolectric/shadows/ContextTest.java b/src/test/java/com/xtremelabs/robolectric/shadows/ContextTest.java index 6ecb79e71..a9c821336 100644 --- a/src/test/java/com/xtremelabs/robolectric/shadows/ContextTest.java +++ b/src/test/java/com/xtremelabs/robolectric/shadows/ContextTest.java @@ -6,26 +6,17 @@ import android.content.res.TypedArray; import com.xtremelabs.robolectric.R; import com.xtremelabs.robolectric.TestRunners; import com.xtremelabs.robolectric.tester.android.util.TestAttributeSet; -import org.hamcrest.CoreMatchers; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.FileWriter; -import java.io.IOException; +import java.io.*; import java.util.HashMap; import java.util.Map; import static org.hamcrest.CoreMatchers.*; import static org.junit.Assert.*; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; @RunWith(TestRunners.WithDefaults.class) public class ContextTest { @@ -111,7 +102,8 @@ public class ContextTest { assertNotNull(context.getCacheDir()); File cacheTest = new File(context.getCacheDir(), "__test__"); - assertThat(cacheTest.getPath(), CoreMatchers.containsString("android-cache")); + assertThat(cacheTest.getAbsolutePath(), startsWith(System.getProperty("java.io.tmpdir"))); + assertThat(cacheTest.getAbsolutePath(), endsWith("android-cache" + File.separator + "__test__")); FileOutputStream fos = null; try { @@ -129,7 +121,8 @@ public class ContextTest { assertNotNull(context.getExternalCacheDir()); File cacheTest = new File(context.getExternalCacheDir(), "__test__"); - assertThat(cacheTest.getPath(), containsString("android-external-cache")); + assertThat(cacheTest.getAbsolutePath(), startsWith(System.getProperty("java.io.tmpdir"))); + assertThat(cacheTest.getAbsolutePath(), endsWith("android-external-cache" + File.separator + "__test__")); FileOutputStream fos = null; try { diff --git a/src/test/java/com/xtremelabs/robolectric/shadows/ContextWrapperTest.java b/src/test/java/com/xtremelabs/robolectric/shadows/ContextWrapperTest.java index 8c2663629..e90241846 100644 --- a/src/test/java/com/xtremelabs/robolectric/shadows/ContextWrapperTest.java +++ b/src/test/java/com/xtremelabs/robolectric/shadows/ContextWrapperTest.java @@ -1,6 +1,7 @@ package com.xtremelabs.robolectric.shadows; import android.app.Activity; +import android.app.Application; import android.appwidget.AppWidgetProvider; import android.content.BroadcastReceiver; import android.content.Context; @@ -196,4 +197,9 @@ public class ContextWrapperTest { } return larryIntentFilter; } + + @Test + public void packageManagerShouldNotBeNullWhenWrappingAnApplication() { + assertThat(new Application().getPackageManager(), notNullValue()); + } } diff --git a/src/test/java/com/xtremelabs/robolectric/shadows/DateFormatTest.java b/src/test/java/com/xtremelabs/robolectric/shadows/DateFormatTest.java new file mode 100644 index 000000000..cf3790a37 --- /dev/null +++ b/src/test/java/com/xtremelabs/robolectric/shadows/DateFormatTest.java @@ -0,0 +1,38 @@ +package com.xtremelabs.robolectric.shadows; + +import android.text.format.DateFormat; +import com.xtremelabs.robolectric.TestRunners; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.Calendar; +import java.util.Date; + +import static org.junit.Assert.assertEquals; + +@RunWith(TestRunners.WithDefaults.class) +public class DateFormatTest { + + @Test + public void getTimeFormat_returnsATimeFormat() { + Calendar cal = Calendar.getInstance(); + cal.clear(); + cal.set(Calendar.HOUR, 7); + cal.set(Calendar.MINUTE, 48); + cal.set(Calendar.SECOND, 3); + Date date = cal.getTime(); + assertEquals("07:48:03", DateFormat.getTimeFormat(null).format(date)); + } + + @Test + public void getDateFormat_returnsADateFormat() { + Calendar cal = Calendar.getInstance(); + cal.clear(); + cal.set(Calendar.DATE, 12); + cal.set(Calendar.MONTH, Calendar.JANUARY); + cal.set(Calendar.YEAR, 1970); + Date date = cal.getTime(); + assertEquals("Jan-12-1970", DateFormat.getDateFormat(null).format(date)); + } + +} diff --git a/src/test/java/com/xtremelabs/robolectric/shadows/DefaultRequestDirectorTest.java b/src/test/java/com/xtremelabs/robolectric/shadows/DefaultRequestDirectorTest.java index f7a0daf38..0c99f547c 100644 --- a/src/test/java/com/xtremelabs/robolectric/shadows/DefaultRequestDirectorTest.java +++ b/src/test/java/com/xtremelabs/robolectric/shadows/DefaultRequestDirectorTest.java @@ -268,6 +268,26 @@ public class DefaultRequestDirectorTest { } @Test + public void getNextSentHttpRequestInfo_shouldRemoveHttpRequestInfos() throws Exception { + Robolectric.addPendingHttpResponse(200, "a happy response body"); + HttpGet httpGet = new HttpGet("http://example.com"); + requestDirector.execute(null, httpGet, null); + + assertSame(Robolectric.getNextSentHttpRequestInfo().getHttpRequest(), httpGet); + assertNull(Robolectric.getNextSentHttpRequestInfo()); + } + + @Test + public void getNextSentHttpRequest_shouldRemoveHttpRequests() throws Exception { + Robolectric.addPendingHttpResponse(200, "a happy response body"); + HttpGet httpGet = new HttpGet("http://example.com"); + requestDirector.execute(null, httpGet, null); + + assertSame(Robolectric.getNextSentHttpRequest(), httpGet); + assertNull(Robolectric.getNextSentHttpRequest()); + } + + @Test public void shouldSupportBasicResponseHandlerHandleResponse() throws Exception { Robolectric.addPendingHttpResponse(200, "OK", new BasicHeader("Content-Type", "text/plain")); diff --git a/src/test/java/com/xtremelabs/robolectric/shadows/DialogTest.java b/src/test/java/com/xtremelabs/robolectric/shadows/DialogTest.java index 31a17e36d..7934981ef 100644 --- a/src/test/java/com/xtremelabs/robolectric/shadows/DialogTest.java +++ b/src/test/java/com/xtremelabs/robolectric/shadows/DialogTest.java @@ -1,8 +1,10 @@ package com.xtremelabs.robolectric.shadows; +import android.app.Activity; import android.app.Dialog; import android.content.DialogInterface; import android.os.Bundle; +import android.view.View; import android.widget.TextView; import com.xtremelabs.robolectric.R; import com.xtremelabs.robolectric.Robolectric; @@ -17,6 +19,7 @@ import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.CoreMatchers.sameInstance; import static org.hamcrest.core.IsEqual.equalTo; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertSame; import static org.junit.Assert.assertThat; @RunWith(TestRunners.WithDefaults.class) @@ -40,6 +43,18 @@ public class DialogTest { } @Test + public void setContentViewWithViewAllowsFindById() throws Exception { + final int viewId = 1234; + Activity context = new Activity(); + final Dialog dialog = new Dialog(context); + final View view = new View(context); + view.setId(viewId); + dialog.setContentView(view); + + assertSame(view, dialog.findViewById(viewId)); + } + + @Test public void shouldGetLayoutInflater() { Dialog dialog = new Dialog(Robolectric.application); assertNotNull(dialog.getLayoutInflater()); diff --git a/src/test/java/com/xtremelabs/robolectric/shadows/DisplayTest.java b/src/test/java/com/xtremelabs/robolectric/shadows/DisplayTest.java index 32fb52a9b..e89e5c35a 100644 --- a/src/test/java/com/xtremelabs/robolectric/shadows/DisplayTest.java +++ b/src/test/java/com/xtremelabs/robolectric/shadows/DisplayTest.java @@ -7,6 +7,8 @@ import com.xtremelabs.robolectric.TestRunners; import org.junit.Test; import org.junit.runner.RunWith; +import static com.xtremelabs.robolectric.Robolectric.newInstanceOf; +import static com.xtremelabs.robolectric.Robolectric.shadowOf; import static org.junit.Assert.assertEquals; @RunWith(TestRunners.WithDefaults.class) @@ -14,8 +16,8 @@ public class DisplayTest { @Test public void shouldProvideDisplayMetrics() throws Exception { - Display display = Robolectric.newInstanceOf(Display.class); - ShadowDisplay shadow = Robolectric.shadowOf(display); + Display display = newInstanceOf(Display.class); + ShadowDisplay shadow = shadowOf(display); shadow.setDensity(1.5f); shadow.setDensityDpi(DisplayMetrics.DENSITY_MEDIUM); @@ -38,4 +40,16 @@ public class DisplayTest { assertEquals(184.0f, metrics.ydpi, 0.05); } + /** + * The {@link android.view.Display#getOrientation()} method is deprecated, but for + * testing purposes, return the value gotten from {@link android.view.Display#getRotation()} + */ + @Test + public void deprecatedGetOrientation_returnsGetRotation() { + Display display = newInstanceOf(Display.class); + int testValue = 33; + shadowOf(display).setRotation(testValue); + assertEquals(testValue, display.getOrientation()); + } + } diff --git a/src/test/java/com/xtremelabs/robolectric/shadows/EditTextTest.java b/src/test/java/com/xtremelabs/robolectric/shadows/EditTextTest.java index 16ef9108e..e63567258 100644 --- a/src/test/java/com/xtremelabs/robolectric/shadows/EditTextTest.java +++ b/src/test/java/com/xtremelabs/robolectric/shadows/EditTextTest.java @@ -1,7 +1,11 @@ package com.xtremelabs.robolectric.shadows; +import android.app.Activity; +import android.content.Context; import android.util.AttributeSet; +import android.view.LayoutInflater; import android.widget.EditText; +import com.xtremelabs.robolectric.R; import com.xtremelabs.robolectric.Robolectric; import com.xtremelabs.robolectric.TestRunners; import org.junit.Test; @@ -100,4 +104,12 @@ public class EditTextTest { when(attrs.getAttributeIntValue("android", "maxLength", Integer.MAX_VALUE)).thenReturn(Integer.MAX_VALUE); return attrs; } + + @Test + public void shouldGetHintFromXml() { + Context context = new Activity(); + LayoutInflater inflater = LayoutInflater.from(context); + EditText editText = (EditText) inflater.inflate(R.layout.edit_text, null); + assertThat(editText.getHint().toString(), equalTo("Hello, Hint")); + } } diff --git a/src/test/java/com/xtremelabs/robolectric/shadows/FrameLayoutTest.java b/src/test/java/com/xtremelabs/robolectric/shadows/FrameLayoutTest.java index 8fe210458..00bf216a6 100644 --- a/src/test/java/com/xtremelabs/robolectric/shadows/FrameLayoutTest.java +++ b/src/test/java/com/xtremelabs/robolectric/shadows/FrameLayoutTest.java @@ -30,4 +30,11 @@ public class FrameLayoutTest { ViewGroup.LayoutParams layoutParams = frameLayout.getLayoutParams(); assertThat(layoutParams, instanceOf(ViewGroup.MarginLayoutParams.class)); } + + @Test + public void getLayoutParams_shouldReturnFrameLayoutParams() throws Exception { + ViewGroup.LayoutParams layoutParams = new FrameLayout(null).getLayoutParams(); + + assertThat(layoutParams, instanceOf(FrameLayout.LayoutParams.class)); + } } diff --git a/src/test/java/com/xtremelabs/robolectric/shadows/HtmlTest.java b/src/test/java/com/xtremelabs/robolectric/shadows/HtmlTest.java index 4bf3f08a0..d4853c252 100644 --- a/src/test/java/com/xtremelabs/robolectric/shadows/HtmlTest.java +++ b/src/test/java/com/xtremelabs/robolectric/shadows/HtmlTest.java @@ -4,6 +4,7 @@ package com.xtremelabs.robolectric.shadows; import android.app.Activity; import android.content.Context; import android.text.Html; +import android.text.Spanned; import android.widget.EditText; import android.widget.TextView; import com.xtremelabs.robolectric.TestRunners; @@ -12,6 +13,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import static org.hamcrest.CoreMatchers.equalTo; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThat; @RunWith(TestRunners.WithDefaults.class) @@ -41,4 +43,10 @@ public class HtmlTest { public void shouldThrowNullPointerExceptionWhenNullStringEncountered() throws Exception { Html.fromHtml(null); } + + public void fromHtml_shouldJustReturnArgByDefault() { + String text = "<b>foo</b>"; + Spanned spanned = Html.fromHtml(text); + assertEquals(text, spanned.toString()); + } } diff --git a/src/test/java/com/xtremelabs/robolectric/shadows/ImageButtonTest.java b/src/test/java/com/xtremelabs/robolectric/shadows/ImageButtonTest.java new file mode 100644 index 000000000..81e9ada7e --- /dev/null +++ b/src/test/java/com/xtremelabs/robolectric/shadows/ImageButtonTest.java @@ -0,0 +1,20 @@ +package com.xtremelabs.robolectric.shadows; + +import android.widget.ImageButton; +import com.xtremelabs.robolectric.Robolectric; +import com.xtremelabs.robolectric.TestRunners; +import com.xtremelabs.robolectric.tester.android.util.TestAttributeSet; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.hamcrest.MatcherAssert.assertThat; + +@RunWith(TestRunners.WithDefaults.class) +public class ImageButtonTest { + @Test + public void testBackground() throws Exception { + ImageButton button = new ImageButton(Robolectric.application, new TestAttributeSet()); + assertThat(button.getBackground(), notNullValue()); + } +} diff --git a/src/test/java/com/xtremelabs/robolectric/shadows/ImageViewTest.java b/src/test/java/com/xtremelabs/robolectric/shadows/ImageViewTest.java index 9b4e28241..eb60d8ed8 100644 --- a/src/test/java/com/xtremelabs/robolectric/shadows/ImageViewTest.java +++ b/src/test/java/com/xtremelabs/robolectric/shadows/ImageViewTest.java @@ -15,6 +15,7 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import static com.xtremelabs.robolectric.Robolectric.application; import static com.xtremelabs.robolectric.Robolectric.shadowOf; import static com.xtremelabs.robolectric.Robolectric.visualize; import static org.hamcrest.CoreMatchers.equalTo; @@ -68,6 +69,11 @@ public class ImageViewTest { } @Test + public void visualizeWithEmpty() throws Exception { + assertEquals("", Robolectric.visualize(new ImageView(application))); + } + + @Test public void testSetImageResource_drawable() { imageView.setImageResource(R.drawable.l0_red); assertTrue("Drawable", imageView.getDrawable() instanceof Drawable); diff --git a/src/test/java/com/xtremelabs/robolectric/shadows/IntentTest.java b/src/test/java/com/xtremelabs/robolectric/shadows/IntentTest.java index 3436d0336..9657666e8 100644 --- a/src/test/java/com/xtremelabs/robolectric/shadows/IntentTest.java +++ b/src/test/java/com/xtremelabs/robolectric/shadows/IntentTest.java @@ -295,7 +295,7 @@ public class IntentTest { String category = "category"; intentB.addCategory(category); intentB.setPackage("com.foobar.app"); - ComponentName cn = new ComponentName("com.foobar.app", "activity"); + ComponentName cn = new ComponentName("com.foobar.app", "fragmentActivity"); intentB.setComponent(cn); intentB.putExtra("FOO", 23); diff --git a/src/test/java/com/xtremelabs/robolectric/shadows/ListActivityTest.java b/src/test/java/com/xtremelabs/robolectric/shadows/ListActivityTest.java index c31ddabc4..46c6f641e 100644 --- a/src/test/java/com/xtremelabs/robolectric/shadows/ListActivityTest.java +++ b/src/test/java/com/xtremelabs/robolectric/shadows/ListActivityTest.java @@ -2,10 +2,12 @@ package com.xtremelabs.robolectric.shadows; import android.app.ListActivity; import android.view.View; +import android.widget.FrameLayout; import android.widget.ListAdapter; import android.widget.ListView; import com.xtremelabs.robolectric.Robolectric; import com.xtremelabs.robolectric.TestRunners; +import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -18,9 +20,24 @@ import static org.hamcrest.MatcherAssert.assertThat; @RunWith(TestRunners.WithDefaults.class) public class ListActivityTest { + private ListActivity listActivity; + private FrameLayout content; + private ListView listView; + + @Before + public void setUp() throws Exception { + listView = new ListView(listActivity); + listView.setId(android.R.id.list); + + content = new FrameLayout(listActivity); + content.addView(listView); + + listActivity = new ListActivity(); + listActivity.setContentView(content); + } + @Test public void shouldSupportSettingAndGettingListAdapter(){ - ListActivity listActivity = new ListActivity(); ListAdapter adapter = new CountingAdapter(5); listActivity.setListAdapter(adapter); @@ -29,6 +46,8 @@ public class ListActivityTest { @Test public void shouldSupportOnItemClick() throws Exception { + listActivity.setContentView(null); + final boolean[] clicked = new boolean[1]; ListActivity listActivity = new ListActivity() { @Override @@ -36,7 +55,7 @@ public class ListActivityTest { clicked[0] = true; } }; - listActivity.setContentView(new ListView(null)); + listActivity.setContentView(content); listActivity.setListAdapter(new CountingAdapter(5)); Robolectric.shadowOf(listActivity.getListView()).performItemClick(0); assertTrue(clicked[0]); @@ -44,11 +63,14 @@ public class ListActivityTest { @Test public void shouldSetAdapterOnListView() throws Exception { - ListActivity listActivity = new ListActivity(); ListAdapter adapter = new CountingAdapter(5); - final ListView listView = new ListView(null); - listActivity.setContentView(listView); listActivity.setListAdapter(adapter); assertThat(listView.getAdapter(), sameInstance(adapter)); } + + @Test(expected = RuntimeException.class) + public void whenNoViewWithListIdExists_shouldRaiseException(){ + ListActivity listActivity = new ListActivity(); + listActivity.setListAdapter(new CountingAdapter(5)); + } } diff --git a/src/test/java/com/xtremelabs/robolectric/shadows/LocalActivityManagerTest.java b/src/test/java/com/xtremelabs/robolectric/shadows/LocalActivityManagerTest.java new file mode 100644 index 000000000..716392d90 --- /dev/null +++ b/src/test/java/com/xtremelabs/robolectric/shadows/LocalActivityManagerTest.java @@ -0,0 +1,31 @@ +package com.xtremelabs.robolectric.shadows; + +import android.app.Activity; +import android.os.Bundle; +import com.xtremelabs.robolectric.R; +import com.xtremelabs.robolectric.TestRunners; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.junit.Assert.assertNotNull; + +@RunWith(TestRunners.WithDefaults.class) +public class LocalActivityManagerTest { + @Test + public void testInflation() throws Exception { + OuterActivity activity = new OuterActivity(); + activity.onCreate(null); + // Make sure the container lookup works. + assertNotNull(activity.findViewById(R.id.lam_container)); + // And make sure the view inside the LocalActivityManager-created view works too. + assertNotNull(activity.findViewById(R.id.lam_inner_contents)); + } + + private class OuterActivity extends Activity { + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.lam_outer); + } + } +} diff --git a/src/test/java/com/xtremelabs/robolectric/shadows/LocationTest.java b/src/test/java/com/xtremelabs/robolectric/shadows/LocationTest.java index 4a72dcd56..3a61c37aa 100644 --- a/src/test/java/com/xtremelabs/robolectric/shadows/LocationTest.java +++ b/src/test/java/com/xtremelabs/robolectric/shadows/LocationTest.java @@ -3,13 +3,13 @@ package com.xtremelabs.robolectric.shadows; import android.location.Location; import android.location.LocationManager; import com.xtremelabs.robolectric.TestRunners; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import static junit.framework.Assert.assertEquals; -import static junit.framework.Assert.assertTrue; -import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.*; +import static org.junit.Assert.assertArrayEquals; @RunWith(TestRunners.WithDefaults.class) public class LocationTest { @@ -18,6 +18,8 @@ public class LocationTest { @Before public void setUp() throws Exception { + ShadowLocation.setDistanceBetween(null); + location = new Location(LocationManager.GPS_PROVIDER); location.setTime(1); location.setLatitude(2); @@ -28,6 +30,11 @@ public class LocationTest { location.setAltitude(3.0d); } + @After + public void tearDown() { + ShadowLocation.setDistanceBetween(null); + } + @Test public void test_copyConstructor() throws Exception { Location copiedLocation = new Location(location); @@ -118,4 +125,44 @@ public class LocationTest { assertEquals(location, l); } + + @Test + public void testDistanceBetween_preventNPE() { + Location.distanceBetween(1.0, 1.0, 1.0, 1.0, new float[2]); + } + + @Test + public void testDistanceBetween_shouldBeMockable() { + float[] expectedDistance = {2.2f, 5.2f}; + ShadowLocation.setDistanceBetween(expectedDistance); + float[] actualDistance = new float[2]; + Location.distanceBetween(1.0, 1.0, 1.0, 1.0, actualDistance); + assertArrayEquals(expectedDistance, actualDistance, 0f); + } + + @Test + public void testDistanceBetweenMocking_requiresArraysOfEqualLength() { + float[] expectedDistance = {2.2f, 5.2f}; + ShadowLocation.setDistanceBetween(expectedDistance); + float[] actualDistance = new float[1]; + Location.distanceBetween(1.0, 1.0, 1.0, 1.0, actualDistance); + assertArrayEquals(new float[]{0.0f}, actualDistance, 0f); + } + + @Test + public void gettersAndSetters_shouldWork() { + Location l = new Location("gps"); + l.setLatitude(1.0); + l.setLongitude(2.0); + l.setAltitude(3.0); + l.setAccuracy(4.0f); + l.setBearing(5.0f); + l.setSpeed(6.0f); + assertEquals(1.0, l.getLatitude(), 0.0); + assertEquals(2.0, l.getLongitude(), 0.0); + assertEquals(3.0, l.getAltitude(), 0.0); + assertEquals(4.0f, l.getAccuracy(), 0.0); + assertEquals(5.0f, l.getBearing(), 0.0); + assertEquals(6.0f, l.getSpeed(), 0.0); + } } diff --git a/src/test/java/com/xtremelabs/robolectric/shadows/NotificationManagerTest.java b/src/test/java/com/xtremelabs/robolectric/shadows/NotificationManagerTest.java index fe14f3666..163a13012 100644 --- a/src/test/java/com/xtremelabs/robolectric/shadows/NotificationManagerTest.java +++ b/src/test/java/com/xtremelabs/robolectric/shadows/NotificationManagerTest.java @@ -27,11 +27,11 @@ public class NotificationManagerTest { public void testNotify() throws Exception { notificationManager.notify(1, notification1); assertEquals(1, shadowOf(notificationManager).size()); - assertEquals(notification1, shadowOf(notificationManager).getNotification(1)); + assertEquals(notification1, shadowOf(notificationManager).getNotification(null, 1)); notificationManager.notify(31, notification2); assertEquals(2, shadowOf(notificationManager).size()); - assertEquals(notification2, shadowOf(notificationManager).getNotification(31)); + assertEquals(notification2, shadowOf(notificationManager).getNotification(null, 31)); } @Test @@ -40,28 +40,28 @@ public class NotificationManagerTest { notificationManager.notify(1, notification2); assertEquals(1, shadowOf(notificationManager).size()); - assertEquals(notification2, shadowOf(notificationManager).getNotification(1)); + assertEquals(notification2, shadowOf(notificationManager).getNotification(null, 1)); } @Test public void testNotifyWithTag() throws Exception { notificationManager.notify("a tag", 1, notification1); assertEquals(1, shadowOf(notificationManager).size()); - assertEquals(notification1, shadowOf(notificationManager).getNotification("a tag")); + assertEquals(notification1, shadowOf(notificationManager).getNotification("a tag", 1)); } @Test public void notifyWithTag_shouldReturnNullForNullTag() throws Exception { notificationManager.notify("a tag", 1, notification1); assertEquals(1, shadowOf(notificationManager).size()); - assertNull(shadowOf(notificationManager).getNotification(null)); + assertNull(shadowOf(notificationManager).getNotification(null, 1)); } @Test public void notifyWithTag_shouldReturnNullForUnknownTag() throws Exception { notificationManager.notify("a tag", 1, notification1); assertEquals(1, shadowOf(notificationManager).size()); - assertNull(shadowOf(notificationManager).getNotification("unknown tag")); + assertNull(shadowOf(notificationManager).getNotification("unknown tag", 1)); } @Test @@ -70,7 +70,7 @@ public class NotificationManagerTest { notificationManager.cancel(1); assertEquals(0, shadowOf(notificationManager).size()); - assertNull(shadowOf(notificationManager).getNotification(1)); + assertNull(shadowOf(notificationManager).getNotification(null, 1)); } @Test @@ -79,8 +79,8 @@ public class NotificationManagerTest { notificationManager.cancel("a tag", 1); assertEquals(0, shadowOf(notificationManager).size()); - assertNull(shadowOf(notificationManager).getNotification(1)); - assertNull(shadowOf(notificationManager).getNotification("a tag")); + assertNull(shadowOf(notificationManager).getNotification(null, 1)); + assertNull(shadowOf(notificationManager).getNotification("a tag", 1)); } @Test @@ -90,7 +90,7 @@ public class NotificationManagerTest { notificationManager.cancelAll(); assertEquals(0, shadowOf(notificationManager).size()); - assertNull(shadowOf(notificationManager).getNotification(1)); - assertNull(shadowOf(notificationManager).getNotification(31)); + assertNull(shadowOf(notificationManager).getNotification(null, 1)); + assertNull(shadowOf(notificationManager).getNotification(null, 31)); } } diff --git a/src/test/java/com/xtremelabs/robolectric/shadows/PreferenceActivityTest.java b/src/test/java/com/xtremelabs/robolectric/shadows/PreferenceActivityTest.java index 0ee0f37d5..274fd3f6b 100644 --- a/src/test/java/com/xtremelabs/robolectric/shadows/PreferenceActivityTest.java +++ b/src/test/java/com/xtremelabs/robolectric/shadows/PreferenceActivityTest.java @@ -4,6 +4,7 @@ import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.instanceOf; import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.CoreMatchers.notNullValue; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertThat; import com.xtremelabs.robolectric.TestRunners; @@ -13,52 +14,64 @@ import org.junit.runner.RunWith; import android.app.Activity; import android.preference.PreferenceActivity; -import android.widget.ListView; - import com.xtremelabs.robolectric.R; import com.xtremelabs.robolectric.Robolectric; @RunWith(TestRunners.WithDefaults.class) public class PreferenceActivityTest { - private TestPreferenceActivity activity; - private ShadowPreferenceActivity shadow; - + private TestPreferenceActivity activity; + private ShadowPreferenceActivity shadow; + @Before public void setUp() throws Exception { - activity = new TestPreferenceActivity(); - shadow = Robolectric.shadowOf(activity); + activity = new TestPreferenceActivity(); + shadow = Robolectric.shadowOf(activity); + } + + @Test + public void shouldInitializeListViewInOnCreate() { + shadow.callOnCreate(null); + assertThat(activity.getListView(), notNullValue()); + } + + @Test + public void shouldInheritFromListActivity() { + assertThat(shadow, instanceOf(ShadowListActivity.class)); + } + + @Test + public void shouldNotInitializePreferenceScreen() { + assertThat(activity.getPreferenceScreen(), nullValue()); + } + + @Test + public void shouldRecordPreferencesResourceId() { + assertThat(shadow.getPreferencesResId(), equalTo(-1)); + activity.addPreferencesFromResource(R.xml.preferences); + assertThat(shadow.getPreferencesResId(), equalTo(R.xml.preferences)); + } + + @Test + public void shouldLoadPreferenceScreen() { + activity.addPreferencesFromResource(R.xml.preferences); + assertThat(activity.getPreferenceScreen().getPreferenceCount(), equalTo(7)); } - + @Test - public void shouldGetListView() { - shadow.setListView( new ListView( new Activity() ) ); - assertThat( activity.getListView(), notNullValue() ); + public void shouldFindPreferences() { + activity.addPreferencesFromResource(R.xml.preferences); + assertNotNull(activity.findPreference("category")); + assertNotNull(activity.findPreference("inside_category")); + assertNotNull(activity.findPreference("screen")); + assertNotNull(activity.findPreference("inside_screen")); + assertNotNull(activity.findPreference("checkbox")); + assertNotNull(activity.findPreference("edit_text")); + assertNotNull(activity.findPreference("list")); + assertNotNull(activity.findPreference("preference")); + assertNotNull(activity.findPreference("ringtone")); + } + + private static class TestPreferenceActivity extends PreferenceActivity { } - - @Test - public void shouldInheritFromListActivity() { - assertThat(shadow, instanceOf(ShadowListActivity.class)); - } - - @Test - public void shouldNotInitializePreferenceScreen() { - assertThat(activity.getPreferenceScreen(), nullValue()); - } - - @Test - public void shouldRecordPreferencesResourceId() { - assertThat(shadow.getPreferencesResId(), equalTo(-1)); - activity.addPreferencesFromResource(R.xml.preferences); - assertThat(shadow.getPreferencesResId(), equalTo(R.xml.preferences)); - } - - @Test - public void shouldLoadPreferenceScreen() { - activity.addPreferencesFromResource(R.xml.preferences); - assertThat( activity.getPreferenceScreen().getPreferenceCount(), equalTo(6)); - } - - private static class TestPreferenceActivity extends PreferenceActivity { - } } diff --git a/src/test/java/com/xtremelabs/robolectric/shadows/ResolveInfoTest.java b/src/test/java/com/xtremelabs/robolectric/shadows/ResolveInfoTest.java index 7dc976cc3..3e581ba2d 100644 --- a/src/test/java/com/xtremelabs/robolectric/shadows/ResolveInfoTest.java +++ b/src/test/java/com/xtremelabs/robolectric/shadows/ResolveInfoTest.java @@ -20,7 +20,7 @@ public class ResolveInfoTest { @Before public void setup() { - mResolveInfo = ShadowResolveInfo.newResolveInfo("name", "package", "activity"); + mResolveInfo = ShadowResolveInfo.newResolveInfo("name", "package", "fragmentActivity"); mShadowInfo = Robolectric.shadowOf(mResolveInfo); } @@ -35,6 +35,6 @@ public class ResolveInfoTest { assertThat(mResolveInfo.loadLabel(null).toString(), equalTo("name")); assertThat(mResolveInfo.activityInfo.packageName, equalTo("package")); assertThat(mResolveInfo.activityInfo.applicationInfo.packageName, equalTo("package")); - assertThat(mResolveInfo.activityInfo.name, equalTo("activity")); + assertThat(mResolveInfo.activityInfo.name, equalTo("fragmentActivity")); } } diff --git a/src/test/java/com/xtremelabs/robolectric/shadows/ResourcesTest.java b/src/test/java/com/xtremelabs/robolectric/shadows/ResourcesTest.java index 2455477dc..29601baee 100644 --- a/src/test/java/com/xtremelabs/robolectric/shadows/ResourcesTest.java +++ b/src/test/java/com/xtremelabs/robolectric/shadows/ResourcesTest.java @@ -1,6 +1,7 @@ package com.xtremelabs.robolectric.shadows; import android.app.Activity; +import android.content.res.ColorStateList; import android.content.res.Configuration; import android.content.res.Resources; import android.content.res.XmlResourceParser; @@ -19,12 +20,8 @@ import org.junit.runner.RunWith; import org.xmlpull.v1.XmlPullParser; 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 static org.junit.Assert.fail; +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; @RunWith(TestRunners.WithDefaults.class) @@ -36,9 +33,9 @@ public class ResourcesTest { @Before public void setup() { resources = new Activity().getResources(); - shadowApp = shadowOf( Robolectric.application ); + shadowApp = shadowOf( Robolectric.application ); } - + @Test(expected = Resources.NotFoundException.class) public void getStringArray_shouldThrowExceptionIfNotFound() throws Exception { resources.getStringArray(-1); @@ -61,6 +58,16 @@ public class ResourcesTest { assertThat(resources.newTheme(), notNullValue()); } + @Test + public void testGetAndSetConfiguration_SameInstance() throws Exception { + Activity activity = new Activity(); + Resources resources = activity.getResources(); + assertSame(resources.getConfiguration(), resources.getConfiguration()); + Configuration diffConfig = new Configuration(); + shadowOf(resources).setConfiguration(diffConfig); + assertSame(diffConfig, resources.getConfiguration()); + } + /** * a missing R.class will result in an BitmapDrawable getting returned * by default @@ -70,7 +77,7 @@ public class ResourcesTest { shadowApp.getResourceLoader().setLocalRClass( null ); assertThat( resources.getDrawable( TestR.anim.test_anim_1 ), instanceOf( BitmapDrawable.class ) ); } - + /** * given an R.anim.id value, will return an AnimationDrawable */ @@ -84,7 +91,7 @@ public class ResourcesTest { @Values( qualifiers="fr" ) public void testGetValuesResFromSpecifiecQualifiers(){ String hello=resources.getString( R.string.hello ); - assertThat( hello, equalTo( "Bonjour" ) ); + assertThat( hello, equalTo("Bonjour") ); } /** @@ -93,7 +100,25 @@ public class ResourcesTest { @Test public void testGetColorDrawable() { shadowApp.getResourceLoader().setLocalRClass( TestR.class ); - assertThat( resources.getDrawable( TestR.color.test_color_1 ), instanceOf( ColorDrawable.class ) ); + assertThat( resources.getDrawable( TestR.color.test_color_1 ), instanceOf( ColorDrawable.class ) ); + } + + /** + * given an R.color.id value, will return a Color + */ + @Test + public void testGetColor() { + shadowApp.getResourceLoader().setLocalRClass( TestR.class ); + assertThat( resources.getColor( TestR.color.test_color_1 ), not( 0 ) ); + } + + /** + * given an R.color.id value, will return a ColorStateList + */ + @Test + public void testGetColorStateList() { + shadowApp.getResourceLoader().setLocalRClass( TestR.class ); + assertThat( resources.getColorStateList( TestR.color.test_color_1 ), instanceOf( ColorStateList.class ) ); } /** @@ -101,16 +126,16 @@ public class ResourcesTest { */ @Test public void testGetBitmapDrawable() { - shadowApp.getResourceLoader().setLocalRClass( TestR.class ); - assertThat( resources.getDrawable( TestR.drawable.test_drawable_1 ), instanceOf( BitmapDrawable.class ) ); + shadowApp.getResourceLoader().setLocalRClass( TestR.class ); + assertThat( resources.getDrawable( TestR.drawable.test_drawable_1 ), instanceOf( BitmapDrawable.class ) ); } - + /** * given an R.drawable.id value, will return a NinePatchDrawable for .9.png file */ @Test public void testGetNinePatchDrawable() { - assertThat(resources.getDrawable(R.drawable.nine_patch_drawable ), instanceOf(NinePatchDrawable.class ) ); + assertThat(resources.getDrawable(R.drawable.nine_patch_drawable ), instanceOf(NinePatchDrawable.class ) ); } /** @@ -118,8 +143,8 @@ public class ResourcesTest { */ @Test public void testGetBitmapDrawableForUnknownId() { - shadowApp.getResourceLoader().setLocalRClass( TestR.class ); - assertThat(resources.getDrawable( Integer.MAX_VALUE ), instanceOf( BitmapDrawable.class )); + shadowApp.getResourceLoader().setLocalRClass( TestR.class ); + assertThat(resources.getDrawable( Integer.MAX_VALUE ), instanceOf( BitmapDrawable.class )); } @Test public void testDensity() { @@ -139,6 +164,40 @@ public class ResourcesTest { assertThat(activity.getResources().getDisplayMetrics().heightPixels, equalTo(800)); assertThat(activity.getResources().getDisplayMetrics().widthPixels, equalTo(480)); } + + @Test + public void getSystemShouldReturnSystemResources() throws Exception { + assertThat(Resources.getSystem(), instanceOf(Resources.class)); + } + + @Test + public void multipleCallsToGetSystemShouldReturnSameInstance() throws Exception { + assertThat(Resources.getSystem(), equalTo(Resources.getSystem())); + } + + @Test + public void applicationResourcesShouldHaveBothSystemAndLocalValues() throws Exception { + Activity activity = new Activity(); + assertThat(activity.getResources().getString(android.R.string.copy), equalTo("Copy")); + assertThat(activity.getResources().getString(R.string.copy), equalTo("Local Copy")); + } + + @Test + public void systemResourcesShouldHaveSystemValuesOnly() throws Exception { + assertThat(Resources.getSystem().getString(android.R.string.copy), equalTo("Copy")); + assertThat(Resources.getSystem().getString(R.string.copy), nullValue()); + } + + @Test + public void systemResourcesShouldReturnCorrectSystemId() throws Exception { + assertThat(Resources.getSystem().getIdentifier("copy", "android:string", null), + equalTo(android.R.string.copy)); + } + + @Test + public void systemResourcesShouldReturnZeroForLocalId() throws Exception { + assertThat(Resources.getSystem().getIdentifier("copy", "string", null), equalTo(0)); + } @Test public void testGetXml() throws Exception { diff --git a/src/test/java/com/xtremelabs/robolectric/shadows/SensorManagerTest.java b/src/test/java/com/xtremelabs/robolectric/shadows/SensorManagerTest.java index bac93e993..a608d0753 100644 --- a/src/test/java/com/xtremelabs/robolectric/shadows/SensorManagerTest.java +++ b/src/test/java/com/xtremelabs/robolectric/shadows/SensorManagerTest.java @@ -68,6 +68,13 @@ public class SensorManagerTest { assertTrue(shadow.createSensorEvent() instanceof SensorEvent); } + @Test + public void getSensor_shouldBeConfigurable() { + Sensor sensor = Robolectric.newInstanceOf(Sensor.class); + shadowOf(sensorManager).addSensor(Sensor.TYPE_ACCELEROMETER, sensor); + assertSame(sensor, sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER)); + } + private class TestSensorEventListener implements SensorEventListener { @Override diff --git a/src/test/java/com/xtremelabs/robolectric/shadows/ShadowEditTextTest.java b/src/test/java/com/xtremelabs/robolectric/shadows/ShadowEditTextTest.java index 915364309..17b5f4521 100644 --- a/src/test/java/com/xtremelabs/robolectric/shadows/ShadowEditTextTest.java +++ b/src/test/java/com/xtremelabs/robolectric/shadows/ShadowEditTextTest.java @@ -4,6 +4,7 @@ import android.widget.EditText; import com.xtremelabs.robolectric.Robolectric; import com.xtremelabs.robolectric.TestRunners; import com.xtremelabs.robolectric.tester.android.util.TestAttributeSet; +import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -14,13 +15,25 @@ import static org.junit.Assert.assertThat; @RunWith(TestRunners.WithDefaults.class) public class ShadowEditTextTest { - @Test - public void shouldRespectMaxLength() throws Exception { + private EditText editText; + + @Before + public void setup() { HashMap<String, String> hash = new HashMap<String, String>(); hash.put("android:maxLength", "5"); TestAttributeSet attributeSet = new TestAttributeSet(hash); - EditText editText = new EditText(Robolectric.application, attributeSet); + editText = new EditText(Robolectric.application, attributeSet); + } + + @Test + public void shouldRespectMaxLength() throws Exception { editText.setText("0123456678"); assertThat(editText.getText().toString(), equalTo("01234")); } + + @Test + public void shouldAcceptNullStrings() { + editText.setText(null); + assertThat(editText.getText().toString(), equalTo("")); + } } diff --git a/src/test/java/com/xtremelabs/robolectric/shadows/SpannableStringBuilderTest.java b/src/test/java/com/xtremelabs/robolectric/shadows/SpannableStringBuilderTest.java index 889e752b5..d925a4232 100644 --- a/src/test/java/com/xtremelabs/robolectric/shadows/SpannableStringBuilderTest.java +++ b/src/test/java/com/xtremelabs/robolectric/shadows/SpannableStringBuilderTest.java @@ -32,11 +32,18 @@ public class SpannableStringBuilderTest { } @Test + public void testReplaceFromSquare() throws Exception { + SpannableStringBuilder builder = new SpannableStringBuilder("abcd"); + builder.replace(1,3,"XXX"); + assertThat(builder.toString(), equalTo("aXXXd")); + } + + @Test public void testInsert() throws Exception { SpannableStringBuilder builder = new SpannableStringBuilder("abc"); assertThat(builder.insert(1, "xy").toString(), equalTo("axybc")); } - + @Test public void testDelete() throws Exception { SpannableStringBuilder builder = new SpannableStringBuilder("abc"); @@ -44,4 +51,11 @@ public class SpannableStringBuilderTest { builder.delete( 0, 3 ); assertThat( builder.length(), equalTo(0)); } + + @Test + public void testReplace_extraParams() throws Exception { + SpannableStringBuilder builder = new SpannableStringBuilder("abcd"); + builder.replace(1,3,"ignoreXXXignore", 6, 9); + assertThat(builder.toString(), equalTo("aXXXd")); + } } diff --git a/src/test/java/com/xtremelabs/robolectric/shadows/SpannedStringTest.java b/src/test/java/com/xtremelabs/robolectric/shadows/SpannedStringTest.java new file mode 100644 index 000000000..74895b0d7 --- /dev/null +++ b/src/test/java/com/xtremelabs/robolectric/shadows/SpannedStringTest.java @@ -0,0 +1,33 @@ +package com.xtremelabs.robolectric.shadows; + +import android.text.SpannedString; +import com.xtremelabs.robolectric.TestRunners; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertSame; + +@RunWith(TestRunners.WithDefaults.class) +public class SpannedStringTest { + + @Test + public void toString_shouldDelegateToUnderlyingCharSequence() { + SpannedString spannedString = new SpannedString("foo"); + assertEquals("foo", spannedString.toString()); + } + + @Test + public void valueOfSpannedString_shouldReturnItself() { + SpannedString spannedString = new SpannedString("foo"); + assertSame(spannedString, SpannedString.valueOf(spannedString)); + } + + @Test + public void valueOfCharSequence_shouldReturnNewSpannedString() { + assertEquals("foo", SpannedString.valueOf("foo").toString()); + } + + +} + diff --git a/src/test/java/com/xtremelabs/robolectric/shadows/TextUtilsTest.java b/src/test/java/com/xtremelabs/robolectric/shadows/TextUtilsTest.java index 3b9a2ebea..cb28c7f0a 100644 --- a/src/test/java/com/xtremelabs/robolectric/shadows/TextUtilsTest.java +++ b/src/test/java/com/xtremelabs/robolectric/shadows/TextUtilsTest.java @@ -1,5 +1,6 @@ package com.xtremelabs.robolectric.shadows; +import android.text.TextPaint; import android.text.TextUtils; import com.xtremelabs.robolectric.TestRunners; @@ -80,4 +81,10 @@ public class TextUtilsTest { assertThat(TextUtils.equals("a", "ab"), equalTo(false)); assertThat(TextUtils.equals("ab", "a"), equalTo(false)); } + + @Test public void testEllipsize() { + TextPaint p = new TextPaint(); + assertThat(TextUtils.ellipsize("apples", p, 100, TextUtils.TruncateAt.END).toString(), equalTo("apples")); + assertThat(TextUtils.ellipsize("", p, 100, TextUtils.TruncateAt.END).toString(), equalTo("")); + } } diff --git a/src/test/java/com/xtremelabs/robolectric/shadows/TextViewTest.java b/src/test/java/com/xtremelabs/robolectric/shadows/TextViewTest.java index a157e8a63..ae9e3deca 100644 --- a/src/test/java/com/xtremelabs/robolectric/shadows/TextViewTest.java +++ b/src/test/java/com/xtremelabs/robolectric/shadows/TextViewTest.java @@ -150,14 +150,14 @@ public class TextViewTest { view.setTransformationMethod(new ShadowPasswordTransformationMethod()); assertEquals(view.getTransformationMethod().getClass(), ShadowPasswordTransformationMethod.class); } - + @Test public void testGetInputType() throws Exception { assertThat(textView.getInputType(), not(equalTo(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD))); textView.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD); assertThat(textView.getInputType(), equalTo(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD)); } - + @Test public void givenATextViewWithATextWatcherAdded_WhenSettingTextWithTextResourceId_ShouldNotifyTextWatcher() { MockTextWatcher mockTextWatcher = new MockTextWatcher(); @@ -167,7 +167,7 @@ public class TextViewTest { assertEachTextWatcherEventWasInvoked(mockTextWatcher); } - + @Test public void givenATextViewWithATextWatcherAdded_WhenSettingTextWithCharSequence_ShouldNotifyTextWatcher() { MockTextWatcher mockTextWatcher = new MockTextWatcher(); @@ -234,6 +234,7 @@ public class TextViewTest { assertThat(mockTextWatcher.afterTextChangeArgument.toString(), equalTo(NEW_TEXT)); } + @Test public void whenAppendingText_ShouldAppendNewTextAfterOldOne() { textView.setText(INITIAL_TEXT); @@ -386,6 +387,26 @@ public class TextViewTest { assertFalse(textView.hasSelection()); } + @Test + public void whenSettingTextToNull_WatchersSeeEmptyString() { + TextWatcher mockTextWatcher = mock(TextWatcher.class); + textView.addTextChangedListener(mockTextWatcher); + textView.setText(null); + verify(mockTextWatcher).onTextChanged("", 0, 0, 0); + } + + @Test + public void getPaint_returnsNonNull() { + assertNotNull(textView.getPaint()); + } + + @Test + public void testNoArgAppend() { + textView.setText("a"); + textView.append("b"); + assertThat(textView.getText().toString(), equalTo("ab")); + } + private List<MockTextWatcher> anyNumberOfTextWatchers() { List<MockTextWatcher> mockTextWatchers = new ArrayList<MockTextWatcher>(); int numberBetweenOneAndTen = new Random().nextInt(10) + 1; diff --git a/src/test/java/com/xtremelabs/robolectric/shadows/TypedArrayTest.java b/src/test/java/com/xtremelabs/robolectric/shadows/TypedArrayTest.java index 0a87c9c65..6b693cff6 100644 --- a/src/test/java/com/xtremelabs/robolectric/shadows/TypedArrayTest.java +++ b/src/test/java/com/xtremelabs/robolectric/shadows/TypedArrayTest.java @@ -11,6 +11,7 @@ import org.junit.runner.RunWith; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assert.assertNotNull; @RunWith(TestRunners.WithDefaults.class) public class TypedArrayTest { @@ -28,4 +29,29 @@ public class TypedArrayTest { assertThat(array.getString(0), equalTo("expected value")); } + + @Test + public void getResources() throws Exception { + assertNotNull(context.obtainStyledAttributes(null).getResources()); + } + + @Test + public void getInt_shouldReturnDefaultValue() throws Exception { + assertThat(context.obtainStyledAttributes(null).getInt(1, -1), equalTo(-1)); + } + + @Test + public void getInteger_shouldReturnDefaultValue() throws Exception { + assertThat(context.obtainStyledAttributes(null).getInteger(1, -1), equalTo(-1)); + } + + @Test + public void getResourceId_shouldReturnDefaultValue() throws Exception { + assertThat(context.obtainStyledAttributes(null).getResourceId(1, -1), equalTo(-1)); + } + + @Test + public void getDimension_shouldReturnDefaultValue() throws Exception { + assertThat(context.obtainStyledAttributes(null).getDimension(1, -1f), equalTo(-1f)); + } } diff --git a/src/test/java/com/xtremelabs/robolectric/shadows/ViewAnimatorTest.java b/src/test/java/com/xtremelabs/robolectric/shadows/ViewAnimatorTest.java index 81bba9be1..8c95411eb 100644 --- a/src/test/java/com/xtremelabs/robolectric/shadows/ViewAnimatorTest.java +++ b/src/test/java/com/xtremelabs/robolectric/shadows/ViewAnimatorTest.java @@ -1,6 +1,6 @@ package com.xtremelabs.robolectric.shadows; -import android.app.Activity; +import android.app.Application; import android.view.View; import android.widget.ViewAnimator; import com.xtremelabs.robolectric.TestRunners; @@ -9,36 +9,69 @@ import org.junit.Test; import org.junit.runner.RunWith; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertSame; @RunWith(TestRunners.WithDefaults.class) public class ViewAnimatorTest { - protected ViewAnimator animator; + + ViewAnimator viewAnimator; + Application application; @Before public void setUp() { - animator = new ViewAnimator(new Activity()); + application = new Application(); + viewAnimator = new ViewAnimator(application); + } + + @Test + public void getDisplayedChildWhenEmpty_shouldDefaultToZero() { + assertEquals(0, viewAnimator.getDisplayedChild()); + } + + @Test + public void getDisplayedChild_shouldDefaultToZero() { + viewAnimator.addView(new View(application)); + assertEquals(0, viewAnimator.getDisplayedChild()); + } + + @Test + public void setDisplayedChild_shouldUpdateDisplayedChildIndex() { + viewAnimator.addView(new View(application)); + viewAnimator.addView(new View(application)); + viewAnimator.setDisplayedChild(2); + assertEquals(2, viewAnimator.getDisplayedChild()); + } + + @Test + public void getCurrentView_shouldWork() { + View view0 = new View(application); + View view1 = new View(application); + viewAnimator.addView(view0); + viewAnimator.addView(view1); + assertSame(view0, viewAnimator.getCurrentView()); + viewAnimator.setDisplayedChild(1); + assertSame(view1, viewAnimator.getCurrentView()); } @Test - public void testHappyPath() { - View v = new View(null); - animator.addView(v); - - assertEquals(0, animator.getDisplayedChild()); - assertEquals(v, animator.getCurrentView()); + public void showNext_shouldDisplayNextChild() { + viewAnimator.addView(new View(application)); + viewAnimator.addView(new View(application)); + assertEquals(0, viewAnimator.getDisplayedChild()); + viewAnimator.showNext(); + assertEquals(1, viewAnimator.getDisplayedChild()); + viewAnimator.showNext(); + assertEquals(0, viewAnimator.getDisplayedChild()); } @Test - public void testAnimatorHandlesCyclingViews() { - View v1 = new View(null); - View v2 = new View(null); - - animator.addView(v1); - animator.addView(v2); - - animator.showNext(); - - assertEquals(1, animator.getDisplayedChild()); - assertEquals(v2, animator.getCurrentView()); + public void showPrevious_shouldDisplayPreviousChild() { + viewAnimator.addView(new View(application)); + viewAnimator.addView(new View(application)); + assertEquals(0, viewAnimator.getDisplayedChild()); + viewAnimator.showPrevious(); + assertEquals(1, viewAnimator.getDisplayedChild()); + viewAnimator.showPrevious(); + assertEquals(0, viewAnimator.getDisplayedChild()); } } diff --git a/src/test/java/com/xtremelabs/robolectric/shadows/ViewGroupTest.java b/src/test/java/com/xtremelabs/robolectric/shadows/ViewGroupTest.java index 7f3c3e4d0..4c3c216dd 100644 --- a/src/test/java/com/xtremelabs/robolectric/shadows/ViewGroupTest.java +++ b/src/test/java/com/xtremelabs/robolectric/shadows/ViewGroupTest.java @@ -7,7 +7,6 @@ import android.view.animation.Animation; import android.view.animation.Animation.AnimationListener; import android.view.animation.LayoutAnimationController; import android.widget.FrameLayout; -import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; import com.xtremelabs.robolectric.R; @@ -25,8 +24,7 @@ import java.io.PrintStream; import static com.xtremelabs.robolectric.Robolectric.DEFAULT_SDK_VERSION; import static com.xtremelabs.robolectric.Robolectric.shadowOf; -import static org.hamcrest.CoreMatchers.equalTo; -import static org.hamcrest.CoreMatchers.sameInstance; +import static org.hamcrest.CoreMatchers.*; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.IsNull.nullValue; import static org.junit.Assert.*; @@ -72,6 +70,11 @@ public class ViewGroupTest { } @Test + public void removeNullView_doesNothing() { + root.removeView(null); + } + + @Test public void testLayoutAnimationListener() { assertThat(root.getLayoutAnimationListener(), nullValue()); @@ -89,7 +92,7 @@ public class ViewGroupTest { assertThat(root.getLayoutAnimationListener(), sameInstance(animationListener)); } - + @Test public void testLayoutAnimation() { assertThat(root.getLayoutAnimation(), nullValue()); @@ -242,11 +245,25 @@ public class ViewGroupTest { public void addViewWithLayoutParams_shouldStoreLayoutParams() throws Exception { FrameLayout.LayoutParams layoutParams1 = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); FrameLayout.LayoutParams layoutParams2 = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); - ImageView child1 = new ImageView(Robolectric.application); - ImageView child2 = new ImageView(Robolectric.application); + View child1 = new View(Robolectric.application); + View child2 = new View(Robolectric.application); root.addView(child1, layoutParams1); root.addView(child2, 1, layoutParams2); assertSame(layoutParams1, child1.getLayoutParams()); assertSame(layoutParams2, child2.getLayoutParams()); } + + @Test + public void getChildAt_shouldReturnNullForInvalidIndices() { + assertThat(root.getChildCount(), equalTo(3)); + assertThat(root.getChildAt(13), nullValue()); + assertThat(root.getChildAt(3), nullValue()); + assertThat(root.getChildAt(-1), nullValue()); + } + + @Test + public void layoutParams_shouldBeViewGroupLayoutParams() { + assertThat(child1.getLayoutParams(), instanceOf(FrameLayout.LayoutParams.class)); + assertThat(child1.getLayoutParams(), instanceOf(ViewGroup.LayoutParams.class)); + } } diff --git a/src/test/java/com/xtremelabs/robolectric/shadows/ViewTest.java b/src/test/java/com/xtremelabs/robolectric/shadows/ViewTest.java index f2bc93d21..03c27ad3b 100644 --- a/src/test/java/com/xtremelabs/robolectric/shadows/ViewTest.java +++ b/src/test/java/com/xtremelabs/robolectric/shadows/ViewTest.java @@ -2,8 +2,11 @@ package com.xtremelabs.robolectric.shadows; import android.app.Activity; import android.content.Context; +import android.graphics.BitmapFactory; import android.graphics.Point; +import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.ColorDrawable; +import android.graphics.drawable.Drawable; import android.view.MotionEvent; import android.view.View; import android.view.View.MeasureSpec; @@ -16,12 +19,16 @@ import android.widget.LinearLayout; import com.xtremelabs.robolectric.R; import com.xtremelabs.robolectric.Robolectric; import com.xtremelabs.robolectric.TestRunners; +import com.xtremelabs.robolectric.tester.android.util.TestAttributeSet; +import com.xtremelabs.robolectric.tester.android.view.TestWindow; import com.xtremelabs.robolectric.util.*; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import static com.xtremelabs.robolectric.Robolectric.shadowOf; +import java.util.concurrent.atomic.AtomicBoolean; + +import static com.xtremelabs.robolectric.Robolectric.*; import static junit.framework.Assert.assertEquals; import static org.hamcrest.CoreMatchers.*; import static org.hamcrest.MatcherAssert.assertThat; @@ -30,9 +37,11 @@ import static org.junit.Assert.*; @RunWith(TestRunners.WithDefaults.class) public class ViewTest { private View view; + private Transcript transcript; @Before public void setUp() throws Exception { + transcript = new Transcript(); view = new View(new Activity()); } @@ -53,6 +62,38 @@ public class ViewTest { } @Test + public void measuredDimensions() throws Exception { + View view1 = new View(null) { + { + setMeasuredDimension(123, 456); + } + }; + assertThat(view1.getMeasuredWidth(), equalTo(123)); + assertThat(view1.getMeasuredHeight(), equalTo(456)); + } + + @Test + public void layout_shouldCallOnLayoutOnlyIfChanged() throws Exception { + View view1 = new View(null) { + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + transcript.add("onLayout " + changed + " " + left + " " + top + " " + right + " " + bottom); + } + + @Override + public void invalidate() { + transcript.add("invalidate"); + } + }; + view1.layout(0, 0, 0, 0); + transcript.assertNoEventsSoFar(); + view1.layout(1, 2, 3, 4); + transcript.assertEventsSoFar("invalidate", "onLayout true 1 2 3 4"); + view1.layout(1, 2, 3, 4); + transcript.assertNoEventsSoFar(); + } + + @Test public void shouldFocus() throws Exception { final Transcript transcript = new Transcript(); @@ -175,6 +216,14 @@ public class ViewTest { } @Test + public void shouldRecordBackgroundDrawable() { + Drawable drawable = new BitmapDrawable(BitmapFactory.decodeFile("some/fake/file")); + view.setBackgroundDrawable(drawable); + assertThat(view.getBackground(), sameInstance(drawable)); + assertThat(visualize(view), equalTo("background:\nBitmap for file:some/fake/file")); + } + + @Test public void shouldPostActionsToTheMessageQueue() throws Exception { Robolectric.pauseMainLooper(); @@ -218,6 +267,54 @@ public class ViewTest { } @Test + public void shouldRememberIsPressed() { + view.setPressed(true); + assertTrue(view.isPressed()); + view.setPressed(false); + assertFalse(view.isPressed()); + } + + @Test + public void shouldAddOnClickListenerFromAttribute() throws Exception { + TestAttributeSet attrs = new TestAttributeSet(); + attrs.put("android:onClick", "clickMe"); + + view = new View(null, attrs); + assertNotNull(shadowOf(view).getOnClickListener()); + } + + @Test + public void shouldCallOnClickWithAttribute() throws Exception { + final AtomicBoolean called = new AtomicBoolean(false); + Activity context = new Activity() { + public void clickMe(View view) { + called.set(true); + } + }; + TestAttributeSet attrs = new TestAttributeSet(); + attrs.put("android:onClick", "clickMe"); + + view = new View(context, attrs); + view.performClick(); + assertTrue("Should have been called", called.get()); + } + + @Test(expected = RuntimeException.class) + public void shouldThrowExceptionWithBadMethodName() throws Exception { + final AtomicBoolean called = new AtomicBoolean(false); + Activity context = new Activity() { + public void clickMe(View view) { + called.set(true); + } + }; + TestAttributeSet attrs = new TestAttributeSet(); + attrs.put("android:onClick", "clickYou"); + + view = new View(context, attrs); + view.performClick(); + } + + @Test public void shouldSetAnimation() throws Exception { Animation anim = new TestAnimation(); view.setAnimation(anim); @@ -490,4 +587,58 @@ public class ViewTest { super.onMeasure(800, 400); } } + + @Test public void shouldCallOnAttachedToAndDetachedFromWindow() throws Exception { + MyView parent = new MyView("parent"); + parent.addView(new MyView("child")); + transcript.assertNoEventsSoFar(); + + TestWindow window = new TestWindow(application); + window.setContentView(parent); + transcript.assertEventsSoFar("parent attached", "child attached"); + + parent.addView(new MyView("another child")); + transcript.assertEventsSoFar("another child attached"); + + MyView temporaryChild = new MyView("temporary child"); + parent.addView(temporaryChild); + transcript.assertEventsSoFar("temporary child attached"); + assertTrue(shadowOf(temporaryChild).isAttachedToWindow()); + + parent.removeView(temporaryChild); + transcript.assertEventsSoFar("temporary child detached"); + assertFalse(shadowOf(temporaryChild).isAttachedToWindow()); + + window.setContentView(null); + transcript.assertEventsSoFar("parent detached", "child detached", "another child detached"); + } + + @Test public void removeAllViews_shouldCallOnAttachedToAndDetachedFromWindow() throws Exception { + MyView parent = new MyView("parent"); + parent.addView(new MyView("child")); + parent.addView(new MyView("another child")); + new TestWindow(application).setContentView(parent); + transcript.clear(); + parent.removeAllViews(); + transcript.assertEventsSoFar("child detached", "another child detached"); + } + + private class MyView extends LinearLayout { + private String name; + + public MyView(String name) { + super(Robolectric.application); + this.name = name; + } + + @Override protected void onAttachedToWindow() { + transcript.add(name + " attached"); + super.onAttachedToWindow(); + } + + @Override protected void onDetachedFromWindow() { + transcript.add(name + " detached"); + super.onDetachedFromWindow(); + } + } } diff --git a/src/test/java/com/xtremelabs/robolectric/shadows/testing/InnerActivity.java b/src/test/java/com/xtremelabs/robolectric/shadows/testing/InnerActivity.java new file mode 100644 index 000000000..63a56c6dc --- /dev/null +++ b/src/test/java/com/xtremelabs/robolectric/shadows/testing/InnerActivity.java @@ -0,0 +1,16 @@ +package com.xtremelabs.robolectric.shadows.testing; + +import android.app.Activity; +import android.os.Bundle; +import com.xtremelabs.robolectric.R; + +/** + * + */ +public class InnerActivity extends Activity { + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.lam_inner); + } +} diff --git a/src/test/java/com/xtremelabs/robolectric/shadows/testing/LocalActivityManagerContainer.java b/src/test/java/com/xtremelabs/robolectric/shadows/testing/LocalActivityManagerContainer.java new file mode 100644 index 000000000..9f4be93a6 --- /dev/null +++ b/src/test/java/com/xtremelabs/robolectric/shadows/testing/LocalActivityManagerContainer.java @@ -0,0 +1,40 @@ +package com.xtremelabs.robolectric.shadows.testing; + +import android.app.Activity; +import android.app.LocalActivityManager; +import android.content.Context; +import android.content.Intent; +import android.util.AttributeSet; +import android.view.View; +import android.view.Window; +import android.widget.LinearLayout; +import com.xtremelabs.robolectric.R; + +@SuppressWarnings("UnusedDeclaration") +// Used in lam_outer.xml +public class LocalActivityManagerContainer extends LinearLayout { + public LocalActivityManagerContainer(Context context) { + super(context); + init(); + } + + public LocalActivityManagerContainer(Context context, AttributeSet attrs) { + super(context, attrs); + init(); + } + + public LocalActivityManagerContainer(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + init(); + } + + private void init() { + LocalActivityManager lam = new LocalActivityManager((Activity) getContext(), true); + lam.dispatchCreate(null); + final Window window = lam.startActivity("foo", new Intent(getContext(), InnerActivity.class)); + // Add the decorView's child to this LinearLayout's children. + final View innerContents = window.getDecorView().findViewById(R.id.lam_inner_contents); + addView(innerContents); + } + +} diff --git a/src/test/java/com/xtremelabs/robolectric/tester/android/view/TestWindowTest.java b/src/test/java/com/xtremelabs/robolectric/tester/android/view/TestWindowTest.java index fd6cb494b..49b175c29 100644 --- a/src/test/java/com/xtremelabs/robolectric/tester/android/view/TestWindowTest.java +++ b/src/test/java/com/xtremelabs/robolectric/tester/android/view/TestWindowTest.java @@ -1,10 +1,17 @@ package com.xtremelabs.robolectric.tester.android.view; +import android.view.View; +import android.view.ViewGroup; +import com.xtremelabs.robolectric.R; +import com.xtremelabs.robolectric.Robolectric; import com.xtremelabs.robolectric.TestRunners; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; +import static org.hamcrest.CoreMatchers.equalTo; +import static org.junit.Assert.assertThat; + @RunWith(TestRunners.WithDefaults.class) public class TestWindowTest { @@ -13,4 +20,26 @@ public class TestWindowTest { TestWindow window = new TestWindow(null); Assert.assertNotNull(window.getWindowManager()); } + + @Test + public void decorViewFindViewById__shouldReturnContentWrapper() throws Exception { + TestWindow window = new TestWindow(null); + View contentView = new View(null); + contentView.setTag("content view"); + window.setContentView(contentView); + + // This is the real meat of the test. ActionBarSherlock relies on this code: + // window.getDecorView().findViewById(R.id.content) + ViewGroup contentWrapper = (ViewGroup) window.getDecorView().findViewById(android.R.id.content); + assertThat("child count", contentWrapper.getChildCount(), equalTo(1)); + assertThat(contentWrapper.getChildAt(0).getTag(), equalTo(contentView.getTag())); + } + + @Test public void setContentViewByResource() throws Exception { + TestWindow window = new TestWindow(Robolectric.application); + window.setContentView(R.layout.text_views); + + ViewGroup contentWrapper = (ViewGroup) window.findViewById(android.R.id.content); + assertThat("child count", contentWrapper.getChildCount(), equalTo(1)); + } } diff --git a/src/test/java/com/xtremelabs/robolectric/util/CustomFragment.java b/src/test/java/com/xtremelabs/robolectric/util/CustomFragment.java new file mode 100644 index 000000000..5fc50a0f5 --- /dev/null +++ b/src/test/java/com/xtremelabs/robolectric/util/CustomFragment.java @@ -0,0 +1,23 @@ +package com.xtremelabs.robolectric.util; + +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +/** + * Stub class that can be used for testing support-lib fragments. + */ +public class CustomFragment extends Fragment { + + private TextView textView; + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + textView = new TextView(inflater.getContext()); + textView.setText("CustomFragment text view"); + return textView; + } +} diff --git a/src/test/java/com/xtremelabs/robolectric/util/TestUtil.java b/src/test/java/com/xtremelabs/robolectric/util/TestUtil.java index 7a77707af..0b0ff209b 100644 --- a/src/test/java/com/xtremelabs/robolectric/util/TestUtil.java +++ b/src/test/java/com/xtremelabs/robolectric/util/TestUtil.java @@ -2,8 +2,15 @@ package com.xtremelabs.robolectric.util; import com.xtremelabs.robolectric.RobolectricConfig; +import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; +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.util.Collection; import java.util.Map; import java.util.Properties; @@ -93,4 +100,19 @@ public abstract class TestUtil { return file(new File(sdkDir, "platforms/android-" + DEFAULT_SDK_VERSION + "/data/res/"), paths); } + + public static String readString(InputStream is) throws IOException { + Writer writer = new StringWriter(); + char[] buffer = new char[1024]; + try { + Reader reader = new BufferedReader(new InputStreamReader(is, "UTF-8")); + int n; + while ((n = reader.read(buffer)) != -1) { + writer.write(buffer, 0, n); + } + } finally { + is.close(); + } + return writer.toString(); + } } diff --git a/src/test/resources/project.properties b/src/test/resources/project.properties new file mode 100644 index 000000000..9db28a091 --- /dev/null +++ b/src/test/resources/project.properties @@ -0,0 +1,9 @@ +# This is for RoboelectricConfigTest to ensure that we parse and include +# library references. + +# Project target. +target=android-42 +android.library.reference.1=../lib1 +android.library.reference.2=../lib2 +android.library.reference.3=../lib3 +android.library.reference.4=../lib4
\ No newline at end of file diff --git a/src/test/resources/res/layout-land/multi_orientation.xml b/src/test/resources/res/layout-land/multi_orientation.xml new file mode 100644 index 000000000..3e53c2451 --- /dev/null +++ b/src/test/resources/res/layout-land/multi_orientation.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> + +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/landscape" + android:orientation="vertical" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:gravity="center_horizontal" + > + + <View + android:layout_width="fill_parent" + android:layout_height="10dip" + /> + + <fragment + android:name="com.xtremelabs.robolectric.util.CustomFragment" + android:id="@+id/my_landscape_fragment" + android:layout_width="match_parent" + android:layout_height="match_parent" + /> +</LinearLayout> diff --git a/src/test/resources/res/layout/edit_text.xml b/src/test/resources/res/layout/edit_text.xml new file mode 100644 index 000000000..3b64e9482 --- /dev/null +++ b/src/test/resources/res/layout/edit_text.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="utf-8"?> + +<EditText + xmlns:android="http://schemas.android.com/apk/res/android" + android:hint="Hello, Hint" + /> diff --git a/src/test/resources/res/layout/fragment.xml b/src/test/resources/res/layout/fragment.xml new file mode 100644 index 000000000..9cfd4c9da --- /dev/null +++ b/src/test/resources/res/layout/fragment.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> + +<fragment xmlns:android="http://schemas.android.com/apk/res/android" + android:name="com.xtremelabs.robolectric.util.CustomFragment" + android:id="@+id/my_fragment" + android:layout_width="match_parent" + android:layout_height="match_parent"/>
\ No newline at end of file diff --git a/src/test/resources/res/layout/fragment_contents.xml b/src/test/resources/res/layout/fragment_contents.xml new file mode 100644 index 000000000..dbcc86d0a --- /dev/null +++ b/src/test/resources/res/layout/fragment_contents.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + android:orientation="vertical" + > + <TextView + android:id="@+id/tacos" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="TACOS"/> + <TextView + android:id="@+id/burritos" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="BURRITOS"/> +</LinearLayout> diff --git a/src/test/resources/res/layout/lam_inner.xml b/src/test/resources/res/layout/lam_inner.xml new file mode 100644 index 000000000..688777542 --- /dev/null +++ b/src/test/resources/res/layout/lam_inner.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="utf-8"?> + +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:gravity="center_horizontal" + > + + <TextView + android:id="@+id/lam_inner_contents" + android:layout_weight="fill_parent" + android:layout_height="fill_parent" + /> +</LinearLayout> + diff --git a/src/test/resources/res/layout/lam_outer.xml b/src/test/resources/res/layout/lam_outer.xml new file mode 100644 index 000000000..4d771a7b4 --- /dev/null +++ b/src/test/resources/res/layout/lam_outer.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="utf-8"?> + +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:gravity="center_horizontal" + > + + <com.xtremelabs.robolectric.shadows.testing.LocalActivityManagerContainer + android:id="@+id/lam_container" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + /> + +</LinearLayout> diff --git a/src/test/resources/res/layout/multi_orientation.xml b/src/test/resources/res/layout/multi_orientation.xml new file mode 100644 index 000000000..351e48d42 --- /dev/null +++ b/src/test/resources/res/layout/multi_orientation.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> + +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/portrait" + android:orientation="vertical" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:gravity="center_horizontal" + > + + <View + android:layout_width="fill_parent" + android:layout_height="10dip" + /> + + <TextView + android:id="@+id/title" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textSize="14dip" + android:textColor="#fff" + android:text="I'm a Portrait Layout!" + /> +</LinearLayout> diff --git a/src/test/resources/res/values/attrs.xml b/src/test/resources/res/values/attrs.xml index b0f0b1298..f929d515b 100644 --- a/src/test/resources/res/values/attrs.xml +++ b/src/test/resources/res/values/attrs.xml @@ -1,9 +1,28 @@ <?xml version="1.0" encoding="utf-8"?> <resources> - <declare-styleable name="com.xtremelabs.robolectric.util.CustomView"> + <declare-styleable name="CustomView"> <attr name="itemType" format="enum"> <enum name="integer" value="0"/> <enum name="string" value="1"/> </attr> + + <attr name="scrollbars"> + <flag name="horizontal" value="0x00000100" /> + <flag name="vertical" value="0x00000200" /> + </attr> + + <attr name="gravity"/> + + <attr name="keycode"/> </declare-styleable> -</resources>
\ No newline at end of file + + <attr name="gravity"> + <flag name="center" value="0x11" /> + <flag name="fill_vertical" value="0x70" /> + </attr> + + <attr name="keycode"> + <enum name="KEYCODE_SOFT_RIGHT" value="2" /> + <enum name="KEYCODE_HOME" value="3" /> + </attr> +</resources> diff --git a/src/test/resources/res/values/colors.xml b/src/test/resources/res/values/colors.xml index 01f249b31..d4189fd27 100644 --- a/src/test/resources/res/values/colors.xml +++ b/src/test/resources/res/values/colors.xml @@ -11,6 +11,8 @@ <color name="android_namespaced_black">@android:color/black</color> + <color name="android_namespaced_transparent">@android:color/transparent</color> + <color name="android_red">red</color> </resources> diff --git a/src/test/resources/res/values/integers.xml b/src/test/resources/res/values/integers.xml new file mode 100644 index 000000000..5dc832695 --- /dev/null +++ b/src/test/resources/res/values/integers.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <integer name="meaning_of_life">42</integer> + <integer name="loneliest_number">1</integer> + <integer name="there_can_be_only">@integer/loneliest_number</integer> + <integer name="hex_int">0xFFFF0000</integer> +</resources>
\ No newline at end of file diff --git a/src/test/resources/res/xml/preferences.xml b/src/test/resources/res/xml/preferences.xml index ad4f095fc..4fbd9c40b 100644 --- a/src/test/resources/res/xml/preferences.xml +++ b/src/test/resources/res/xml/preferences.xml @@ -2,45 +2,56 @@ <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"> - <PreferenceCategory - android:key="category" - android:title="Category Test"> - - <Preference - android:key="inside_category" - android:title="Inside Category Test" - android:summary=""/> - - </PreferenceCategory> - - <CheckBoxPreference - android:key="checkbox" - android:title="Checkbox Test" - android:summary="" - android:defaultValue="true" - /> - - <EditTextPreference - android:key="edit_text" - android:title="EditText Test" - android:summary="" - /> - - <ListPreference - android:key="list" - android:title="List Test" - android:summary="" - /> - - <Preference - android:key="preference" - android:title="Preference Title" - android:summary=""/> - - <RingtonePreference - android:key="ringtone" - android:title="Ringtone Test" - android:summary="" - /> - -</PreferenceScreen>
\ No newline at end of file + <PreferenceCategory + android:key="category" + android:title="Category Test"> + + <Preference + android:key="inside_category" + android:title="Inside Category Test" + android:summary=""/> + + </PreferenceCategory> + + <PreferenceScreen + android:key="screen" + android:title="Screen Test"> + + <Preference + android:key="inside_screen" + android:title="Inside Screen Test" + android:summary=""/> + + </PreferenceScreen> + + <CheckBoxPreference + android:key="checkbox" + android:title="Checkbox Test" + android:summary="" + android:defaultValue="true" + /> + + <EditTextPreference + android:key="edit_text" + android:title="EditText Test" + android:summary="" + /> + + <ListPreference + android:key="list" + android:title="List Test" + android:summary="" + /> + + <Preference + android:key="preference" + android:title="Preference Title" + android:summary=""/> + + <RingtonePreference + android:key="ringtone" + android:title="Ringtone Test" + android:summary="" + /> + +</PreferenceScreen>
\ No newline at end of file |