diff options
author | Elliot Sisteron <elliotsisteron@google.com> | 2023-06-08 10:36:53 +0000 |
---|---|---|
committer | Elliot Sisteron <elliotsisteron@google.com> | 2023-06-08 16:13:49 +0000 |
commit | faf20cf1ac1f2a19221ce84a0dc9d337f2dd598a (patch) | |
tree | d01b078f1974bbbb8f9f402c65be26775a4bbbd3 /SafetyCenter | |
parent | 12ee4e41001227322fa0859d6487a6e2b1848e7e (diff) | |
download | Permission-faf20cf1ac1f2a19221ce84a0dc9d337f2dd598a.tar.gz |
Further refactoring of SafetyCenterResourcesContext.
Stop making it implement ContextWrapper, that's error-prone
as it implies it has the Context from the resources APK as a base
when in fact the base is the context passed in; and methods are
overridden only partially.
Also increase test coverage.
Bug: 283100177
Test: atest CtsSafetyCenterTestCases
Relnote: N/A
Change-Id: I2925921de3de01633beacbbe713d46e9522f003b
Diffstat (limited to 'SafetyCenter')
3 files changed, 366 insertions, 178 deletions
diff --git a/SafetyCenter/ResourcesLib/java/com/android/safetycenter/resources/SafetyCenterResourcesContext.java b/SafetyCenter/ResourcesLib/java/com/android/safetycenter/resources/SafetyCenterResourcesContext.java index 215e0f6d7..25455dc7a 100644 --- a/SafetyCenter/ResourcesLib/java/com/android/safetycenter/resources/SafetyCenterResourcesContext.java +++ b/SafetyCenter/ResourcesLib/java/com/android/safetycenter/resources/SafetyCenterResourcesContext.java @@ -19,11 +19,9 @@ package com.android.safetycenter.resources; import static java.util.Objects.requireNonNull; import android.content.Context; -import android.content.ContextWrapper; import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; -import android.content.res.AssetManager; import android.content.res.Resources; import android.graphics.drawable.Drawable; import android.graphics.drawable.Icon; @@ -39,12 +37,17 @@ import java.io.InputStream; import java.util.List; /** - * Wrapper for a base context to expose Safety Center resources that need to be fetched from a - * dedicated APK. + * A class to access Safety Center resources that need to be fetched from a dedicated APK. * - * <p>This class isn't thread safe. Thread safety must be handled by the caller. + * <p>You must check whether Safety Center is enabled or the value returned by {@link #init()} prior + * to interacting with this class. Failure to do so may cause an {@link IllegalStateException} if + * the resources APK cannot be accessed. + * + * <p>This class isn't thread safe. Thread safety must be handled by the caller, or this may cause + * the resources APK {@link Context} to be initialized multiple times. */ -public final class SafetyCenterResourcesContext extends ContextWrapper { +public final class SafetyCenterResourcesContext { + private static final String TAG = "SafetyCenterResContext"; /** Intent action that is used to identify the Safety Center resources APK */ @@ -63,15 +66,14 @@ public final class SafetyCenterResourcesContext extends ContextWrapper { /** Raw XML config resource name */ private static final String CONFIG_NAME = "safety_center_config"; + private final Context mContext; + /** Intent action that is used to identify the Safety Center resources APK */ private final String mResourcesApkAction; /** The path where the Safety Center resources APK is expected to be installed */ private final String mResourcesApkPath; - /** Raw XML config resource name */ - private final String mConfigName; - /** Specific flags used for retrieving resolve info. */ private final int mFlags; @@ -81,36 +83,33 @@ public final class SafetyCenterResourcesContext extends ContextWrapper { */ private final boolean mShouldFallbackIfNamedResourceNotFound; - // Cached context from the resources APK + // Cached context from the resources APK. @Nullable private Context mResourcesApkContext; - public SafetyCenterResourcesContext(Context contextBase) { - this(contextBase, /* shouldFallbackIfNamedResourceNotFound */ true); + public SafetyCenterResourcesContext(Context context) { + this(context, /* shouldFallbackIfNamedResourceNotFound */ true); } private SafetyCenterResourcesContext( - Context contextBase, boolean shouldFallbackIfNamedResourceNotFound) { + Context context, boolean shouldFallbackIfNamedResourceNotFound) { this( - contextBase, + context, RESOURCES_APK_ACTION, APEX_MODULE_PATH, - CONFIG_NAME, PackageManager.MATCH_SYSTEM_ONLY, shouldFallbackIfNamedResourceNotFound); } @VisibleForTesting SafetyCenterResourcesContext( - Context contextBase, + Context context, String resourcesApkAction, String resourcesApkPath, - String configName, int flags, boolean shouldFallbackIfNamedResourceNotFound) { - super(contextBase); + mContext = requireNonNull(context); mResourcesApkAction = requireNonNull(resourcesApkAction); mResourcesApkPath = requireNonNull(resourcesApkPath); - mConfigName = requireNonNull(configName); mFlags = flags; mShouldFallbackIfNamedResourceNotFound = shouldFallbackIfNamedResourceNotFound; } @@ -132,25 +131,33 @@ public final class SafetyCenterResourcesContext extends ContextWrapper { * explicitly. */ public boolean init() { - return getResourcesApkContext() != null; + mResourcesApkContext = loadResourcesApkContext(); + return mResourcesApkContext != null; } - /** Gets the {@link Context} of the Safety Center resources APK. */ - @VisibleForTesting - @Nullable - Context getResourcesApkContext() { + /** + * Returns the {@link Context} of the Safety Center resources APK. + * + * <p>Throws an {@link IllegalStateException} if the resources APK is not available + */ + public Context getResourcesApkContext() { if (mResourcesApkContext != null) { return mResourcesApkContext; } mResourcesApkContext = loadResourcesApkContext(); + if (mResourcesApkContext == null) { + throw new IllegalStateException("Resources APK context not found"); + } + return mResourcesApkContext; } @Nullable private Context loadResourcesApkContext() { List<ResolveInfo> resolveInfos = - getPackageManager().queryIntentActivities(new Intent(mResourcesApkAction), mFlags); + mContext.getPackageManager() + .queryIntentActivities(new Intent(mResourcesApkAction), mFlags); if (resolveInfos.size() > 1) { // multiple apps found, log a warning, but continue @@ -197,183 +204,153 @@ public final class SafetyCenterResourcesContext extends ContextWrapper { @Nullable private Context getPackageContext(String packageName) { try { - return createPackageContext(packageName, 0); + return mContext.createPackageContext(packageName, 0); } catch (PackageManager.NameNotFoundException e) { Log.e(TAG, "Failed to load package context for: " + packageName, e); } return null; } + /** Calls {@link Context#getResources()} for the resources APK {@link Context}. */ + public Resources getResources() { + return getResourcesApkContext().getResources(); + } + /** - * Gets the raw XML resource representing the Safety Center configuration from the Safety Center - * resources APK. + * Returns the raw XML resource representing the Safety Center configuration file from the + * Safety Center resources APK. */ @Nullable public InputStream getSafetyCenterConfig() { - int id = getResId(mConfigName, "raw"); - if (id == Resources.ID_NULL) { - return null; - } - return getResources().openRawResource(id); + return getSafetyCenterConfig(CONFIG_NAME); } - /** - * Returns an optional {@link String} resource from the given {@code stringId}. - * - * <p>Returns {@code null} if {@code stringId} is equal to {@link Resources#ID_NULL}. Otherwise, - * throws a {@link Resources.NotFoundException} if the resource cannot be accessed. - */ + @VisibleForTesting @Nullable - public String getOptionalString(@StringRes int stringId) { - if (stringId == Resources.ID_NULL) { + InputStream getSafetyCenterConfig(String configName) { + int id = getResIdAndMaybeThrowIfNull(configName, "raw"); + if (id == Resources.ID_NULL) { return null; } - return getString(stringId); + return getResources().openRawResource(id); } - /** Same as {@link #getOptionalString(int)} but with the given {@code formatArgs}. */ - @Nullable - public String getOptionalString(@StringRes int stringId, Object... formatArgs) { - if (stringId == Resources.ID_NULL) { - return null; - } - return getString(stringId, formatArgs); + /** Calls {@link Context#getString(int)} for the resources APK {@link Context}. */ + public String getString(@StringRes int stringId) { + return getResourcesApkContext().getString(stringId); } - /** Same as {@link #getOptionalString(int)} but using the string name rather than ID. */ - @Nullable - public String getOptionalStringByName(String name) { - return getOptionalString(getResId(name, "string")); + /** Same as {@link #getString(int)} but with the given {@code formatArgs}. */ + public String getString(@StringRes int stringId, Object... formatArgs) { + return getResourcesApkContext().getString(stringId, formatArgs); } /** - * Gets a string resource by name from the Safety Center resources APK, and returns an empty - * string if the resource does not exist (or throws a {@link Resources.NotFoundException} if - * {@link #mShouldFallbackIfNamedResourceNotFound} is {@code false}). + * Returns the {@link String} with the given resource name. + * + * <p>If the {@link String} cannot be accessed, returns {@code ""} or throws {@link + * Resources.NotFoundException} depending on {@link #mShouldFallbackIfNamedResourceNotFound}. */ public String getStringByName(String name) { - int id = getResId(name, "string"); - return maybeFallbackIfNamedResourceIsNull(name, getOptionalString(id)); + int resId = getResIdAndMaybeThrowIfNull(name, "string"); + if (resId == Resources.ID_NULL) { + return ""; + } + return getString(resId); } /** Same as {@link #getStringByName(String)} but with the given {@code formatArgs}. */ public String getStringByName(String name, Object... formatArgs) { - int id = getResId(name, "string"); - return maybeFallbackIfNamedResourceIsNull(name, getOptionalString(id, formatArgs)); - } - - /** Retrieve assets held in the Safety Center resources APK. */ - @Override - @Nullable - public AssetManager getAssets() { - Context resourcesApkContext = getResourcesApkContext(); - if (resourcesApkContext == null) { - return null; + int resId = getResIdAndMaybeThrowIfNull(name, "string"); + if (resId == Resources.ID_NULL) { + return ""; } - return resourcesApkContext.getAssets(); + return getString(resId, formatArgs); } - /** Retrieve resources held in the Safety Center resources APK. */ - @Override + /** + * Returns an optional {@link String} resource with the given {@code stringId}. + * + * <p>Returns {@code null} if {@code stringId} is equal to {@link Resources#ID_NULL}. Otherwise, + * throws a {@link Resources.NotFoundException}. + */ @Nullable - public Resources getResources() { - Context resourcesApkContext = getResourcesApkContext(); - if (resourcesApkContext == null) { + public String getOptionalString(@StringRes int stringId) { + if (stringId == Resources.ID_NULL) { return null; } - return resourcesApkContext.getResources(); + return getString(stringId); } - /** Retrieve theme held in the Safety Center resources APK. */ - @Override + /** Same as {@link #getOptionalString(int)} but with the given resource name rather than ID. */ @Nullable - public Resources.Theme getTheme() { - Context resourcesApkContext = getResourcesApkContext(); - if (resourcesApkContext == null) { - return null; - } - return resourcesApkContext.getTheme(); + public String getOptionalStringByName(String name) { + return getOptionalString(getResId(name, "string")); } /** - * Gets a drawable resource by name from the Safety Center resources APK. Returns a null - * drawable if the resource does not exist (or throws a {@link Resources.NotFoundException} if - * {@link #mShouldFallbackIfNamedResourceNotFound} is {@code false}). + * Returns the {@link Drawable} with the given resource name. + * + * <p>If the {@link Drawable} cannot be accessed, returns {@code null} or throws {@link + * Resources.NotFoundException} depending on {@link #mShouldFallbackIfNamedResourceNotFound}. * - * @param name the identifier for this drawable resource * @param theme the theme used to style the drawable attributes, may be {@code null} */ @Nullable public Drawable getDrawableByName(String name, @Nullable Resources.Theme theme) { - int resId = getResId(name, "drawable"); - if (resId != Resources.ID_NULL) { - return getResources().getDrawable(resId, theme); - } - - if (!mShouldFallbackIfNamedResourceNotFound) { - throw new Resources.NotFoundException(); + int resId = getResIdAndMaybeThrowIfNull(name, "drawable"); + if (resId == Resources.ID_NULL) { + return null; } - - Log.w(TAG, "Drawable resource " + name + " not found"); - return null; + return getResources().getDrawable(resId, theme); } /** - * Returns an {@link Icon} instance containing a drawable with the given name. If no such - * drawable exists, returns {@code null} or throws {@link Resources.NotFoundException}. + * Returns an {@link Icon} containing the {@link Drawable} with the given resource name. + * + * <p>If the {@link Drawable} cannot be accessed, returns {@code null} or throws {@link + * Resources.NotFoundException} depending on {@link #mShouldFallbackIfNamedResourceNotFound}. */ @Nullable public Icon getIconByDrawableName(String name) { - int resId = getResId(name, "drawable"); - if (resId != Resources.ID_NULL) { - return Icon.createWithResource(getResourcesApkContext().getPackageName(), resId); - } - - if (!mShouldFallbackIfNamedResourceNotFound) { - throw new Resources.NotFoundException(); + int resId = getResIdAndMaybeThrowIfNull(name, "drawable"); + if (resId == Resources.ID_NULL) { + return null; } - - Log.w(TAG, "Drawable resource " + name + " not found"); - return null; + return Icon.createWithResource(getResourcesApkContext().getPackageName(), resId); } - /** Gets a color by resource name */ + /** + * Returns the {@link ColorInt} with the given resource name. + * + * <p>If the {@link ColorInt} cannot be accessed, returns {@code null} or throws {@link + * Resources.NotFoundException} depending on {@link #mShouldFallbackIfNamedResourceNotFound}. + */ @ColorInt @Nullable public Integer getColorByName(String name) { - int resId = getResId(name, "color"); - if (resId != Resources.ID_NULL) { - return getResources().getColor(resId, getTheme()); - } - - if (!mShouldFallbackIfNamedResourceNotFound) { - throw new Resources.NotFoundException(); + int resId = getResIdAndMaybeThrowIfNull(name, "color"); + if (resId == Resources.ID_NULL) { + return null; } - - Log.w(TAG, "Color resource " + name + " not found"); - return null; + return getResources().getColor(resId, getResourcesApkContext().getTheme()); } - private String maybeFallbackIfNamedResourceIsNull(String name, @Nullable String value) { - if (value != null) { - return value; + private int getResIdAndMaybeThrowIfNull(String name, String type) { + int resId = getResId(name, type); + if (resId != Resources.ID_NULL) { + return resId; } if (!mShouldFallbackIfNamedResourceNotFound) { throw new Resources.NotFoundException(); } - Log.w(TAG, "String resource " + name + " not found"); - return ""; + Log.w(TAG, "Named " + type + " resource: " + name + " not found"); + return resId; } private int getResId(String name, String type) { - Context resourcesApkContext = getResourcesApkContext(); - if (resourcesApkContext == null) { - return Resources.ID_NULL; - } // TODO(b/227738283): profile the performance of this operation and consider adding caching // or finding some alternative solution. - return resourcesApkContext - .getResources() - .getIdentifier(name, type, resourcesApkContext.getPackageName()); + return getResources().getIdentifier(name, type, getResourcesApkContext().getPackageName()); } } diff --git a/SafetyCenter/ResourcesLib/tests/SafetyCenterResourcesLibTestResources/res/raw/safety_center_config.txt b/SafetyCenter/ResourcesLib/tests/SafetyCenterResourcesLibTestResources/res/raw/safety_center_config.txt new file mode 100644 index 000000000..3b1246497 --- /dev/null +++ b/SafetyCenter/ResourcesLib/tests/SafetyCenterResourcesLibTestResources/res/raw/safety_center_config.txt @@ -0,0 +1 @@ +TEST
\ No newline at end of file diff --git a/SafetyCenter/ResourcesLib/tests/java/com/android/safetycenter/resources/SafetyCenterResourcesContextTest.kt b/SafetyCenter/ResourcesLib/tests/java/com/android/safetycenter/resources/SafetyCenterResourcesContextTest.kt index c7905ea47..33ff7543e 100644 --- a/SafetyCenter/ResourcesLib/tests/java/com/android/safetycenter/resources/SafetyCenterResourcesContextTest.kt +++ b/SafetyCenter/ResourcesLib/tests/java/com/android/safetycenter/resources/SafetyCenterResourcesContextTest.kt @@ -22,6 +22,7 @@ import android.content.res.Resources import androidx.test.core.app.ApplicationProvider.getApplicationContext import androidx.test.ext.junit.runners.AndroidJUnit4 import com.google.common.truth.Truth.assertThat +import java.lang.IllegalStateException import kotlin.test.assertFailsWith import org.junit.Test import org.junit.runner.RunWith @@ -31,65 +32,172 @@ class SafetyCenterResourcesContextTest { private val context: Context = getApplicationContext() @Test - fun validDataWithValidInputs() { + fun init_withValidInputs_returnsTrue() { val resourcesContext = createNewResourcesContext() - val resourcesApkContext = resourcesContext.resourcesApkContext - assertThat(resourcesApkContext).isNotNull() - assertThat(resourcesApkContext!!.packageName).isEqualTo(RESOURCES_APK_PKG_NAME) + val initialized = resourcesContext.init() - val configContent = - resourcesContext.safetyCenterConfig?.bufferedReader().use { it?.readText() } + assertThat(initialized).isTrue() + } - assertThat(configContent).isEqualTo(CONFIG_CONTENT) - assertThat(resourcesContext.assets).isNotNull() - assertThat(resourcesContext.resources).isNotNull() - assertThat(resourcesContext.theme).isNotNull() + @Test + fun init_withWrongAction_returnsFalse() { + val resourcesContext = createNewResourcesContext(resourcesApkAction = "wrong") + + val initialized = resourcesContext.init() + + assertThat(initialized).isFalse() + } + + @Test + fun init_withWrongPath_returnsFalse() { + val resourcesContext = + createNewResourcesContext(resourcesApkPath = "/apex/com.android.permission") + + val initialized = resourcesContext.init() + + assertThat(initialized).isFalse() + } + + @Test + fun init_withWrongFlags_returnsFalse() { + val resourcesContext = createNewResourcesContext(flags = PackageManager.MATCH_SYSTEM_ONLY) + + val initialized = resourcesContext.init() + + assertThat(initialized).isFalse() } @Test - fun nullDataWithWrongAction() { + fun getResourcesApkContext_withValidInputs_returnsResourcesApkContext() { + val resourcesContext = createNewResourcesContext() + + val resourcesApkContext = resourcesContext.resourcesApkContext + + assertThat(resourcesApkContext.packageName).isEqualTo(RESOURCES_APK_PKG_NAME) + } + + @Test + fun getResourcesApkContext_withWrongAction_throws() { val resourcesContext = createNewResourcesContext(resourcesApkAction = "wrong") - assertThat(resourcesContext.resourcesApkContext).isNull() - assertThat(resourcesContext.safetyCenterConfig).isNull() - assertThat(resourcesContext.assets).isNull() - assertThat(resourcesContext.resources).isNull() - assertThat(resourcesContext.theme).isNull() + assertFailsWith(IllegalStateException::class) { resourcesContext.resourcesApkContext } } @Test - fun nullDataWithWrongPath() { + fun getResourcesApkContext_withWrongPath_throws() { val resourcesContext = createNewResourcesContext(resourcesApkPath = "/apex/com.android.permission") - assertThat(resourcesContext.resourcesApkContext).isNull() - assertThat(resourcesContext.safetyCenterConfig).isNull() - assertThat(resourcesContext.assets).isNull() - assertThat(resourcesContext.resources).isNull() - assertThat(resourcesContext.theme).isNull() + assertFailsWith(IllegalStateException::class) { resourcesContext.resourcesApkContext } } @Test - fun nullDataWithWrongFlag() { + fun getResourcesApkContext_withWrongFlags_throws() { val resourcesContext = createNewResourcesContext(flags = PackageManager.MATCH_SYSTEM_ONLY) - assertThat(resourcesContext.resourcesApkContext).isNull() - assertThat(resourcesContext.safetyCenterConfig).isNull() - assertThat(resourcesContext.assets).isNull() - assertThat(resourcesContext.resources).isNull() - assertThat(resourcesContext.theme).isNull() + assertFailsWith(IllegalStateException::class) { resourcesContext.resourcesApkContext } + } + + @Test + fun getResources_withValidInputs_returnsResourcesApkContextResources() { + val resourcesContext = createNewResourcesContext() + + val resources = resourcesContext.resources + + assertThat(resources).isEqualTo(resourcesContext.resourcesApkContext.resources) + } + + @Test + fun getResources_nullContext_throwsRegardlessOfFallback() { + val resourcesContext = + createNewResourcesContext(resourcesApkAction = "wrong", fallback = true) + + assertFailsWith(IllegalStateException::class) { resourcesContext.resources } + } + + @Test + fun getSafetyCenterConfig_withValidInputs_returnsConfigContent() { + val resourcesContext = createNewResourcesContext() + + val config = resourcesContext.safetyCenterConfig + val configContent = config?.bufferedReader().use { it?.readText() } + + assertThat(config).isNotNull() + assertThat(configContent).isEqualTo(CONFIG_CONTENT) + } + + @Test + fun getSafetyCenterConfig_anotherValidConfigName_returnsConfigContent() { + val resourcesContext = createNewResourcesContext() + + val config = resourcesContext.getSafetyCenterConfig(CONFIG_NAME) + val configContent = config?.bufferedReader().use { it?.readText() } + + assertThat(config).isNotNull() + assertThat(configContent).isEqualTo(CONFIG_CONTENT) + } + + @Test + fun getSafetyCenterConfig_invalidConfigNameWithFallback_returnsNull() { + val resourcesContext = createNewResourcesContext(fallback = true) + + assertThat(resourcesContext.getSafetyCenterConfig("wrong")).isNull() + } + + @Test + fun getSafetyCenterConfig_invalidConfigNameWithoutFallback_throws() { + val resourcesContext = createNewResourcesContext(fallback = false) + + assertFailsWith(Resources.NotFoundException::class) { + resourcesContext.getSafetyCenterConfig("wrong") + } + } + + @Test + fun getSafetyCenterConfig_nullContext_throwsRegardlessOfFallback() { + val resourcesContext = + createNewResourcesContext(resourcesApkAction = "wrong", fallback = true) + + assertFailsWith(IllegalStateException::class) { resourcesContext.safetyCenterConfig } + } + + @Test + fun getString_validString_returnsString() { + val resourcesContext = createNewResourcesContext() + + val ok = resourcesContext.getString(android.R.string.ok) + + assertThat(ok).isEqualTo("OK") + } + + @Test + fun getString_nullContext_throwsRegardlessOfFallback() { + val resourcesContext = + createNewResourcesContext(resourcesApkAction = "wrong", fallback = true) + + assertFailsWith(IllegalStateException::class) { + resourcesContext.getString(android.R.string.ok) + } + } + + @Test + fun getStringWithFormatArgs_validString_returnsString() { + val resourcesContext = createNewResourcesContext() + + val ok = resourcesContext.getString(android.R.string.ok, "") + + assertThat(ok).isEqualTo("OK") } @Test - fun nullConfigWithWrongConfigName() { - val resourcesContext = createNewResourcesContext(configName = "wrong") + fun getStringWithFormatArgs_nullContext_throwsRegardlessOfFallback() { + val resourcesContext = + createNewResourcesContext(resourcesApkAction = "wrong", fallback = true) - assertThat(resourcesContext.resourcesApkContext).isNotNull() - assertThat(resourcesContext.safetyCenterConfig).isNull() - assertThat(resourcesContext.assets).isNotNull() - assertThat(resourcesContext.resources).isNotNull() - assertThat(resourcesContext.theme).isNotNull() + assertFailsWith(IllegalStateException::class) { + resourcesContext.getString(android.R.string.ok, "") + } } @Test @@ -116,6 +224,77 @@ class SafetyCenterResourcesContextTest { } @Test + fun getStringByName_nullContext_throwsRegardlessOfFallback() { + val resourcesContext = + createNewResourcesContext(resourcesApkAction = "wrong", fallback = true) + + assertFailsWith(IllegalStateException::class) { + resourcesContext.getStringByName("valid_string") + } + } + + @Test + fun getStringByNameWithFormatArgs_validString_returnsString() { + val resourcesContext = createNewResourcesContext() + + assertThat(resourcesContext.getStringByName("valid_string", "")).isEqualTo("I exist!") + } + + @Test + fun getStringByNameWithFormatArgs_invalidStringWithFallback_returnsEmptyString() { + val resourcesContext = createNewResourcesContext(fallback = true) + + assertThat(resourcesContext.getStringByName("invalid_string", "")).isEqualTo("") + } + + @Test + fun getStringByNameWithFormatArgs_invalidStringWithoutFallback_throws() { + val resourcesContext = createNewResourcesContext(fallback = false) + + assertFailsWith(Resources.NotFoundException::class) { + resourcesContext.getStringByName("invalid_string", "") + } + } + + @Test + fun getStringByNameWithFormatArgs_nullContext_throwsRegardlessOfFallback() { + val resourcesContext = + createNewResourcesContext(resourcesApkAction = "wrong", fallback = true) + + assertFailsWith(IllegalStateException::class) { + resourcesContext.getStringByName("valid_string", "") + } + } + + @Test + fun getOptionalString_validString_returnsString() { + val resourcesContext = createNewResourcesContext() + + val ok = resourcesContext.getOptionalString(android.R.string.ok) + + assertThat(ok).isEqualTo("OK") + } + + @Test + fun getOptionalString_resourceIdNull_returnsNull() { + val resourcesContext = createNewResourcesContext() + + val string = resourcesContext.getOptionalString(Resources.ID_NULL) + + assertThat(string).isNull() + } + + @Test + fun getOptionalString_nullContext_throwsRegardlessOfFallback() { + val resourcesContext = + createNewResourcesContext(resourcesApkAction = "wrong", fallback = true) + + assertFailsWith(IllegalStateException::class) { + resourcesContext.getOptionalString(android.R.string.ok) + } + } + + @Test fun getOptionalStringByName_validString_returnsString() { val resourcesContext = createNewResourcesContext() @@ -137,6 +316,16 @@ class SafetyCenterResourcesContextTest { } @Test + fun getOptionalStringByName_nullContext_throwsRegardlessOfFallback() { + val resourcesContext = + createNewResourcesContext(resourcesApkAction = "wrong", fallback = true) + + assertFailsWith(IllegalStateException::class) { + resourcesContext.getOptionalStringByName("valid_string") + } + } + + @Test fun getDrawableByName_validDrawable_returnsDrawable() { val resourcesContext = createNewResourcesContext() @@ -160,6 +349,16 @@ class SafetyCenterResourcesContextTest { } @Test + fun getDrawableByName_nullContext_throwsRegardlessOfFallback() { + val resourcesContext = + createNewResourcesContext(resourcesApkAction = "wrong", fallback = true) + + assertFailsWith(IllegalStateException::class) { + resourcesContext.getDrawableByName("valid_drawable", context.theme) + } + } + + @Test fun getIconByDrawableName_validDrawable_returnsIcon() { val resourcesContext = createNewResourcesContext() @@ -183,6 +382,16 @@ class SafetyCenterResourcesContextTest { } @Test + fun getIconByDrawableByName_nullContext_throwsRegardlessOfFallback() { + val resourcesContext = + createNewResourcesContext(resourcesApkAction = "wrong", fallback = true) + + assertFailsWith(IllegalStateException::class) { + resourcesContext.getIconByDrawableName("valid_drawable") + } + } + + @Test fun getColorByName_validColor_returnsColor() { val resourcesContext = createNewResourcesContext() @@ -205,21 +414,22 @@ class SafetyCenterResourcesContextTest { } } + @Test + fun getColorByName_nullContext_throwsRegardlessOfFallback() { + val resourcesContext = + createNewResourcesContext(resourcesApkAction = "wrong", fallback = true) + + assertFailsWith(IllegalStateException::class) { + resourcesContext.getColorByName("valid_color") + } + } + private fun createNewResourcesContext( resourcesApkAction: String = RESOURCES_APK_ACTION, resourcesApkPath: String = "", - configName: String = CONFIG_NAME, flags: Int = 0, fallback: Boolean = false - ) = - SafetyCenterResourcesContext( - context, - resourcesApkAction, - resourcesApkPath, - configName, - flags, - fallback - ) + ) = SafetyCenterResourcesContext(context, resourcesApkAction, resourcesApkPath, flags, fallback) companion object { const val RESOURCES_APK_ACTION = |