From ad8d74fc09dd7b2dc689872e719293cd4f849399 Mon Sep 17 00:00:00 2001 From: "Eric C. Johnson" Date: Tue, 24 Feb 2026 18:03:54 -0700 Subject: [PATCH 01/14] @W-21148602: Internal Server List does not respect changes made to Servers.xml file --- .../androidsdk/config/LoginServerManager.java | 80 ++++++++++++++----- 1 file changed, 61 insertions(+), 19 deletions(-) diff --git a/libs/SalesforceSDK/src/com/salesforce/androidsdk/config/LoginServerManager.java b/libs/SalesforceSDK/src/com/salesforce/androidsdk/config/LoginServerManager.java index bc38f9f71d..3651ee2730 100644 --- a/libs/SalesforceSDK/src/com/salesforce/androidsdk/config/LoginServerManager.java +++ b/libs/SalesforceSDK/src/com/salesforce/androidsdk/config/LoginServerManager.java @@ -50,7 +50,6 @@ import java.util.Deque; import java.util.List; import java.util.Locale; -import java.util.Map; /** * Class to manage login hosts (default and user entered). @@ -92,7 +91,14 @@ public LoginServerManager(Context ctx) { Context.MODE_PRIVATE); runtimePrefs = ctx.getSharedPreferences(RUNTIME_PREFS_FILE, Context.MODE_PRIVATE); + + // Reset non-custom servers from mobile device management (MDM) and servers XML. + resetNonCustomLoginServers(runtimePrefs); + resetNonCustomLoginServers(settings); + + // Refresh non-custom servers from MDM or servers.xml initSharedPrefFile(); + getSelectedLoginServer(); } @@ -228,32 +234,48 @@ public void reset() { } /** - * Removes a login server from the list. + * Removes a custom login server from the list. * - * @param server the server to remove + * @param server The server to remove. If the server is not custom, this method does nothing */ public void removeServer(LoginServer server) { + removeServer(server, settings, false); + } + + /** + * Removes a login server from the list. + * + * @param server The server to remove + * @param sharedPreferences The shared preferences to remove the server from + * @param allowNonCustomRemoval Boolean true allows the removal of non-custom login servers and + * false does not + */ + private void removeServer( + final LoginServer server, + final SharedPreferences sharedPreferences, + final boolean allowNonCustomRemoval + ) { List servers = getLoginServers(); int index = servers.indexOf(server); - if (server.isCustom && index != -1) { - int numServers = settings.getInt(NUMBER_OF_ENTRIES, 0); - Deque stack = new ArrayDeque<>(servers.subList(index+1, numServers)); + if (allowNonCustomRemoval || server.isCustom && index != -1) { + int numServers = servers.size(); + Deque stack = new ArrayDeque<>(servers.subList(index + 1, numServers)); - final Editor edit = settings.edit(); + final Editor edit = sharedPreferences.edit(); edit.remove(String.format(Locale.US, SERVER_NAME, index)) - .remove(String.format(Locale.US, SERVER_URL, index)) - .remove(String.format(Locale.US, IS_CUSTOM, index)); + .remove(String.format(Locale.US, SERVER_URL, index)) + .remove(String.format(Locale.US, IS_CUSTOM, index)); // Re-index servers after the one removed from the list. for (int i = (index + 1); i < numServers; i++) { LoginServer reIndexServer = stack.pop(); edit.remove(String.format(Locale.US, SERVER_NAME, i)) - .remove(String.format(Locale.US, SERVER_URL, i)) - .remove(String.format(Locale.US, IS_CUSTOM, i)) - .putString(String.format(Locale.US, SERVER_NAME, i-1), reIndexServer.name) - .putString(String.format(Locale.US, SERVER_URL, i-1), reIndexServer.url) - .putBoolean(String.format(Locale.US, IS_CUSTOM, i-1), reIndexServer.isCustom); + .remove(String.format(Locale.US, SERVER_URL, i)) + .remove(String.format(Locale.US, IS_CUSTOM, i)) + .putString(String.format(Locale.US, SERVER_NAME, i - 1), reIndexServer.name) + .putString(String.format(Locale.US, SERVER_URL, i - 1), reIndexServer.url) + .putBoolean(String.format(Locale.US, IS_CUSTOM, i - 1), reIndexServer.isCustom); } edit.putInt(NUMBER_OF_ENTRIES, --numServers).apply(); @@ -467,16 +489,17 @@ private List getLoginServersFromXML() { * first time a user is upgrading to a newer version of the Mobile SDK. */ private void initSharedPrefFile() { - final Map values = settings.getAll(); - if (values != null && !values.isEmpty()) { - return; - } + final List loginServersFromXml = getLoginServersFromXML(); + List servers = getLoginServers(); if (servers == null || servers.isEmpty()) { - servers = getLoginServersFromXML(); + servers = loginServersFromXml; if (servers == null || servers.isEmpty()) { servers = getLegacyLoginServers(); } + } else { + loginServersFromXml.addAll(servers); + servers = loginServersFromXml; } int numServers = servers.size(); final Editor edit = settings.edit(); @@ -538,6 +561,25 @@ private List getLoginServersFromPreferences(SharedPreferences prefs return (!allServers.isEmpty() ? allServers : null); } + /** + * Resets the list of non-custom login servers in the provided shared preferences. + * + * @param sharedPreferences The shared preferences + */ + private void resetNonCustomLoginServers( + final SharedPreferences sharedPreferences + ) { + final List loginServersFromPreferences = getLoginServersFromPreferences(sharedPreferences); + if (loginServersFromPreferences != null) { + for (int i = 0; i < loginServersFromPreferences.size(); i++) { + final LoginServer loginServer = loginServersFromPreferences.get(i); + if (!loginServer.isCustom) { + removeServer(loginServer, sharedPreferences, true); + } + } + } + } + /** * Class to encapsulate a login server name, URL, index and type (custom or not). */ From 84a644105dfacbbef2c67f0e3f849c440aed7fb8 Mon Sep 17 00:00:00 2001 From: "Eric C. Johnson" Date: Fri, 27 Feb 2026 19:42:14 -0700 Subject: [PATCH 02/14] @W-21148602: Internal Server List does not respect changes made to Servers.xml file (Greatly Improved Shared Preferences Data Integrity And Dynamic Handling Of RuntimeConfig. Removal Of To-Dos And Diagnostics Pending) --- .../androidsdk/config/LoginServerManager.java | 387 ++++++++++++------ 1 file changed, 267 insertions(+), 120 deletions(-) diff --git a/libs/SalesforceSDK/src/com/salesforce/androidsdk/config/LoginServerManager.java b/libs/SalesforceSDK/src/com/salesforce/androidsdk/config/LoginServerManager.java index 3651ee2730..5e62939872 100644 --- a/libs/SalesforceSDK/src/com/salesforce/androidsdk/config/LoginServerManager.java +++ b/libs/SalesforceSDK/src/com/salesforce/androidsdk/config/LoginServerManager.java @@ -26,6 +26,10 @@ */ package com.salesforce.androidsdk.config; +import static android.content.Context.MODE_PRIVATE; +import static com.salesforce.androidsdk.config.RuntimeConfig.ConfigKey.AppServiceHostLabels; +import static com.salesforce.androidsdk.config.RuntimeConfig.ConfigKey.AppServiceHosts; +import static com.salesforce.androidsdk.config.RuntimeConfig.getRuntimeConfig; import static java.lang.String.format; import static java.util.Locale.US; @@ -34,12 +38,12 @@ import android.content.SharedPreferences.Editor; import android.content.res.XmlResourceParser; import android.os.Looper; +import android.util.Log; import androidx.annotation.NonNull; import androidx.lifecycle.MutableLiveData; import com.salesforce.androidsdk.R; -import com.salesforce.androidsdk.config.RuntimeConfig.ConfigKey; import com.salesforce.androidsdk.util.SalesforceSDKLogger; import org.xmlpull.v1.XmlPullParserException; @@ -47,9 +51,10 @@ import java.io.IOException; import java.util.ArrayDeque; import java.util.ArrayList; +import java.util.Collections; import java.util.Deque; import java.util.List; -import java.util.Locale; +import java.util.Map; /** * Class to manage login hosts (default and user entered). @@ -88,18 +93,29 @@ public class LoginServerManager { public LoginServerManager(Context ctx) { this.ctx = ctx; settings = ctx.getSharedPreferences(SERVER_URL_FILE, - Context.MODE_PRIVATE); + MODE_PRIVATE); runtimePrefs = ctx.getSharedPreferences(RUNTIME_PREFS_FILE, - Context.MODE_PRIVATE); + MODE_PRIVATE); - // Reset non-custom servers from mobile device management (MDM) and servers XML. - resetNonCustomLoginServers(runtimePrefs); + // TODO: Remove Diagnostic. ECJ20260227 + Log.i("LSM", "INITIAL SETTINGS SHARED PREFERENCES."); + logSharedPreferences(settings); + + // Reset non-custom servers from resources login servers provided by servers.xml. resetNonCustomLoginServers(settings); - // Refresh non-custom servers from MDM or servers.xml + // TODO: Remove Diagnostic. ECJ20260227 + Log.i("LSM", "AFTER RESET MANAGED SERVERS FROM SETTINGS SHARED PREFERENCES."); + logSharedPreferences(settings); + initSharedPrefFile(); + // Select a default login server. getSelectedLoginServer(); + + // TODO: Remove Diagnostic. ECJ20260227 + Log.i("LSM", "UPDATED SETTINGS SHARED PREFERENCES."); + logSharedPreferences(settings); } /** @@ -124,20 +140,29 @@ public LoginServer getLoginServerFromURL(String url) { } /** - * Returns the selected login server to display. + * Returns the selected login server. This will set a default login server if needed and ensure + * the selected login server is available in the current list of login servers. * - * @return LoginServer instance. + * @return The selected login server */ public LoginServer getSelectedLoginServer() { final SharedPreferences selectedServerPrefs = ctx.getSharedPreferences(SERVER_SELECTION_FILE, - Context.MODE_PRIVATE); + MODE_PRIVATE); final String name = selectedServerPrefs.getString(SERVER_NAME, null); final String url = selectedServerPrefs.getString(SERVER_URL, null); boolean isCustom = selectedServerPrefs.getBoolean(IS_CUSTOM, false); - // Selection has been saved before. - if (name != null && url != null) { - LoginServer server = new LoginServer(name, url, isCustom); + // Refresh the list of mobile device management (MDM) servers from the runtime config. + if (isRuntimeConfigAppServiceHostsSet()) { + resetLoginServersFromRuntimeConfig(); + } + + // Get the active list of login servers. + final List loginServers = getLoginServers(); + + // Selection has been saved before and is available in the active list of login servers. + if (name != null && url != null && loginServers.stream().anyMatch(server -> server.name.equals(name) && server.url.equals(url))) { + final LoginServer server = new LoginServer(name, url, isCustom); // Only notify live data consumers if the value has changed. if (!server.equals(selectedServer.getValue())) { @@ -146,9 +171,8 @@ public LoginServer getSelectedLoginServer() { } else { // First time selection defaults to the first server on the list. - final List allServers = getLoginServers(); - if (allServers != null) { - final LoginServer server = allServers.get(0); + if (loginServers != null) { + final LoginServer server = loginServers.get(0); if (server != null) { selectedServer.postValue(server); } @@ -170,7 +194,7 @@ public void setSelectedLoginServer(LoginServer server) { return; } final SharedPreferences selectedServerPrefs = ctx.getSharedPreferences(SERVER_SELECTION_FILE, - Context.MODE_PRIVATE); + MODE_PRIVATE); final Editor edit = selectedServerPrefs.edit(); edit.clear(); edit.putString(SERVER_NAME, server.name); @@ -207,11 +231,12 @@ public void addCustomLoginServer(String name, String url) { } } - if (getLoginServersFromRuntimeConfig() == null) { - persistLoginServer(name, url, true, settings); - } else { - persistLoginServer(name, url, true, runtimePrefs); - } + persistLoginServer( + name, + url, + true /* Custom */, + getSharedPreferences() /* Active Shared Preferences */ + ); setSelectedLoginServer(new LoginServer(name, url, true)); } @@ -226,7 +251,7 @@ public void reset() { edit.clear(); edit.apply(); final SharedPreferences selectedServerPrefs = ctx.getSharedPreferences(SERVER_SELECTION_FILE, - Context.MODE_PRIVATE); + MODE_PRIVATE); edit = selectedServerPrefs.edit(); edit.clear(); edit.apply(); @@ -238,7 +263,7 @@ public void reset() { * * @param server The server to remove. If the server is not custom, this method does nothing */ - public void removeServer(LoginServer server) { + public void removeServer(final LoginServer server) { removeServer(server, settings, false); } @@ -255,7 +280,8 @@ private void removeServer( final SharedPreferences sharedPreferences, final boolean allowNonCustomRemoval ) { - List servers = getLoginServers(); + final List servers = getLoginServersFromPreferences(sharedPreferences); + int index = servers.indexOf(server); if (allowNonCustomRemoval || server.isCustom && index != -1) { @@ -263,19 +289,19 @@ private void removeServer( Deque stack = new ArrayDeque<>(servers.subList(index + 1, numServers)); final Editor edit = sharedPreferences.edit(); - edit.remove(String.format(Locale.US, SERVER_NAME, index)) - .remove(String.format(Locale.US, SERVER_URL, index)) - .remove(String.format(Locale.US, IS_CUSTOM, index)); + edit.remove(format(US, SERVER_NAME, index)) + .remove(format(US, SERVER_URL, index)) + .remove(format(US, IS_CUSTOM, index)); // Re-index servers after the one removed from the list. for (int i = (index + 1); i < numServers; i++) { LoginServer reIndexServer = stack.pop(); - edit.remove(String.format(Locale.US, SERVER_NAME, i)) - .remove(String.format(Locale.US, SERVER_URL, i)) - .remove(String.format(Locale.US, IS_CUSTOM, i)) - .putString(String.format(Locale.US, SERVER_NAME, i - 1), reIndexServer.name) - .putString(String.format(Locale.US, SERVER_URL, i - 1), reIndexServer.url) - .putBoolean(String.format(Locale.US, IS_CUSTOM, i - 1), reIndexServer.isCustom); + edit.remove(format(US, SERVER_NAME, i)) + .remove(format(US, SERVER_URL, i)) + .remove(format(US, IS_CUSTOM, i)) + .putString(format(US, SERVER_NAME, i - 1), reIndexServer.name) + .putString(format(US, SERVER_URL, i - 1), reIndexServer.url) + .putBoolean(format(US, IS_CUSTOM, i - 1), reIndexServer.isCustom); } edit.putInt(NUMBER_OF_ENTRIES, --numServers).apply(); @@ -283,41 +309,60 @@ private void removeServer( } /** - * Returns the list of login servers. - * Checks run time configuration first. - * Reads from preferences if no runtime configuration found. + * Returns the list of login servers. Defaults to mobile device management (MDM) login servers + * from the runtime configuration, if available, or resources login servers from the + * servers.xml. MDM and resources login servers are not custom and cannot be removed by the + * user. A separate list of custom login servers is stored in a shared preferences for each of + * MDM and resources. * - * @return List of login servers. + * @return The list of login servers */ public List getLoginServers() { - List allServers = getLoginServersFromRuntimeConfig(); - if (allServers == null) { - allServers = getLoginServersFromPreferences(); - } else { - allServers = getLoginServersFromPreferences(runtimePrefs); - } - return allServers; + return getLoginServersFromPreferences(getSharedPreferences()); } /** - * Returns the list of login servers from runtime configuration - * (from MDM provider), if any. + * Returns the active shared preferences when using mobile device management (MDM) or otherwise. * - * @return List of login servers or null. + * @return SharedPreferences The active shared preferences + */ + private SharedPreferences getSharedPreferences() { + return isRuntimeConfigAppServiceHostsSet() ? runtimePrefs : settings; + } + + /** + * Determines if managed (non-custom) login servers are provided by mobile device management + * (MDM). + * + * @return boolean True indicates managed login servers are provided by MDM and false otherwise + */ + private boolean isRuntimeConfigAppServiceHostsSet() { + try { + return true; // TODO: Remove MDM testing diagnostic. ECJ20260227 +// return getRuntimeConfig(ctx).getStringArrayStoredAsArrayOrCSV(AppServiceHosts) != null; + } catch (Exception e) { + return false; + } + } + + /** + * Resets the list of Mobile Device Management (MDM) login servers from the runtime + * configuration. This does not remove the user's custom login servers. */ - public List getLoginServersFromRuntimeConfig() { - final RuntimeConfig runtimeConfig = RuntimeConfig.getRuntimeConfig(ctx); + private void resetLoginServersFromRuntimeConfig() { + final RuntimeConfig runtimeConfig = getRuntimeConfig(ctx); String[] mdmLoginServers = null; try { - mdmLoginServers = runtimeConfig.getStringArrayStoredAsArrayOrCSV(ConfigKey.AppServiceHosts); + mdmLoginServers = new String[]{"https://mdm.example.com", "https://mdm2.example.com/2"}; // TODO: Remove MDM testing diagnostic. ECJ20260227 +// mdmLoginServers = runtimeConfig.getStringArrayStoredAsArrayOrCSV(AppServiceHosts); } catch (Exception e) { SalesforceSDKLogger.w(TAG, "Exception thrown while attempting to read array, attempting to read string value instead", e); } - final List allServers = new ArrayList<>(); if (mdmLoginServers != null) { String[] mdmLoginServersLabels = null; try { - mdmLoginServersLabels = runtimeConfig.getStringArrayStoredAsArrayOrCSV(ConfigKey.AppServiceHostLabels); + mdmLoginServersLabels = new String[]{"MDM", "MDM2.1"}; // TODO: Remove MDM testing diagnostic. ECJ20260227 +// mdmLoginServersLabels = runtimeConfig.getStringArrayStoredAsArrayOrCSV(AppServiceHostLabels); } catch (Exception e) { SalesforceSDKLogger.w(TAG, "Exception thrown while attempting to read array, attempting to read string value instead", e); } @@ -325,25 +370,40 @@ public List getLoginServersFromRuntimeConfig() { SalesforceSDKLogger.w(TAG, "No login servers labels provided or wrong number of login servers labels provided - using URLs for the labels"); mdmLoginServersLabels = mdmLoginServers; } - final List storedServers = getLoginServersFromPreferences(runtimePrefs); + + // TODO: Remove Diagnostic. ECJ20260227 + Log.i("LSM", "INITIAL RUNTIME SHARED PREFERENCES."); + logSharedPreferences(runtimePrefs); + + // Reset non-custom servers from Mobile Device Management (MDM). + resetNonCustomLoginServers(runtimePrefs); + + // TODO: Remove Diagnostic. ECJ20260227 + Log.i("LSM", "AFTER RESET MANAGED SERVERS FROM RUNTIME SHARED PREFERENCES."); + logSharedPreferences(runtimePrefs); + for (int i = 0; i < mdmLoginServers.length; i++) { final String name = mdmLoginServersLabels[i]; final String url = mdmLoginServers[i]; - final LoginServer server = new LoginServer(name, url, false); - if (storedServers == null || !storedServers.contains(server)) { - persistLoginServer(name, url, false, runtimePrefs); - } - allServers.add(server); + persistLoginServer( + name, + url, + false, /* Non-Custom */ + runtimePrefs + ); } } - return (!allServers.isEmpty() ? allServers : null); } /** - * Returns the list of all saved servers, including custom servers. + * Returns the list of login servers from resources (servers.xml) and the user's custom servers. + * Note this does not consider login servers the runtime config provides from Mobile Device + * Management (MDM). Consider using getLoginServers() instead if you need to consider MDM + * login servers. * - * @return List of all saved servers. + * @return The list of login servers from resources (servers.xml) and the user's custom servers */ + @SuppressWarnings("unused") public List getLoginServersFromPreferences() { return getLoginServersFromPreferences(settings); } @@ -371,28 +431,16 @@ public void reorderCustomLoginServer( return; } - // Determine the last non-custom login server index. - final List servers = getLoginServers(); - int firstCustomLoginServerIndex = -1; - for (int i = servers.size() - 1; i >= 0; i--) { - if (servers.get(i).isCustom) { - firstCustomLoginServerIndex = i; - } - } - // Adjust the re-ordered custom login server index to be within bounds. - if (updatedIndex <= firstCustomLoginServerIndex) { - updatedIndex = firstCustomLoginServerIndex; - } else if (updatedIndex >= servers.size()) { - updatedIndex = servers.size() - 1; - } + final List servers = getLoginServers(); + updatedIndex = getIndexAdjustedToCustomLoginServerBounds(updatedIndex, getSharedPreferences()); // Update the login server list. loginServers.remove(originalIndex); loginServers.add(updatedIndex, originalLoginServer); // Edit each login server indexed after the updated index. - final Editor editor = settings.edit(); + final Editor editor = getSharedPreferences().edit(); for (int i = updatedIndex; i < loginServers.size(); i++) { final LoginServer loginServer = loginServers.get(i); editor.remove(format(US, SERVER_NAME, i)) @@ -436,6 +484,32 @@ public void replaceCustomLoginServer( reorderCustomLoginServer(getLoginServers().size() - 1, originalIndex); } + /** + * Adjusts a login server index to be within the bounds of the custom login servers. + * + * @param index The login server index + * @param sharedPreferences The login server shared preferences + * @return The adjusted login server index + */ + private int getIndexAdjustedToCustomLoginServerBounds( + final Integer index, + final SharedPreferences sharedPreferences + ) { + // Determine the last non-custom login server index. + int firstCustomLoginServerIndex = getNextNonCustomLoginServerIndex(sharedPreferences); + + final List servers = getLoginServers(); + if (index == null) { + return servers.size(); + } else if (index <= firstCustomLoginServerIndex) { + return firstCustomLoginServerIndex; + } else if (index >= servers.size()) { + return servers.size() - 1; + } else { + return index; + } + } + /** * Returns production and sandbox as the login servers * (only called when servers.xml is missing). @@ -484,57 +558,121 @@ private List getLoginServersFromXML() { } /** - * Initializes the shared pref file with all available servers for - * the first time, if necessary. This is required primarily for the - * first time a user is upgrading to a newer version of the Mobile SDK. + * Returns the next available non-custom login server index which suffixes the non-custom login + * servers and prefixes the custom login servers. + * + * @param sharedPreferences The login server shared preferences + * @return The next available non-custom login server index */ - private void initSharedPrefFile() { - final List loginServersFromXml = getLoginServersFromXML(); + private Integer getNextNonCustomLoginServerIndex(final SharedPreferences sharedPreferences) { + final List servers = getLoginServersFromPreferences(sharedPreferences); + int result = servers.size(); + for (int i = result - 1; i >= 0; i--) { + if (servers.get(i).isCustom) { + result = i; + } + } + return result; + } - List servers = getLoginServers(); + /** + * Resets the list of resources login servers from the server.xml. This does not remove the + * user's custom login servers. + */ + private void initSharedPrefFile() { + if (isRuntimeConfigAppServiceHostsSet()) { + return; + } + List servers = getLoginServersFromXML(); if (servers == null || servers.isEmpty()) { - servers = loginServersFromXml; - if (servers == null || servers.isEmpty()) { - servers = getLegacyLoginServers(); - } - } else { - loginServersFromXml.addAll(servers); - servers = loginServersFromXml; + servers = getLegacyLoginServers(); } int numServers = servers.size(); final Editor edit = settings.edit(); for (int i = 0; i < numServers; i++) { final LoginServer curServer = servers.get(i); - edit.putString(String.format(Locale.US, SERVER_NAME, i), curServer.name.trim()); - edit.putString(String.format(Locale.US, SERVER_URL, i), curServer.url.trim()); - edit.putBoolean(String.format(Locale.US, IS_CUSTOM, i), curServer.isCustom); - if (i == 0) { + persistLoginServer( + curServer.name, + curServer.url, + curServer.isCustom, + settings + ); + + // Set the default login server to the first entry once. + if (i == 0 && ctx.getSharedPreferences(SERVER_SELECTION_FILE, MODE_PRIVATE).getAll().isEmpty()) { setSelectedLoginServer(curServer); } } - edit.putInt(NUMBER_OF_ENTRIES, numServers); edit.apply(); } /** - * Adds a custom login server to the specified shared pref file. + * Persists a login server the specified shared preferences file. * - * @param name Server name. - * @param url Server URL. - * @param isCustom True - if it is a custom server, False - otherwise. - * @param sharedPrefs SharedPreferences file. + * @param name The login server name + * @param url The login server URL + * @param isCustom boolean true for non-custom (managed) login servers, false for + * custom (user-entered) login servers + * @param sharedPreferences SharedPreferences file */ - private void persistLoginServer(String name, String url, boolean isCustom, SharedPreferences sharedPrefs) { + private void persistLoginServer(final String name, + final String url, + final boolean isCustom, + final SharedPreferences sharedPreferences + ) { + // Guards. if (name == null || url == null) { return; } - int numServers = sharedPrefs.getInt(NUMBER_OF_ENTRIES, 0); - final Editor edit = sharedPrefs.edit(); - edit.putString(String.format(Locale.US, SERVER_NAME, numServers), name.trim()); - edit.putString(String.format(Locale.US, SERVER_URL, numServers), url.trim()); - edit.putBoolean(String.format(Locale.US, IS_CUSTOM, numServers), isCustom); - edit.putInt(NUMBER_OF_ENTRIES, ++numServers); - edit.apply(); + + // Fetch the current number of servers. + final int numberOfServers = sharedPreferences.getInt(NUMBER_OF_ENTRIES, 0); + + // Adjust the requested index to the bounds of the non-custom (managed) or custom servers. + Integer adjustedIndex; + if (isCustom) { + adjustedIndex = getIndexAdjustedToCustomLoginServerBounds(null, sharedPreferences); + } else { + adjustedIndex = getNextNonCustomLoginServerIndex(sharedPreferences); + } + + Log.i("LSM", "Persisting Login Server: '" + name + "', URL: '" + url + "', isCustom: '" + isCustom + "', Index: '" + null + "', adjustedIndex: '" + adjustedIndex + "'."); + + final Editor editor = sharedPreferences.edit(); + + // Increment existing login servers as needed. + if (adjustedIndex != null) { + for (int i = numberOfServers - 1; i >= adjustedIndex; i--) { + final int incrementedIndex = i + 1; + final String loginServerNameKey = format(US, SERVER_NAME, i); + final String loginServerUrlKey = format(US, SERVER_URL, i); + final String loginServerIsCustomKey = format(US, IS_CUSTOM, i); + + final String loginServerName = sharedPreferences.getString(loginServerNameKey, null); + final String loginServerUrl = sharedPreferences.getString(loginServerUrlKey, null); + final boolean loginServerIsCustom = sharedPreferences.getBoolean(loginServerIsCustomKey, false); + + Log.i("LSM", "Incrementing Login Server: '" + loginServerName + "', URL: '" + loginServerUrl + "', isCustom: '" + loginServerIsCustom + "', Index: '" + i + "'/'" + incrementedIndex + "'."); + + editor + .remove(loginServerNameKey) + .remove(loginServerUrlKey) + .remove(loginServerIsCustomKey) + .putString(format(US, SERVER_NAME, incrementedIndex), loginServerName) + .putString(format(US, SERVER_URL, incrementedIndex), loginServerUrl) + .putBoolean(format(US, IS_CUSTOM, incrementedIndex), loginServerIsCustom); + } + } + + // Insert the new login server. + Log.i("LSM", "Inserting Login Server: '" + name + "', URL: '" + url + "', isCustom: '" + isCustom + "', Index: '" + adjustedIndex + "'."); + editor.putString(format(US, SERVER_NAME, adjustedIndex), name.trim()); + editor.putString(format(US, SERVER_URL, adjustedIndex), url.trim()); + editor.putBoolean(format(US, IS_CUSTOM, adjustedIndex), isCustom); + + editor.putInt(NUMBER_OF_ENTRIES, numberOfServers + 1); + + editor.apply(); } /** @@ -543,22 +681,33 @@ private void persistLoginServer(String name, String url, boolean isCustom, Share * @param prefs SharedPreferences file. * @return List of all saved servers. */ - private List getLoginServersFromPreferences(SharedPreferences prefs) { + private @NonNull List getLoginServersFromPreferences(final SharedPreferences prefs) { int numServers = prefs.getInt(NUMBER_OF_ENTRIES, 0); if (numServers == 0) { - return null; + return new ArrayList<>(); } final List allServers = new ArrayList<>(); for (int i = 0; i < numServers; i++) { - final String name = prefs.getString(String.format(Locale.US, SERVER_NAME, i), null); - final String url = prefs.getString(String.format(Locale.US, SERVER_URL, i), null); - boolean isCustom = prefs.getBoolean(String.format(Locale.US, IS_CUSTOM, i), false); + final String name = prefs.getString(format(US, SERVER_NAME, i), null); + final String url = prefs.getString(format(US, SERVER_URL, i), null); + boolean isCustom = prefs.getBoolean(format(US, IS_CUSTOM, i), false); if (name != null && url != null) { final LoginServer server = new LoginServer(name, url.trim(), isCustom); allServers.add(server); } } - return (!allServers.isEmpty() ? allServers : null); + return !allServers.isEmpty() ? allServers : new ArrayList<>(); + } + + // TODO: Remove Diagnostic. ECJ20260227 + private void logSharedPreferences(final SharedPreferences sharedPreferences) { + final Map preferences = sharedPreferences.getAll(); + final ArrayList keys = new ArrayList<>(preferences.keySet()); + Collections.sort(keys); + for (int i = 0; i < preferences.size(); i++) { + final Object key = keys.toArray()[i]; + Log.i("LSM", "Settings: '" + key + "'/'" + preferences.get(key) + "'."); + } } /** @@ -570,12 +719,10 @@ private void resetNonCustomLoginServers( final SharedPreferences sharedPreferences ) { final List loginServersFromPreferences = getLoginServersFromPreferences(sharedPreferences); - if (loginServersFromPreferences != null) { - for (int i = 0; i < loginServersFromPreferences.size(); i++) { - final LoginServer loginServer = loginServersFromPreferences.get(i); - if (!loginServer.isCustom) { - removeServer(loginServer, sharedPreferences, true); - } + for (int i = 0; i < loginServersFromPreferences.size(); i++) { + final LoginServer loginServer = loginServersFromPreferences.get(i); + if (!loginServer.isCustom) { + removeServer(loginServer, sharedPreferences, true); } } } From 2d06029f44f7960354c87f468ad135eae896d744 Mon Sep 17 00:00:00 2001 From: "Eric C. Johnson" Date: Mon, 2 Mar 2026 16:39:13 -0700 Subject: [PATCH 03/14] @W-21148602: Internal Server List does not respect changes made to Servers.xml file (Remove Diagnostics And To-Dos) --- .../androidsdk/config/LoginServerManager.java | 80 +++++-------------- 1 file changed, 20 insertions(+), 60 deletions(-) diff --git a/libs/SalesforceSDK/src/com/salesforce/androidsdk/config/LoginServerManager.java b/libs/SalesforceSDK/src/com/salesforce/androidsdk/config/LoginServerManager.java index 5e62939872..e82bf5fa17 100644 --- a/libs/SalesforceSDK/src/com/salesforce/androidsdk/config/LoginServerManager.java +++ b/libs/SalesforceSDK/src/com/salesforce/androidsdk/config/LoginServerManager.java @@ -27,6 +27,7 @@ package com.salesforce.androidsdk.config; import static android.content.Context.MODE_PRIVATE; +import static com.salesforce.androidsdk.R.xml.servers; import static com.salesforce.androidsdk.config.RuntimeConfig.ConfigKey.AppServiceHostLabels; import static com.salesforce.androidsdk.config.RuntimeConfig.ConfigKey.AppServiceHosts; import static com.salesforce.androidsdk.config.RuntimeConfig.getRuntimeConfig; @@ -38,7 +39,6 @@ import android.content.SharedPreferences.Editor; import android.content.res.XmlResourceParser; import android.os.Looper; -import android.util.Log; import androidx.annotation.NonNull; import androidx.lifecycle.MutableLiveData; @@ -51,10 +51,8 @@ import java.io.IOException; import java.util.ArrayDeque; import java.util.ArrayList; -import java.util.Collections; import java.util.Deque; import java.util.List; -import java.util.Map; /** * Class to manage login hosts (default and user entered). @@ -82,7 +80,15 @@ public class LoginServerManager { private static final String SERVER_SELECTION_FILE = "server_selection_file"; private final Context ctx; + + /** + * Shared preferences when non-custom resources login servers are provided by servers.xml and associated custom login servers added by the user + */ private final SharedPreferences settings; + + /** + * Shared preferences when non-custom resources login servers are provided by the runtime configuration (Mobile Device Management) and associated custom login servers added by the user + */ private final SharedPreferences runtimePrefs; /** @@ -92,30 +98,15 @@ public class LoginServerManager { */ public LoginServerManager(Context ctx) { this.ctx = ctx; - settings = ctx.getSharedPreferences(SERVER_URL_FILE, - MODE_PRIVATE); - runtimePrefs = ctx.getSharedPreferences(RUNTIME_PREFS_FILE, - MODE_PRIVATE); - - // TODO: Remove Diagnostic. ECJ20260227 - Log.i("LSM", "INITIAL SETTINGS SHARED PREFERENCES."); - logSharedPreferences(settings); + settings = ctx.getSharedPreferences(SERVER_URL_FILE, MODE_PRIVATE); + runtimePrefs = ctx.getSharedPreferences(RUNTIME_PREFS_FILE, MODE_PRIVATE); - // Reset non-custom servers from resources login servers provided by servers.xml. + // (Re-)initialize non-custom servers provided by the servers.xml. resetNonCustomLoginServers(settings); - - // TODO: Remove Diagnostic. ECJ20260227 - Log.i("LSM", "AFTER RESET MANAGED SERVERS FROM SETTINGS SHARED PREFERENCES."); - logSharedPreferences(settings); - initSharedPrefFile(); // Select a default login server. getSelectedLoginServer(); - - // TODO: Remove Diagnostic. ECJ20260227 - Log.i("LSM", "UPDATED SETTINGS SHARED PREFERENCES."); - logSharedPreferences(settings); } /** @@ -146,8 +137,7 @@ public LoginServer getLoginServerFromURL(String url) { * @return The selected login server */ public LoginServer getSelectedLoginServer() { - final SharedPreferences selectedServerPrefs = ctx.getSharedPreferences(SERVER_SELECTION_FILE, - MODE_PRIVATE); + final SharedPreferences selectedServerPrefs = ctx.getSharedPreferences(SERVER_SELECTION_FILE, MODE_PRIVATE); final String name = selectedServerPrefs.getString(SERVER_NAME, null); final String url = selectedServerPrefs.getString(SERVER_URL, null); boolean isCustom = selectedServerPrefs.getBoolean(IS_CUSTOM, false); @@ -193,8 +183,7 @@ public void setSelectedLoginServer(LoginServer server) { if (server == null) { return; } - final SharedPreferences selectedServerPrefs = ctx.getSharedPreferences(SERVER_SELECTION_FILE, - MODE_PRIVATE); + final SharedPreferences selectedServerPrefs = ctx.getSharedPreferences(SERVER_SELECTION_FILE, MODE_PRIVATE); final Editor edit = selectedServerPrefs.edit(); edit.clear(); edit.putString(SERVER_NAME, server.name); @@ -250,8 +239,7 @@ public void reset() { edit = runtimePrefs.edit(); edit.clear(); edit.apply(); - final SharedPreferences selectedServerPrefs = ctx.getSharedPreferences(SERVER_SELECTION_FILE, - MODE_PRIVATE); + final SharedPreferences selectedServerPrefs = ctx.getSharedPreferences(SERVER_SELECTION_FILE, MODE_PRIVATE); edit = selectedServerPrefs.edit(); edit.clear(); edit.apply(); @@ -338,8 +326,7 @@ private SharedPreferences getSharedPreferences() { */ private boolean isRuntimeConfigAppServiceHostsSet() { try { - return true; // TODO: Remove MDM testing diagnostic. ECJ20260227 -// return getRuntimeConfig(ctx).getStringArrayStoredAsArrayOrCSV(AppServiceHosts) != null; + return getRuntimeConfig(ctx).getStringArrayStoredAsArrayOrCSV(AppServiceHosts) != null; } catch (Exception e) { return false; } @@ -353,16 +340,14 @@ private void resetLoginServersFromRuntimeConfig() { final RuntimeConfig runtimeConfig = getRuntimeConfig(ctx); String[] mdmLoginServers = null; try { - mdmLoginServers = new String[]{"https://mdm.example.com", "https://mdm2.example.com/2"}; // TODO: Remove MDM testing diagnostic. ECJ20260227 -// mdmLoginServers = runtimeConfig.getStringArrayStoredAsArrayOrCSV(AppServiceHosts); + mdmLoginServers = runtimeConfig.getStringArrayStoredAsArrayOrCSV(AppServiceHosts); } catch (Exception e) { SalesforceSDKLogger.w(TAG, "Exception thrown while attempting to read array, attempting to read string value instead", e); } if (mdmLoginServers != null) { String[] mdmLoginServersLabels = null; try { - mdmLoginServersLabels = new String[]{"MDM", "MDM2.1"}; // TODO: Remove MDM testing diagnostic. ECJ20260227 -// mdmLoginServersLabels = runtimeConfig.getStringArrayStoredAsArrayOrCSV(AppServiceHostLabels); + mdmLoginServersLabels = runtimeConfig.getStringArrayStoredAsArrayOrCSV(AppServiceHostLabels); } catch (Exception e) { SalesforceSDKLogger.w(TAG, "Exception thrown while attempting to read array, attempting to read string value instead", e); } @@ -371,17 +356,9 @@ private void resetLoginServersFromRuntimeConfig() { mdmLoginServersLabels = mdmLoginServers; } - // TODO: Remove Diagnostic. ECJ20260227 - Log.i("LSM", "INITIAL RUNTIME SHARED PREFERENCES."); - logSharedPreferences(runtimePrefs); - // Reset non-custom servers from Mobile Device Management (MDM). resetNonCustomLoginServers(runtimePrefs); - // TODO: Remove Diagnostic. ECJ20260227 - Log.i("LSM", "AFTER RESET MANAGED SERVERS FROM RUNTIME SHARED PREFERENCES."); - logSharedPreferences(runtimePrefs); - for (int i = 0; i < mdmLoginServers.length; i++) { final String name = mdmLoginServersLabels[i]; final String url = mdmLoginServers[i]; @@ -532,10 +509,9 @@ private List getLegacyLoginServers() { */ private List getLoginServersFromXML() { List loginServers = null; - int id = ctx.getResources().getIdentifier("servers", "xml", ctx.getPackageName()); - if (id != 0) { + if (servers != 0) { loginServers = new ArrayList<>(); - final XmlResourceParser xml = ctx.getResources().getXml(id); + final XmlResourceParser xml = ctx.getResources().getXml(servers); int eventType = -1; while (eventType != XmlResourceParser.END_DOCUMENT) { if (eventType == XmlResourceParser.START_TAG) { @@ -636,8 +612,6 @@ private void persistLoginServer(final String name, adjustedIndex = getNextNonCustomLoginServerIndex(sharedPreferences); } - Log.i("LSM", "Persisting Login Server: '" + name + "', URL: '" + url + "', isCustom: '" + isCustom + "', Index: '" + null + "', adjustedIndex: '" + adjustedIndex + "'."); - final Editor editor = sharedPreferences.edit(); // Increment existing login servers as needed. @@ -652,8 +626,6 @@ private void persistLoginServer(final String name, final String loginServerUrl = sharedPreferences.getString(loginServerUrlKey, null); final boolean loginServerIsCustom = sharedPreferences.getBoolean(loginServerIsCustomKey, false); - Log.i("LSM", "Incrementing Login Server: '" + loginServerName + "', URL: '" + loginServerUrl + "', isCustom: '" + loginServerIsCustom + "', Index: '" + i + "'/'" + incrementedIndex + "'."); - editor .remove(loginServerNameKey) .remove(loginServerUrlKey) @@ -665,7 +637,6 @@ private void persistLoginServer(final String name, } // Insert the new login server. - Log.i("LSM", "Inserting Login Server: '" + name + "', URL: '" + url + "', isCustom: '" + isCustom + "', Index: '" + adjustedIndex + "'."); editor.putString(format(US, SERVER_NAME, adjustedIndex), name.trim()); editor.putString(format(US, SERVER_URL, adjustedIndex), url.trim()); editor.putBoolean(format(US, IS_CUSTOM, adjustedIndex), isCustom); @@ -699,17 +670,6 @@ private void persistLoginServer(final String name, return !allServers.isEmpty() ? allServers : new ArrayList<>(); } - // TODO: Remove Diagnostic. ECJ20260227 - private void logSharedPreferences(final SharedPreferences sharedPreferences) { - final Map preferences = sharedPreferences.getAll(); - final ArrayList keys = new ArrayList<>(preferences.keySet()); - Collections.sort(keys); - for (int i = 0; i < preferences.size(); i++) { - final Object key = keys.toArray()[i]; - Log.i("LSM", "Settings: '" + key + "'/'" + preferences.get(key) + "'."); - } - } - /** * Resets the list of non-custom login servers in the provided shared preferences. * From 99edfa600b975b09b5bf14178360a1a68e93d80a Mon Sep 17 00:00:00 2001 From: "Eric C. Johnson" Date: Mon, 2 Mar 2026 20:44:31 -0700 Subject: [PATCH 04/14] @W-21148602: Internal Server List does not respect changes made to Servers.xml file (Slight Self-Review Cleanup, Revert `getLoginServersFromRuntimeConfig` To Maintain Point Release API Compatibility) --- .../androidsdk/config/LoginServerManager.java | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/libs/SalesforceSDK/src/com/salesforce/androidsdk/config/LoginServerManager.java b/libs/SalesforceSDK/src/com/salesforce/androidsdk/config/LoginServerManager.java index e82bf5fa17..b620d31f40 100644 --- a/libs/SalesforceSDK/src/com/salesforce/androidsdk/config/LoginServerManager.java +++ b/libs/SalesforceSDK/src/com/salesforce/androidsdk/config/LoginServerManager.java @@ -144,7 +144,7 @@ public LoginServer getSelectedLoginServer() { // Refresh the list of mobile device management (MDM) servers from the runtime config. if (isRuntimeConfigAppServiceHostsSet()) { - resetLoginServersFromRuntimeConfig(); + getLoginServersFromRuntimeConfig(); } // Get the active list of login servers. @@ -310,7 +310,8 @@ public List getLoginServers() { } /** - * Returns the active shared preferences when using mobile device management (MDM) or otherwise. + * Returns the active shared preferences when using login servers from mobile device management + * (MDM) or the resources server.xml. * * @return SharedPreferences The active shared preferences */ @@ -322,7 +323,8 @@ private SharedPreferences getSharedPreferences() { * Determines if managed (non-custom) login servers are provided by mobile device management * (MDM). * - * @return boolean True indicates managed login servers are provided by MDM and false otherwise + * @return boolean True indicates managed login servers are provided by MDM or the resources + * server.xml when false */ private boolean isRuntimeConfigAppServiceHostsSet() { try { @@ -336,7 +338,8 @@ private boolean isRuntimeConfigAppServiceHostsSet() { * Resets the list of Mobile Device Management (MDM) login servers from the runtime * configuration. This does not remove the user's custom login servers. */ - private void resetLoginServersFromRuntimeConfig() { + @SuppressWarnings("UnusedReturnValue") + public List getLoginServersFromRuntimeConfig() { final RuntimeConfig runtimeConfig = getRuntimeConfig(ctx); String[] mdmLoginServers = null; try { @@ -344,6 +347,7 @@ private void resetLoginServersFromRuntimeConfig() { } catch (Exception e) { SalesforceSDKLogger.w(TAG, "Exception thrown while attempting to read array, attempting to read string value instead", e); } + final List allServers = new ArrayList<>(); if (mdmLoginServers != null) { String[] mdmLoginServersLabels = null; try { @@ -362,14 +366,17 @@ private void resetLoginServersFromRuntimeConfig() { for (int i = 0; i < mdmLoginServers.length; i++) { final String name = mdmLoginServersLabels[i]; final String url = mdmLoginServers[i]; + final LoginServer server = new LoginServer(name, url, false); persistLoginServer( name, url, false, /* Non-Custom */ runtimePrefs ); + allServers.add(server); } } + return (!allServers.isEmpty() ? allServers : null); } /** @@ -583,13 +590,13 @@ private void initSharedPrefFile() { } /** - * Persists a login server the specified shared preferences file. + * Persists a login server to the specified shared preferences. * * @param name The login server name * @param url The login server URL * @param isCustom boolean true for non-custom (managed) login servers, false for * custom (user-entered) login servers - * @param sharedPreferences SharedPreferences file + * @param sharedPreferences The shared preferences */ private void persistLoginServer(final String name, final String url, From 48a047e8715007507770a8d5cfc463ecf2b32f08 Mon Sep 17 00:00:00 2001 From: "Eric C. Johnson" Date: Mon, 2 Mar 2026 21:08:14 -0700 Subject: [PATCH 05/14] @W-21148602: Internal Server List does not respect changes made to Servers.xml file (Unit Test Updates) --- .../auth/LoginServerManagerTest.java | 760 +++++++++--------- 1 file changed, 381 insertions(+), 379 deletions(-) diff --git a/libs/test/SalesforceSDKTest/src/com/salesforce/androidsdk/auth/LoginServerManagerTest.java b/libs/test/SalesforceSDKTest/src/com/salesforce/androidsdk/auth/LoginServerManagerTest.java index f0cd848973..7f3a894d88 100644 --- a/libs/test/SalesforceSDKTest/src/com/salesforce/androidsdk/auth/LoginServerManagerTest.java +++ b/libs/test/SalesforceSDKTest/src/com/salesforce/androidsdk/auth/LoginServerManagerTest.java @@ -26,21 +26,14 @@ */ package com.salesforce.androidsdk.auth; -import android.app.Application; -import android.app.Instrumentation; -import android.content.Context; +import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; import androidx.arch.core.executor.testing.InstantTaskExecutorRule; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; -import androidx.test.platform.app.InstrumentationRegistry; -import com.salesforce.androidsdk.TestForceApp; -import com.salesforce.androidsdk.app.SalesforceSDKManager; import com.salesforce.androidsdk.config.LoginServerManager; import com.salesforce.androidsdk.config.LoginServerManager.LoginServer; -import com.salesforce.androidsdk.util.EventsObservable.EventType; -import com.salesforce.androidsdk.util.test.EventsListenerQueue; import org.junit.After; import org.junit.Assert; @@ -53,451 +46,460 @@ /** * Tests for LoginServerManager. + *

+ * TODO: Each test should include a cold start. ECJ20260302 + *

+ * TODO: Default servers from resources server.xml. ECJ20260302 + *

+ * TODO: Add server from resources server.xml WITH NO custom servers. ECJ20260302 + * TODO: Update server from resources server.xml WITH NO custom servers. ECJ20260302 + * TODO: Remove server from resources server.xml WITH NO custom servers. ECJ20260302 + *

+ * TODO: Add server from resources server.xml WITH custom servers. ECJ20260302 + * TODO: Update server from resources server.xml WITH custom servers. ECJ20260302 + * TODO: Remove server from resources server.xml WITH custom servers. ECJ20260302 + *

+ * TODO: Default servers from runtime config. ECJ20260302 + *

+ * TODO: Add server from runtime config WITH NO custom servers. ECJ20260302 + * TODO: Update server from runtime config WITH NO custom servers. ECJ20260302 + * TODO: Remove server from runtime config WITH NO custom servers. ECJ20260302 + *

+ * TODO: Add server from runtime config WITH custom servers. ECJ20260302 + * TODO: Update server from runtime config WITH custom servers. ECJ20260302 + * TODO: Remove server from runtime config WITH custom servers. ECJ20260302 + * TODO: ECJ20260302 */ @RunWith(AndroidJUnit4.class) @SmallTest public class LoginServerManagerTest { - private static final String PRODUCTION_URL = "https://login.salesforce.com"; - private static final String SANDBOX_URL = "https://test.salesforce.com"; - private static final String OTHER_URL = "https://other.salesforce.com"; - private static final String CUSTOM_NAME = "New"; - private static final String CUSTOM_URL = "https://new.com"; - private static final String CUSTOM_NAME_2 = "New2"; - private static final String CUSTOM_URL_2 = "https://new2.com"; + private static final String PRODUCTION_URL = "https://login.salesforce.com"; + private static final String SANDBOX_URL = "https://test.salesforce.com"; + private static final String OTHER_URL = "https://other.salesforce.com"; + private static final String CUSTOM_NAME = "New"; + private static final String CUSTOM_URL = "https://new.com"; + private static final String CUSTOM_NAME_2 = "New2"; + private static final String CUSTOM_URL_2 = "https://new2.com"; - private LoginServerManager loginServerManager; - private EventsListenerQueue eq; + private LoginServerManager loginServerManager; @Rule - public final InstantTaskExecutorRule instantExecutorRule = new InstantTaskExecutorRule(); - - @Before - public void setUp() throws Exception { - Context targetContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); - eq = new EventsListenerQueue(); - - // Wait for app initialization to complete. - final Application app = Instrumentation.newApplication(TestForceApp.class, targetContext); - InstrumentationRegistry.getInstrumentation().callApplicationOnCreate(app); - if (!SalesforceSDKManager.hasInstance()) { - eq.waitForEvent(EventType.AppCreateComplete, 5000); - } - loginServerManager = SalesforceSDKManager.getInstance().getLoginServerManager(); + public final InstantTaskExecutorRule instantExecutorRule = new InstantTaskExecutorRule(); + + @Before + public void setUp() throws Exception { + loginServerManager = new LoginServerManager(getInstrumentation().getTargetContext()); + loginServerManager.reset(); } @After public void tearDown() throws Exception { - if (loginServerManager != null) { - loginServerManager.reset(); - } - if (eq != null) { - eq.tearDown(); - eq = null; - } + loginServerManager.reset(); + loginServerManager = null; } /** - * Test for getLoginServerFromURL. - */ + * Test for getLoginServerFromURL. + */ @Test - public void testGetLoginServerFromURL() { + public void testGetLoginServerFromURL() { assertProduction(loginServerManager.getLoginServerFromURL(PRODUCTION_URL)); assertSandbox(loginServerManager.getLoginServerFromURL(SANDBOX_URL)); assertOther(loginServerManager.getLoginServerFromURL(OTHER_URL)); Assert.assertNull("Expected null", loginServerManager.getLoginServerFromURL("https://wrong.salesforce.com")); - } + } - /** - * Test for getDefaultLoginServer. - */ + /** + * Test for getDefaultLoginServer. + */ @Test - public void testGetDefaultLoginServers() { - final List servers = loginServerManager.getLoginServers(); + public void testGetDefaultLoginServers() { + final List servers = loginServerManager.getLoginServers(); Assert.assertEquals("Wrong number of servers", 3, servers.size()); - assertProduction(servers.get(0)); - assertSandbox(servers.get(1)); - assertOther(servers.get(2)); - } - - /** - * Test for getSelectedLoginServer/setSelectedLoginServer when there is no custom login server. - */ + assertProduction(servers.get(0)); + assertSandbox(servers.get(1)); + assertOther(servers.get(2)); + } + + /** + * Test for getSelectedLoginServer/setSelectedLoginServer when there is no custom login server. + */ @Test - public void testGetSetLoginServerWithoutCustomServer() { - - // Starting point, production selected by default. - assertProduction(loginServerManager.getSelectedLoginServer()); - - // Selecting production. - loginServerManager.setSelectedLoginServer(new LoginServer("Production", - PRODUCTION_URL, false)); - assertProduction(loginServerManager.getSelectedLoginServer()); - - // Selecting sandbox. - loginServerManager.setSelectedLoginServer(new LoginServer("Sandbox", - SANDBOX_URL, false)); - assertSandbox(loginServerManager.getSelectedLoginServer()); - - // Selecting other. - loginServerManager.setSelectedLoginServer(new LoginServer("Other", - OTHER_URL, false)); - assertOther(loginServerManager.getSelectedLoginServer()); - } - - /** - * Test for getSelectedLoginServer/setSelectedLoginServer when there is a custom login server. - */ + public void testGetSetLoginServerWithoutCustomServer() { + + // Starting point, production selected by default. + assertProduction(loginServerManager.getSelectedLoginServer()); + + // Selecting production. + loginServerManager.setSelectedLoginServer(new LoginServer("Production", + PRODUCTION_URL, false)); + assertProduction(loginServerManager.getSelectedLoginServer()); + + // Selecting sandbox. + loginServerManager.setSelectedLoginServer(new LoginServer("Sandbox", + SANDBOX_URL, false)); + assertSandbox(loginServerManager.getSelectedLoginServer()); + + // Selecting other. + loginServerManager.setSelectedLoginServer(new LoginServer("Other", + OTHER_URL, false)); + assertOther(loginServerManager.getSelectedLoginServer()); + } + + /** + * Test for getSelectedLoginServer/setSelectedLoginServer when there is a custom login server. + */ @Test - public void testGetSetLoginServerWithCustomServer() { + public void testGetSetLoginServerWithCustomServer() { - // Starting point, production selected by default. - assertProduction(loginServerManager.getSelectedLoginServer()); + // Starting point, production selected by default. + assertProduction(loginServerManager.getSelectedLoginServer()); - // Adding custom server, custom should be selected. - loginServerManager.addCustomLoginServer(CUSTOM_NAME, CUSTOM_URL); - assertCustom(loginServerManager.getSelectedLoginServer()); - } + // Adding custom server, custom should be selected. + loginServerManager.addCustomLoginServer(CUSTOM_NAME, CUSTOM_URL); + assertCustom(loginServerManager.getSelectedLoginServer()); + } - /** - * Test for adding more than one custom server. - */ + /** + * Test for adding more than one custom server. + */ @Test - public void testAddMultipleCustomServers() { + public void testAddMultipleCustomServers() { - // Starting point, only 3 servers. - List servers = loginServerManager.getLoginServers(); + // Starting point, only 3 servers. + List servers = loginServerManager.getLoginServers(); Assert.assertEquals("Expected no custom login servers", 3, servers.size()); - // Adding first custom server. - loginServerManager.addCustomLoginServer(CUSTOM_NAME, CUSTOM_URL); - servers = loginServerManager.getLoginServers(); + // Adding first custom server. + loginServerManager.addCustomLoginServer(CUSTOM_NAME, CUSTOM_URL); + servers = loginServerManager.getLoginServers(); Assert.assertEquals("Expected one custom login server", 4, servers.size()); - // Adding second custom server. - loginServerManager.addCustomLoginServer(CUSTOM_NAME_2, CUSTOM_URL_2); - servers = loginServerManager.getLoginServers(); + // Adding second custom server. + loginServerManager.addCustomLoginServer(CUSTOM_NAME_2, CUSTOM_URL_2); + servers = loginServerManager.getLoginServers(); Assert.assertEquals("Expected one custom login server", 5, servers.size()); - } + } - /** - * Test for getCustomLoginServer/setCustomLoginServer. - */ + /** + * Test for getCustomLoginServer/setCustomLoginServer. + */ @Test - public void testGetSetCustomLoginServer() { + public void testGetSetCustomLoginServer() { - // Starting point, custom is null. + // Starting point, custom is null. Assert.assertNull("Expected no custom login server", loginServerManager.getLoginServerFromURL(CUSTOM_URL)); - // Adding custom server. - loginServerManager.addCustomLoginServer(CUSTOM_NAME, CUSTOM_URL); - assertCustom(loginServerManager.getSelectedLoginServer()); + // Adding custom server. + loginServerManager.addCustomLoginServer(CUSTOM_NAME, CUSTOM_URL); + assertCustom(loginServerManager.getSelectedLoginServer()); - // Adding a second custom server. - loginServerManager.addCustomLoginServer(CUSTOM_NAME_2, CUSTOM_URL_2); - assertCustom2(loginServerManager.getSelectedLoginServer()); - } + // Adding a second custom server. + loginServerManager.addCustomLoginServer(CUSTOM_NAME_2, CUSTOM_URL_2); + assertCustom2(loginServerManager.getSelectedLoginServer()); + } - /** - * Test for useSandbox. - */ + /** + * Test for useSandbox. + */ @Test - public void testUseSandbox() { + public void testUseSandbox() { - // Starting point, production selected by default. - assertProduction(loginServerManager.getSelectedLoginServer()); + // Starting point, production selected by default. + assertProduction(loginServerManager.getSelectedLoginServer()); - // Calling useSandbox. - loginServerManager.useSandbox(); - assertSandbox(loginServerManager.getSelectedLoginServer()); - } + // Calling useSandbox. + loginServerManager.useSandbox(); + assertSandbox(loginServerManager.getSelectedLoginServer()); + } - /** - * Test for reset. - */ + /** + * Test for reset. + */ @Test - public void testReset() { + public void testReset() { - // Starting point, only 3 servers. - List servers = loginServerManager.getLoginServers(); + // Starting point, only 3 servers. + List servers = loginServerManager.getLoginServers(); Assert.assertEquals("Expected no custom login servers", 3, servers.size()); - // Adding custom server. - loginServerManager.addCustomLoginServer(CUSTOM_NAME, CUSTOM_URL); - servers = loginServerManager.getLoginServers(); + // Adding custom server. + loginServerManager.addCustomLoginServer(CUSTOM_NAME, CUSTOM_URL); + servers = loginServerManager.getLoginServers(); Assert.assertEquals("Expected one custom login server", 4, servers.size()); - // Selecting sandbox. - loginServerManager.useSandbox(); - assertSandbox(loginServerManager.getSelectedLoginServer()); + // Selecting sandbox. + loginServerManager.useSandbox(); + assertSandbox(loginServerManager.getSelectedLoginServer()); - /* - * Calling reset - selection should go back to production - * and custom server should be removed from shared prefs. - */ - loginServerManager.reset(); - servers = loginServerManager.getLoginServers(); + /* + * Calling reset - selection should go back to production + * and custom server should be removed from shared prefs. + */ + loginServerManager.reset(); + servers = loginServerManager.getLoginServers(); Assert.assertEquals("Expected no custom login servers", 3, servers.size()); - assertProduction(loginServerManager.getSelectedLoginServer()); - } - - /** - * Test selectedServer LiveData. - */ - @Test - public void testLiveData() { - // Assert the method returns the same result as the backing LiveData. - assertLiveData(); - - loginServerManager.addCustomLoginServer("live data", PRODUCTION_URL); - assertLiveData(); - - loginServerManager.selectedServer.postValue(new LoginServer("Live Data 2", PRODUCTION_URL, false)); - assertLiveData(); - } - - /** - * Test removing the last server. - */ - @Test - public void testRemoveServer() { - loginServerManager.addCustomLoginServer(CUSTOM_NAME, CUSTOM_URL); - int originalServerSize = 4; // 3 default servers + 1 custom - List servers = loginServerManager.getLoginServers(); - Assert.assertEquals("Expected one custom login server", originalServerSize, servers.size()); - LoginServer lastServer = servers.get(3); - - // Remove - loginServerManager.removeServer(lastServer); - servers = loginServerManager.getLoginServers(); - Assert.assertEquals("", (originalServerSize - 1), servers.size()); - Assert.assertFalse("List should not contain removed server.", servers.contains(lastServer)); - } - - /** - * Test removing a server in the middle reorders the rest. - */ - @Test - public void testRemoveReordersServers() { - loginServerManager.addCustomLoginServer(CUSTOM_NAME, CUSTOM_URL); - loginServerManager.addCustomLoginServer(CUSTOM_NAME_2, CUSTOM_URL_2); - int originalServerSize = 5; // 3 default servers + 2 custom - List servers = loginServerManager.getLoginServers(); - Assert.assertEquals("Expected one custom login server", originalServerSize, servers.size()); - LoginServer serverToDelete = servers.get(3); - - // Remove - loginServerManager.removeServer(serverToDelete); - servers = loginServerManager.getLoginServers(); - Assert.assertEquals("No servers removed.", (originalServerSize - 1), servers.size()); - Assert.assertFalse("List should not contain removed server.", servers.contains(serverToDelete)); - - // Assert Reorder - assertProduction(servers.get(0)); - assertSandbox(servers.get(1)); - assertOther(servers.get(2)); - assertCustom2(servers.get(3)); - } - - /** - * Test attempting to remove a non-custom server. - */ - @Test - public void testRemoveNonCustomServer() { - int originalServerSize = 3; // 3 default servers - List servers = loginServerManager.getLoginServers(); - Assert.assertEquals("Expected one custom login server", originalServerSize, servers.size()); - LoginServer serverToDelete = servers.get(0); - - // Remove - loginServerManager.removeServer(serverToDelete); - servers = loginServerManager.getLoginServers(); - Assert.assertEquals("Servers should not be removed.", originalServerSize, servers.size()); - } - - /** - * Test attempting to add a duplicate server default or custom server. - */ - @Test - public void testAddingDuplicateServers() { - int originalServerSize = 3; // 3 default servers - List servers = loginServerManager.getLoginServers(); - Assert.assertEquals("Expected one custom login server", originalServerSize, servers.size()); - LoginServer prodServer = loginServerManager.getLoginServerFromURL(PRODUCTION_URL); - - // Attempt to add a default server as a custom server. - loginServerManager.addCustomLoginServer(prodServer.name, prodServer.url); - Assert.assertEquals("Duplicate server should not be added.", originalServerSize, - loginServerManager.getLoginServers().size()); - - // Attempt to add a duplicate custom server. - loginServerManager.addCustomLoginServer(CUSTOM_NAME, CUSTOM_URL); - Assert.assertEquals("Custom server should be added.", (originalServerSize + 1), - loginServerManager.getLoginServers().size()); - loginServerManager.addCustomLoginServer(CUSTOM_NAME, CUSTOM_URL); - Assert.assertEquals("Duplicate custom server should not be added.", (originalServerSize + 1), - loginServerManager.getLoginServers().size()); - - // Ensure servers with duplicate names but unique URLs are allowed. - loginServerManager.addCustomLoginServer(CUSTOM_NAME, CUSTOM_URL_2); - Assert.assertEquals("Custom server should be added.", (originalServerSize + 2), - loginServerManager.getLoginServers().size()); - loginServerManager.addCustomLoginServer(prodServer.name, "https://custom3.com"); - Assert.assertEquals("Custom server should be added..", (originalServerSize + 3), - loginServerManager.getLoginServers().size()); - } - - /** - * Test both replace and re-order custom login server. - */ - @Test - public void testReplaceAndReOrderCustomLoginServer() { - - // Test data. - final String originalName = "ORIGINAL_CUSTOM_LOGIN_SERVER_FOR_REPLACEMENT_TEST"; - final String originalUrl = "https://original.example.com"; - final LoginServer originalCustomLoginServer = new LoginServer( - originalName, - originalUrl, - true - ); - final String otherName = "OTHER_CUSTOM_LOGIN_SERVER_FOR_REPLACEMENT_TEST"; - final String otherUrl = "https://other.example.com"; - final LoginServer otherCustomLoginServer = new LoginServer( - otherName, - otherUrl, - true - ); - final String updatedName = "UPDATED_CUSTOM_LOGIN_SERVER_FOR_REPLACEMENT_TEST"; - final String updatedUrl = "https://updated.example.com"; - final LoginServer updatedCustomLoginServer = new LoginServer( - updatedName, - updatedUrl, - true - ); - final String nonCustomName = "NON_CUSTOM_LOGIN_SERVER_FOR_REPLACEMENT_TEST"; - final String nonCustomUrl = "https://non.custom.example.com"; - final LoginServer nonCustomLoginServer = new LoginServer( - nonCustomName, - nonCustomUrl, - false - ); - - // Verify the original and other custom login servers are not present. - Assert.assertFalse(loginServerManager.getLoginServers().contains(originalCustomLoginServer)); - Assert.assertFalse(loginServerManager.getLoginServers().contains(otherCustomLoginServer)); - - - // Add the original and other custom login server. - loginServerManager.addCustomLoginServer(originalName, originalUrl); - loginServerManager.addCustomLoginServer(otherName, otherUrl); - - // Verify the original and other custom login servers were added. - Assert.assertEquals(originalCustomLoginServer, loginServerManager.getLoginServers().get(loginServerManager.getLoginServers().size() - 2)); - Assert.assertEquals(otherCustomLoginServer, loginServerManager.getLoginServers().get(loginServerManager.getLoginServers().size() - 1)); - - - // Prepare for negative tests. - final LoginServer production = new LoginServer("Production", "https://login.salesforce.com", false); - final LoginServer productionMismatch = new LoginServer("Production?", "https://login.salesforce.com", true); - final LoginServer productionReplacement = new LoginServer("Production Replaced", "https://login.salesforce.com", false); - final LoginServer productionReplacementMismatch = new LoginServer("Production Replaced?", "https://login.salesforce.com", true); - - // Attempt the prohibited replacement of a non-custom login server where the original matches. - loginServerManager.replaceCustomLoginServer(production, productionReplacement); - Assert.assertTrue(loginServerManager.getLoginServers().contains(production)); - Assert.assertFalse(loginServerManager.getLoginServers().contains(productionReplacement)); - - - // Attempt the prohibited replacement of a non-custom login server where the original doesn't exit. - loginServerManager.replaceCustomLoginServer(productionMismatch, productionReplacementMismatch); - Assert.assertTrue(loginServerManager.getLoginServers().contains(production)); - Assert.assertFalse(loginServerManager.getLoginServers().contains(productionReplacement)); - - - // Attempt the prohibited reordering of a non-custom login server. - loginServerManager.reorderCustomLoginServer(0, 1); - Assert.assertEquals(loginServerManager.getLoginServers().get(0), production); - - - // Replace the original custom login server with a non-custom server. - loginServerManager.replaceCustomLoginServer(originalCustomLoginServer, nonCustomLoginServer); - - // Verify the original and other custom login servers weren't changed. - Assert.assertFalse(loginServerManager.getLoginServers().contains(nonCustomLoginServer)); - Assert.assertEquals(originalCustomLoginServer, loginServerManager.getLoginServers().get(loginServerManager.getLoginServers().size() - 2)); - Assert.assertEquals(otherCustomLoginServer, loginServerManager.getLoginServers().get(loginServerManager.getLoginServers().size() - 1)); - - - // Replace the original custom login server. - loginServerManager.replaceCustomLoginServer(originalCustomLoginServer, updatedCustomLoginServer); - - // Verify the original custom login server is not present. - Assert.assertFalse(loginServerManager.getLoginServers().contains(originalCustomLoginServer)); - - // Verify the updated and other custom login servers are present. - Assert.assertEquals(updatedCustomLoginServer, loginServerManager.getLoginServers().get(loginServerManager.getLoginServers().size() - 2)); - Assert.assertEquals(otherCustomLoginServer, loginServerManager.getLoginServers().get(loginServerManager.getLoginServers().size() - 1)); - - // Attempt to move the updated custom login server above the non-custom login servers. - loginServerManager.reorderCustomLoginServer(loginServerManager.getLoginServers().indexOf(updatedCustomLoginServer), 0); - - // Verify the updated custom login server is actually immediately following the last non-custom login server. - final List loginServers = loginServerManager.getLoginServers(); - int lastNonCustomIndex = -1; - for (int i = 0; i < loginServers.size(); i++) { - final LoginServer loginServer = loginServers.get(i); - if (!loginServer.isCustom) { - lastNonCustomIndex = i; - } - } - Assert.assertEquals(loginServers.get(lastNonCustomIndex + 1), updatedCustomLoginServer); - - - // Attempt to move the updated custom login server one greater than the upper bounds of the login servers list. - loginServerManager.reorderCustomLoginServer(loginServerManager.getLoginServers().indexOf(updatedCustomLoginServer), loginServerManager.getLoginServers().size()); - - // Attempt to move the updated custom login server more than one greater than the upper bounds of the login servers list. - loginServerManager.reorderCustomLoginServer(loginServerManager.getLoginServers().indexOf(updatedCustomLoginServer), loginServerManager.getLoginServers().size() + 1); - - // Attempt to move the updated custom login server more than one less than the upper bounds of the login servers list. - loginServerManager.reorderCustomLoginServer(loginServerManager.getLoginServers().indexOf(updatedCustomLoginServer), loginServerManager.getLoginServers().size() - 1); + assertProduction(loginServerManager.getSelectedLoginServer()); + } - // Verify the updated custom login server is now the last login server in the list. - Assert.assertEquals(loginServerManager.getLoginServers().getLast(), updatedCustomLoginServer); - } + /** + * Test selectedServer LiveData. + */ + @Test + public void testLiveData() { + // Assert the method returns the same result as the backing LiveData. + assertLiveData(); - private void assertProduction(LoginServer server) { + loginServerManager.addCustomLoginServer("live data", PRODUCTION_URL); + assertLiveData(); + + loginServerManager.selectedServer.postValue(new LoginServer("Live Data 2", PRODUCTION_URL, false)); + assertLiveData(); + } + + /** + * Test removing the last server. + */ + @Test + public void testRemoveServer() { + loginServerManager.addCustomLoginServer(CUSTOM_NAME, CUSTOM_URL); + int originalServerSize = 4; // 3 default servers + 1 custom + List servers = loginServerManager.getLoginServers(); + Assert.assertEquals("Expected one custom login server", originalServerSize, servers.size()); + LoginServer lastServer = servers.get(3); + + // Remove + loginServerManager.removeServer(lastServer); + servers = loginServerManager.getLoginServers(); + Assert.assertEquals("", (originalServerSize - 1), servers.size()); + Assert.assertFalse("List should not contain removed server.", servers.contains(lastServer)); + } + + /** + * Test removing a server in the middle reorders the rest. + */ + @Test + public void testRemoveReordersServers() { + loginServerManager.addCustomLoginServer(CUSTOM_NAME, CUSTOM_URL); + loginServerManager.addCustomLoginServer(CUSTOM_NAME_2, CUSTOM_URL_2); + int originalServerSize = 5; // 3 default servers + 2 custom + List servers = loginServerManager.getLoginServers(); + Assert.assertEquals("Expected one custom login server", originalServerSize, servers.size()); + LoginServer serverToDelete = servers.get(3); + + // Remove + loginServerManager.removeServer(serverToDelete); + servers = loginServerManager.getLoginServers(); + Assert.assertEquals("No servers removed.", (originalServerSize - 1), servers.size()); + Assert.assertFalse("List should not contain removed server.", servers.contains(serverToDelete)); + + // Assert Reorder + assertProduction(servers.get(0)); + assertSandbox(servers.get(1)); + assertOther(servers.get(2)); + assertCustom2(servers.get(3)); + } + + /** + * Test attempting to remove a non-custom server. + */ + @Test + public void testRemoveNonCustomServer() { + int originalServerSize = 3; // 3 default servers + List servers = loginServerManager.getLoginServers(); + Assert.assertEquals("Expected one custom login server", originalServerSize, servers.size()); + LoginServer serverToDelete = servers.get(0); + + // Remove + loginServerManager.removeServer(serverToDelete); + servers = loginServerManager.getLoginServers(); + Assert.assertEquals("Servers should not be removed.", originalServerSize, servers.size()); + } + + /** + * Test attempting to add a duplicate server default or custom server. + */ + @Test + public void testAddingDuplicateServers() { + int originalServerSize = 3; // 3 default servers + List servers = loginServerManager.getLoginServers(); + Assert.assertEquals("Expected one custom login server", originalServerSize, servers.size()); + LoginServer prodServer = loginServerManager.getLoginServerFromURL(PRODUCTION_URL); + + // Attempt to add a default server as a custom server. + loginServerManager.addCustomLoginServer(prodServer.name, prodServer.url); + Assert.assertEquals("Duplicate server should not be added.", originalServerSize, + loginServerManager.getLoginServers().size()); + + // Attempt to add a duplicate custom server. + loginServerManager.addCustomLoginServer(CUSTOM_NAME, CUSTOM_URL); + Assert.assertEquals("Custom server should be added.", (originalServerSize + 1), + loginServerManager.getLoginServers().size()); + loginServerManager.addCustomLoginServer(CUSTOM_NAME, CUSTOM_URL); + Assert.assertEquals("Duplicate custom server should not be added.", (originalServerSize + 1), + loginServerManager.getLoginServers().size()); + + // Ensure servers with duplicate names but unique URLs are allowed. + loginServerManager.addCustomLoginServer(CUSTOM_NAME, CUSTOM_URL_2); + Assert.assertEquals("Custom server should be added.", (originalServerSize + 2), + loginServerManager.getLoginServers().size()); + loginServerManager.addCustomLoginServer(prodServer.name, "https://custom3.com"); + Assert.assertEquals("Custom server should be added..", (originalServerSize + 3), + loginServerManager.getLoginServers().size()); + } + + /** + * Test both replace and re-order custom login server. + */ + @Test + public void testReplaceAndReOrderCustomLoginServer() { + + // Test data. + final String originalName = "ORIGINAL_CUSTOM_LOGIN_SERVER_FOR_REPLACEMENT_TEST"; + final String originalUrl = "https://original.example.com"; + final LoginServer originalCustomLoginServer = new LoginServer( + originalName, + originalUrl, + true + ); + final String otherName = "OTHER_CUSTOM_LOGIN_SERVER_FOR_REPLACEMENT_TEST"; + final String otherUrl = "https://other.example.com"; + final LoginServer otherCustomLoginServer = new LoginServer( + otherName, + otherUrl, + true + ); + final String updatedName = "UPDATED_CUSTOM_LOGIN_SERVER_FOR_REPLACEMENT_TEST"; + final String updatedUrl = "https://updated.example.com"; + final LoginServer updatedCustomLoginServer = new LoginServer( + updatedName, + updatedUrl, + true + ); + final String nonCustomName = "NON_CUSTOM_LOGIN_SERVER_FOR_REPLACEMENT_TEST"; + final String nonCustomUrl = "https://non.custom.example.com"; + final LoginServer nonCustomLoginServer = new LoginServer( + nonCustomName, + nonCustomUrl, + false + ); + + // Verify the original and other custom login servers are not present. + Assert.assertFalse(loginServerManager.getLoginServers().contains(originalCustomLoginServer)); + Assert.assertFalse(loginServerManager.getLoginServers().contains(otherCustomLoginServer)); + + + // Add the original and other custom login server. + loginServerManager.addCustomLoginServer(originalName, originalUrl); + loginServerManager.addCustomLoginServer(otherName, otherUrl); + + // Verify the original and other custom login servers were added. + Assert.assertEquals(originalCustomLoginServer, loginServerManager.getLoginServers().get(loginServerManager.getLoginServers().size() - 2)); + Assert.assertEquals(otherCustomLoginServer, loginServerManager.getLoginServers().get(loginServerManager.getLoginServers().size() - 1)); + + + // Prepare for negative tests. + final LoginServer production = new LoginServer("Production", "https://login.salesforce.com", false); + final LoginServer productionMismatch = new LoginServer("Production?", "https://login.salesforce.com", true); + final LoginServer productionReplacement = new LoginServer("Production Replaced", "https://login.salesforce.com", false); + final LoginServer productionReplacementMismatch = new LoginServer("Production Replaced?", "https://login.salesforce.com", true); + + // Attempt the prohibited replacement of a non-custom login server where the original matches. + loginServerManager.replaceCustomLoginServer(production, productionReplacement); + Assert.assertTrue(loginServerManager.getLoginServers().contains(production)); + Assert.assertFalse(loginServerManager.getLoginServers().contains(productionReplacement)); + + + // Attempt the prohibited replacement of a non-custom login server where the original doesn't exit. + loginServerManager.replaceCustomLoginServer(productionMismatch, productionReplacementMismatch); + Assert.assertTrue(loginServerManager.getLoginServers().contains(production)); + Assert.assertFalse(loginServerManager.getLoginServers().contains(productionReplacement)); + + + // Attempt the prohibited reordering of a non-custom login server. + loginServerManager.reorderCustomLoginServer(0, 1); + Assert.assertEquals(loginServerManager.getLoginServers().get(0), production); + + + // Replace the original custom login server with a non-custom server. + loginServerManager.replaceCustomLoginServer(originalCustomLoginServer, nonCustomLoginServer); + + // Verify the original and other custom login servers weren't changed. + Assert.assertFalse(loginServerManager.getLoginServers().contains(nonCustomLoginServer)); + Assert.assertEquals(originalCustomLoginServer, loginServerManager.getLoginServers().get(loginServerManager.getLoginServers().size() - 2)); + Assert.assertEquals(otherCustomLoginServer, loginServerManager.getLoginServers().get(loginServerManager.getLoginServers().size() - 1)); + + + // Replace the original custom login server. + loginServerManager.replaceCustomLoginServer(originalCustomLoginServer, updatedCustomLoginServer); + + // Verify the original custom login server is not present. + Assert.assertFalse(loginServerManager.getLoginServers().contains(originalCustomLoginServer)); + + // Verify the updated and other custom login servers are present. + Assert.assertEquals(updatedCustomLoginServer, loginServerManager.getLoginServers().get(loginServerManager.getLoginServers().size() - 2)); + Assert.assertEquals(otherCustomLoginServer, loginServerManager.getLoginServers().get(loginServerManager.getLoginServers().size() - 1)); + + // Attempt to move the updated custom login server above the non-custom login servers. + loginServerManager.reorderCustomLoginServer(loginServerManager.getLoginServers().indexOf(updatedCustomLoginServer), 0); + + // Verify the updated custom login server is actually immediately following the last non-custom login server. + final List loginServers = loginServerManager.getLoginServers(); + int lastNonCustomIndex = -1; + for (int i = 0; i < loginServers.size(); i++) { + final LoginServer loginServer = loginServers.get(i); + if (!loginServer.isCustom) { + lastNonCustomIndex = i; + } + } + Assert.assertEquals(loginServers.get(lastNonCustomIndex + 1), updatedCustomLoginServer); + + + // Attempt to move the updated custom login server one greater than the upper bounds of the login servers list. + loginServerManager.reorderCustomLoginServer(loginServerManager.getLoginServers().indexOf(updatedCustomLoginServer), loginServerManager.getLoginServers().size()); + + // Attempt to move the updated custom login server more than one greater than the upper bounds of the login servers list. + loginServerManager.reorderCustomLoginServer(loginServerManager.getLoginServers().indexOf(updatedCustomLoginServer), loginServerManager.getLoginServers().size() + 1); + + // Attempt to move the updated custom login server more than one less than the upper bounds of the login servers list. + loginServerManager.reorderCustomLoginServer(loginServerManager.getLoginServers().indexOf(updatedCustomLoginServer), loginServerManager.getLoginServers().size() - 1); + + // Verify the updated custom login server is now the last login server in the list. + Assert.assertEquals(loginServerManager.getLoginServers().getLast(), updatedCustomLoginServer); + } + + private void assertProduction(LoginServer server) { Assert.assertEquals("Expected production's name", "Production", server.name); Assert.assertEquals("Expected production's url", PRODUCTION_URL, server.url); Assert.assertFalse("Expected production to be marked as not custom", server.isCustom); - } + } - private void assertSandbox(LoginServer server) { + private void assertSandbox(LoginServer server) { Assert.assertEquals("Expected sandbox's name", "Sandbox", server.name); Assert.assertEquals("Expected sandbox's url", SANDBOX_URL, server.url); Assert.assertFalse("Expected sandbox to be marked as not custom", server.isCustom); - } + } - private void assertOther(LoginServer server) { + private void assertOther(LoginServer server) { Assert.assertEquals("Expected other's name", "Other", server.name); Assert.assertEquals("Expected other's url", OTHER_URL, server.url); Assert.assertFalse("Expected other to be marked as not custom", server.isCustom); - } + } - private void assertCustom(LoginServer server) { + private void assertCustom(LoginServer server) { Assert.assertEquals("Expected custom's name", CUSTOM_NAME, server.name); Assert.assertEquals("Expected custom's url", CUSTOM_URL, server.url); Assert.assertTrue("Expected custom to be marked as not custom", server.isCustom); - } + } - private void assertCustom2(LoginServer server) { + private void assertCustom2(LoginServer server) { Assert.assertEquals("Expected custom2's name", CUSTOM_NAME_2, server.name); Assert.assertEquals("Expected custom2's url", CUSTOM_URL_2, server.url); Assert.assertTrue("Expected custom2 to be marked as not custom", server.isCustom); - } + } - private void assertLiveData() { - Assert.assertEquals(loginServerManager.getSelectedLoginServer(), loginServerManager.selectedServer.getValue()); - } + private void assertLiveData() { + Assert.assertEquals(loginServerManager.getSelectedLoginServer(), loginServerManager.selectedServer.getValue()); + } } From a75405701581767f2a82ef7163c7c2141b8c7002 Mon Sep 17 00:00:00 2001 From: "Eric C. Johnson" Date: Mon, 2 Mar 2026 21:42:48 -0700 Subject: [PATCH 06/14] @W-21148602: Internal Server List does not respect changes made to Servers.xml file (Unit Tests For Add, Update And Remove Servers Via Resources) --- .../androidsdk/config/LoginServerManager.java | 43 ++- .../res/xml/servers_addition.xml | 7 + .../res/xml/servers_empty.xml | 2 + .../res/xml/servers_remove.xml | 5 + .../res/xml/servers_update.xml | 7 + .../auth/LoginServerManagerTest.java | 333 +++++++++++++++--- 6 files changed, 325 insertions(+), 72 deletions(-) create mode 100644 libs/test/SalesforceSDKTest/res/xml/servers_addition.xml create mode 100644 libs/test/SalesforceSDKTest/res/xml/servers_empty.xml create mode 100644 libs/test/SalesforceSDKTest/res/xml/servers_remove.xml create mode 100644 libs/test/SalesforceSDKTest/res/xml/servers_update.xml diff --git a/libs/SalesforceSDK/src/com/salesforce/androidsdk/config/LoginServerManager.java b/libs/SalesforceSDK/src/com/salesforce/androidsdk/config/LoginServerManager.java index b620d31f40..a54b8a64f3 100644 --- a/libs/SalesforceSDK/src/com/salesforce/androidsdk/config/LoginServerManager.java +++ b/libs/SalesforceSDK/src/com/salesforce/androidsdk/config/LoginServerManager.java @@ -41,6 +41,7 @@ import android.os.Looper; import androidx.annotation.NonNull; +import androidx.annotation.VisibleForTesting; import androidx.lifecycle.MutableLiveData; import com.salesforce.androidsdk.R; @@ -81,6 +82,9 @@ public class LoginServerManager { private final Context ctx; + /** The resource id of the servers.xml file */ + private final int serversXmlResourceId; + /** * Shared preferences when non-custom resources login servers are provided by servers.xml and associated custom login servers added by the user */ @@ -92,21 +96,12 @@ public class LoginServerManager { private final SharedPreferences runtimePrefs; /** - * Parameterized constructor. + * Constructs a new login server manager. * - * @param ctx Context. + * @param ctx The context */ - public LoginServerManager(Context ctx) { - this.ctx = ctx; - settings = ctx.getSharedPreferences(SERVER_URL_FILE, MODE_PRIVATE); - runtimePrefs = ctx.getSharedPreferences(RUNTIME_PREFS_FILE, MODE_PRIVATE); - - // (Re-)initialize non-custom servers provided by the servers.xml. - resetNonCustomLoginServers(settings); - initSharedPrefFile(); - - // Select a default login server. - getSelectedLoginServer(); + public LoginServerManager(final Context ctx) { + this(ctx, servers); } /** @@ -130,6 +125,26 @@ public LoginServer getLoginServerFromURL(String url) { return null; } + /** + * Constructs a new login server manager. + * + * @param ctx The context + */ + @VisibleForTesting + public LoginServerManager(final Context ctx, final int servers) { + this.ctx = ctx; + this.serversXmlResourceId = servers; + settings = ctx.getSharedPreferences(SERVER_URL_FILE, MODE_PRIVATE); + runtimePrefs = ctx.getSharedPreferences(RUNTIME_PREFS_FILE, MODE_PRIVATE); + + // (Re-)initialize non-custom servers provided by the servers.xml. + resetNonCustomLoginServers(settings); + initSharedPrefFile(); + + // Select a default login server. + getSelectedLoginServer(); + } + /** * Returns the selected login server. This will set a default login server if needed and ensure * the selected login server is available in the current list of login servers. @@ -518,7 +533,7 @@ private List getLoginServersFromXML() { List loginServers = null; if (servers != 0) { loginServers = new ArrayList<>(); - final XmlResourceParser xml = ctx.getResources().getXml(servers); + final XmlResourceParser xml = ctx.getResources().getXml(serversXmlResourceId); int eventType = -1; while (eventType != XmlResourceParser.END_DOCUMENT) { if (eventType == XmlResourceParser.START_TAG) { diff --git a/libs/test/SalesforceSDKTest/res/xml/servers_addition.xml b/libs/test/SalesforceSDKTest/res/xml/servers_addition.xml new file mode 100644 index 0000000000..c2543a84c0 --- /dev/null +++ b/libs/test/SalesforceSDKTest/res/xml/servers_addition.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/libs/test/SalesforceSDKTest/res/xml/servers_empty.xml b/libs/test/SalesforceSDKTest/res/xml/servers_empty.xml new file mode 100644 index 0000000000..9c66eb919b --- /dev/null +++ b/libs/test/SalesforceSDKTest/res/xml/servers_empty.xml @@ -0,0 +1,2 @@ + + diff --git a/libs/test/SalesforceSDKTest/res/xml/servers_remove.xml b/libs/test/SalesforceSDKTest/res/xml/servers_remove.xml new file mode 100644 index 0000000000..6f8b863faf --- /dev/null +++ b/libs/test/SalesforceSDKTest/res/xml/servers_remove.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/libs/test/SalesforceSDKTest/res/xml/servers_update.xml b/libs/test/SalesforceSDKTest/res/xml/servers_update.xml new file mode 100644 index 0000000000..37586b9b70 --- /dev/null +++ b/libs/test/SalesforceSDKTest/res/xml/servers_update.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/libs/test/SalesforceSDKTest/src/com/salesforce/androidsdk/auth/LoginServerManagerTest.java b/libs/test/SalesforceSDKTest/src/com/salesforce/androidsdk/auth/LoginServerManagerTest.java index 7f3a894d88..054d33e7a5 100644 --- a/libs/test/SalesforceSDKTest/src/com/salesforce/androidsdk/auth/LoginServerManagerTest.java +++ b/libs/test/SalesforceSDKTest/src/com/salesforce/androidsdk/auth/LoginServerManagerTest.java @@ -27,10 +27,17 @@ package com.salesforce.androidsdk.auth; import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; +import static com.salesforce.androidsdk.tests.R.xml.servers_addition; +import static com.salesforce.androidsdk.tests.R.xml.servers_empty; +import static com.salesforce.androidsdk.tests.R.xml.servers_remove; +import static com.salesforce.androidsdk.tests.R.xml.servers_update; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import androidx.arch.core.executor.testing.InstantTaskExecutorRule; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; +import androidx.test.platform.app.InstrumentationRegistry; import com.salesforce.androidsdk.config.LoginServerManager; import com.salesforce.androidsdk.config.LoginServerManager.LoginServer; @@ -47,17 +54,19 @@ /** * Tests for LoginServerManager. *

- * TODO: Each test should include a cold start. ECJ20260302 + * TODO: ✅Each test should include a cold start. ECJ20260302 *

- * TODO: Default servers from resources server.xml. ECJ20260302 + * TODO: ✅Default servers from legacy defaults. ECJ20260302 *

- * TODO: Add server from resources server.xml WITH NO custom servers. ECJ20260302 - * TODO: Update server from resources server.xml WITH NO custom servers. ECJ20260302 - * TODO: Remove server from resources server.xml WITH NO custom servers. ECJ20260302 + * TODO: ✅Default servers from resources server.xml. ECJ20260302 *

- * TODO: Add server from resources server.xml WITH custom servers. ECJ20260302 - * TODO: Update server from resources server.xml WITH custom servers. ECJ20260302 - * TODO: Remove server from resources server.xml WITH custom servers. ECJ20260302 + * TODO: ✅Add server from resources server.xml WITH NO custom servers. ECJ20260302 + * TODO: ✅Update server from resources server.xml WITH NO custom servers. ECJ20260302 + * TODO: ✅Remove server from resources server.xml WITH NO custom servers. ECJ20260302 + *

+ * TODO: ✅Add server from resources server.xml WITH custom servers. ECJ20260302 + * TODO: ✅Update server from resources server.xml WITH custom servers. ECJ20260302 + * TODO: ✅Remove server from resources server.xml WITH custom servers. ECJ20260302 *

* TODO: Default servers from runtime config. ECJ20260302 *

@@ -110,16 +119,224 @@ public void testGetLoginServerFromURL() { Assert.assertNull("Expected null", loginServerManager.getLoginServerFromURL("https://wrong.salesforce.com")); } + /** + * Test for testGetLegacyDefaultLoginServers. + */ + @Test + public void testGetLegacyDefaultLoginServers() { + loginServerManager = new LoginServerManager(getInstrumentation().getTargetContext(), servers_empty); + + final List servers = loginServerManager.getLoginServers(); + assertEquals("Wrong number of servers", 2, servers.size()); + assertProduction(servers.get(0)); + assertSandbox(servers.get(1)); + + assertProduction(loginServerManager.getSelectedLoginServer()); + } + /** * Test for getDefaultLoginServer. */ @Test public void testGetDefaultLoginServers() { final List servers = loginServerManager.getLoginServers(); - Assert.assertEquals("Wrong number of servers", 3, servers.size()); + assertEquals("Wrong number of servers", 3, servers.size()); + assertProduction(servers.get(0)); + assertSandbox(servers.get(1)); + assertOther(servers.get(2)); + + assertProduction(loginServerManager.getSelectedLoginServer()); + } + + /** + * Test for testAddDefaultLoginServers. + */ + @Test + public void testAddDefaultLoginServers() { + + List servers = loginServerManager.getLoginServers(); + assertEquals("Wrong number of servers", 3, servers.size()); assertProduction(servers.get(0)); assertSandbox(servers.get(1)); assertOther(servers.get(2)); + + assertProduction(loginServerManager.getSelectedLoginServer()); + + loginServerManager = new LoginServerManager(InstrumentationRegistry.getInstrumentation().getTargetContext(), servers_addition); + + servers = loginServerManager.getLoginServers(); + assertEquals("Wrong number of servers", 4, servers.size()); + assertProduction(servers.get(0)); + assertSandbox(servers.get(1)); + assertEquals("Added", servers.get(2).name); + assertEquals("https://added.salesforce.com", servers.get(2).url); + assertFalse(servers.get(2).isCustom); + assertOther(servers.get(3)); + + assertProduction(loginServerManager.getSelectedLoginServer()); + } + + /** + * Test for testUpdateDefaultLoginServers. + */ + @Test + public void testUpdateDefaultLoginServers() { + + loginServerManager = new LoginServerManager(InstrumentationRegistry.getInstrumentation().getTargetContext(), servers_addition); + List servers = loginServerManager.getLoginServers(); + assertEquals("Wrong number of servers", 4, servers.size()); + assertProduction(servers.get(0)); + assertSandbox(servers.get(1)); + assertEquals("Added", servers.get(2).name); + assertEquals("https://added.salesforce.com", servers.get(2).url); + assertFalse(servers.get(2).isCustom); + assertOther(servers.get(3)); + + assertProduction(loginServerManager.getSelectedLoginServer()); + + loginServerManager = new LoginServerManager(InstrumentationRegistry.getInstrumentation().getTargetContext(), servers_update); + + servers = loginServerManager.getLoginServers(); + assertEquals("Wrong number of servers", 4, servers.size()); + assertProduction(servers.get(0)); + assertEquals("Updated", servers.get(1).name); + assertEquals("https://updated.salesforce.com", servers.get(1).url); + assertFalse(servers.get(1).isCustom); + assertSandbox(servers.get(2)); + assertOther(servers.get(3)); + + assertProduction(loginServerManager.getSelectedLoginServer()); + } + + /** + * Test for testRemoveDefaultLoginServers. + */ + @Test + public void testRemoveDefaultLoginServers() { + + loginServerManager = new LoginServerManager(InstrumentationRegistry.getInstrumentation().getTargetContext(), servers_update); + List servers = loginServerManager.getLoginServers(); + assertEquals("Wrong number of servers", 4, servers.size()); + assertProduction(servers.get(0)); + assertEquals("Updated", servers.get(1).name); + assertEquals("https://updated.salesforce.com", servers.get(1).url); + assertFalse(servers.get(1).isCustom); + assertSandbox(servers.get(2)); + assertOther(servers.get(3)); + + assertProduction(loginServerManager.getSelectedLoginServer()); + + loginServerManager = new LoginServerManager(InstrumentationRegistry.getInstrumentation().getTargetContext(), servers_remove); + + servers = loginServerManager.getLoginServers(); + assertEquals("Wrong number of servers", 2, servers.size()); + assertProduction(servers.get(0)); + assertOther(servers.get(1)); + + assertProduction(loginServerManager.getSelectedLoginServer()); + } + + /** + * Test for testAddDefaultLoginServersWithCustomServers. + */ + @Test + public void testAddDefaultLoginServersWithCustomServers() { + + List servers = loginServerManager.getLoginServers(); + assertEquals("Wrong number of servers", 3, servers.size()); + assertProduction(servers.get(0)); + assertSandbox(servers.get(1)); + assertOther(servers.get(2)); + + assertProduction(loginServerManager.getSelectedLoginServer()); + + loginServerManager.addCustomLoginServer(CUSTOM_NAME, CUSTOM_URL); + assertCustom(loginServerManager.getLoginServers().getLast()); + assertCustom(loginServerManager.getSelectedLoginServer()); + + loginServerManager = new LoginServerManager(InstrumentationRegistry.getInstrumentation().getTargetContext(), servers_addition); + + servers = loginServerManager.getLoginServers(); + assertEquals("Wrong number of servers", 5, servers.size()); + assertProduction(servers.get(0)); + assertSandbox(servers.get(1)); + assertEquals("Added", servers.get(2).name); + assertEquals("https://added.salesforce.com", servers.get(2).url); + assertFalse(servers.get(2).isCustom); + assertOther(servers.get(3)); + + assertCustom(loginServerManager.getLoginServers().getLast()); + assertCustom(loginServerManager.getSelectedLoginServer()); + } + + /** + * Test for testUpdateDefaultLoginServersWithCustomServers. + */ + @Test + public void testUpdateDefaultLoginServersWithCustomServers() { + + loginServerManager = new LoginServerManager(InstrumentationRegistry.getInstrumentation().getTargetContext(), servers_addition); + List servers = loginServerManager.getLoginServers(); + assertEquals("Wrong number of servers", 4, servers.size()); + assertProduction(servers.get(0)); + assertSandbox(servers.get(1)); + assertEquals("Added", servers.get(2).name); + assertEquals("https://added.salesforce.com", servers.get(2).url); + assertFalse(servers.get(2).isCustom); + assertOther(servers.get(3)); + + assertProduction(loginServerManager.getSelectedLoginServer()); + + loginServerManager.addCustomLoginServer(CUSTOM_NAME, CUSTOM_URL); + assertCustom(loginServerManager.getLoginServers().getLast()); + assertCustom(loginServerManager.getSelectedLoginServer()); + + loginServerManager = new LoginServerManager(InstrumentationRegistry.getInstrumentation().getTargetContext(), servers_update); + + servers = loginServerManager.getLoginServers(); + assertEquals("Wrong number of servers", 5, servers.size()); + assertProduction(servers.get(0)); + assertEquals("Updated", servers.get(1).name); + assertEquals("https://updated.salesforce.com", servers.get(1).url); + assertFalse(servers.get(1).isCustom); + assertSandbox(servers.get(2)); + assertOther(servers.get(3)); + + assertCustom(loginServerManager.getLoginServers().getLast()); + assertCustom(loginServerManager.getSelectedLoginServer()); + } + + /** + * Test for testRemoveDefaultLoginServersWithCustomServers. + */ + @Test + public void testRemoveDefaultLoginServersWithCustomServers() { + + loginServerManager = new LoginServerManager(InstrumentationRegistry.getInstrumentation().getTargetContext(), servers_update); + List servers = loginServerManager.getLoginServers(); + assertEquals("Wrong number of servers", 4, servers.size()); + assertProduction(servers.get(0)); + assertEquals("Updated", servers.get(1).name); + assertEquals("https://updated.salesforce.com", servers.get(1).url); + assertFalse(servers.get(1).isCustom); + assertSandbox(servers.get(2)); + assertOther(servers.get(3)); + + assertProduction(loginServerManager.getSelectedLoginServer()); + + loginServerManager.addCustomLoginServer(CUSTOM_NAME, CUSTOM_URL); + assertCustom(loginServerManager.getLoginServers().getLast()); + assertCustom(loginServerManager.getSelectedLoginServer()); + + loginServerManager = new LoginServerManager(InstrumentationRegistry.getInstrumentation().getTargetContext(), servers_remove); + + servers = loginServerManager.getLoginServers(); + assertEquals("Wrong number of servers", 3, servers.size()); + assertProduction(servers.get(0)); + assertOther(servers.get(1)); + + assertCustom(loginServerManager.getLoginServers().getLast()); + assertCustom(loginServerManager.getSelectedLoginServer()); } /** @@ -169,17 +386,17 @@ public void testAddMultipleCustomServers() { // Starting point, only 3 servers. List servers = loginServerManager.getLoginServers(); - Assert.assertEquals("Expected no custom login servers", 3, servers.size()); + assertEquals("Expected no custom login servers", 3, servers.size()); // Adding first custom server. loginServerManager.addCustomLoginServer(CUSTOM_NAME, CUSTOM_URL); servers = loginServerManager.getLoginServers(); - Assert.assertEquals("Expected one custom login server", 4, servers.size()); + assertEquals("Expected one custom login server", 4, servers.size()); // Adding second custom server. loginServerManager.addCustomLoginServer(CUSTOM_NAME_2, CUSTOM_URL_2); servers = loginServerManager.getLoginServers(); - Assert.assertEquals("Expected one custom login server", 5, servers.size()); + assertEquals("Expected one custom login server", 5, servers.size()); } /** @@ -222,12 +439,12 @@ public void testReset() { // Starting point, only 3 servers. List servers = loginServerManager.getLoginServers(); - Assert.assertEquals("Expected no custom login servers", 3, servers.size()); + assertEquals("Expected no custom login servers", 3, servers.size()); // Adding custom server. loginServerManager.addCustomLoginServer(CUSTOM_NAME, CUSTOM_URL); servers = loginServerManager.getLoginServers(); - Assert.assertEquals("Expected one custom login server", 4, servers.size()); + assertEquals("Expected one custom login server", 4, servers.size()); // Selecting sandbox. loginServerManager.useSandbox(); @@ -239,7 +456,7 @@ public void testReset() { */ loginServerManager.reset(); servers = loginServerManager.getLoginServers(); - Assert.assertEquals("Expected no custom login servers", 3, servers.size()); + assertEquals("Expected no custom login servers", 3, servers.size()); assertProduction(loginServerManager.getSelectedLoginServer()); } @@ -266,14 +483,14 @@ public void testRemoveServer() { loginServerManager.addCustomLoginServer(CUSTOM_NAME, CUSTOM_URL); int originalServerSize = 4; // 3 default servers + 1 custom List servers = loginServerManager.getLoginServers(); - Assert.assertEquals("Expected one custom login server", originalServerSize, servers.size()); + assertEquals("Expected one custom login server", originalServerSize, servers.size()); LoginServer lastServer = servers.get(3); // Remove loginServerManager.removeServer(lastServer); servers = loginServerManager.getLoginServers(); - Assert.assertEquals("", (originalServerSize - 1), servers.size()); - Assert.assertFalse("List should not contain removed server.", servers.contains(lastServer)); + assertEquals("", (originalServerSize - 1), servers.size()); + assertFalse("List should not contain removed server.", servers.contains(lastServer)); } /** @@ -285,14 +502,14 @@ public void testRemoveReordersServers() { loginServerManager.addCustomLoginServer(CUSTOM_NAME_2, CUSTOM_URL_2); int originalServerSize = 5; // 3 default servers + 2 custom List servers = loginServerManager.getLoginServers(); - Assert.assertEquals("Expected one custom login server", originalServerSize, servers.size()); + assertEquals("Expected one custom login server", originalServerSize, servers.size()); LoginServer serverToDelete = servers.get(3); // Remove loginServerManager.removeServer(serverToDelete); servers = loginServerManager.getLoginServers(); - Assert.assertEquals("No servers removed.", (originalServerSize - 1), servers.size()); - Assert.assertFalse("List should not contain removed server.", servers.contains(serverToDelete)); + assertEquals("No servers removed.", (originalServerSize - 1), servers.size()); + assertFalse("List should not contain removed server.", servers.contains(serverToDelete)); // Assert Reorder assertProduction(servers.get(0)); @@ -308,13 +525,13 @@ public void testRemoveReordersServers() { public void testRemoveNonCustomServer() { int originalServerSize = 3; // 3 default servers List servers = loginServerManager.getLoginServers(); - Assert.assertEquals("Expected one custom login server", originalServerSize, servers.size()); + assertEquals("Expected one custom login server", originalServerSize, servers.size()); LoginServer serverToDelete = servers.get(0); // Remove loginServerManager.removeServer(serverToDelete); servers = loginServerManager.getLoginServers(); - Assert.assertEquals("Servers should not be removed.", originalServerSize, servers.size()); + assertEquals("Servers should not be removed.", originalServerSize, servers.size()); } /** @@ -324,28 +541,28 @@ public void testRemoveNonCustomServer() { public void testAddingDuplicateServers() { int originalServerSize = 3; // 3 default servers List servers = loginServerManager.getLoginServers(); - Assert.assertEquals("Expected one custom login server", originalServerSize, servers.size()); + assertEquals("Expected one custom login server", originalServerSize, servers.size()); LoginServer prodServer = loginServerManager.getLoginServerFromURL(PRODUCTION_URL); // Attempt to add a default server as a custom server. loginServerManager.addCustomLoginServer(prodServer.name, prodServer.url); - Assert.assertEquals("Duplicate server should not be added.", originalServerSize, + assertEquals("Duplicate server should not be added.", originalServerSize, loginServerManager.getLoginServers().size()); // Attempt to add a duplicate custom server. loginServerManager.addCustomLoginServer(CUSTOM_NAME, CUSTOM_URL); - Assert.assertEquals("Custom server should be added.", (originalServerSize + 1), + assertEquals("Custom server should be added.", (originalServerSize + 1), loginServerManager.getLoginServers().size()); loginServerManager.addCustomLoginServer(CUSTOM_NAME, CUSTOM_URL); - Assert.assertEquals("Duplicate custom server should not be added.", (originalServerSize + 1), + assertEquals("Duplicate custom server should not be added.", (originalServerSize + 1), loginServerManager.getLoginServers().size()); // Ensure servers with duplicate names but unique URLs are allowed. loginServerManager.addCustomLoginServer(CUSTOM_NAME, CUSTOM_URL_2); - Assert.assertEquals("Custom server should be added.", (originalServerSize + 2), + assertEquals("Custom server should be added.", (originalServerSize + 2), loginServerManager.getLoginServers().size()); loginServerManager.addCustomLoginServer(prodServer.name, "https://custom3.com"); - Assert.assertEquals("Custom server should be added..", (originalServerSize + 3), + assertEquals("Custom server should be added..", (originalServerSize + 3), loginServerManager.getLoginServers().size()); } @@ -386,8 +603,8 @@ public void testReplaceAndReOrderCustomLoginServer() { ); // Verify the original and other custom login servers are not present. - Assert.assertFalse(loginServerManager.getLoginServers().contains(originalCustomLoginServer)); - Assert.assertFalse(loginServerManager.getLoginServers().contains(otherCustomLoginServer)); + assertFalse(loginServerManager.getLoginServers().contains(originalCustomLoginServer)); + assertFalse(loginServerManager.getLoginServers().contains(otherCustomLoginServer)); // Add the original and other custom login server. @@ -395,8 +612,8 @@ public void testReplaceAndReOrderCustomLoginServer() { loginServerManager.addCustomLoginServer(otherName, otherUrl); // Verify the original and other custom login servers were added. - Assert.assertEquals(originalCustomLoginServer, loginServerManager.getLoginServers().get(loginServerManager.getLoginServers().size() - 2)); - Assert.assertEquals(otherCustomLoginServer, loginServerManager.getLoginServers().get(loginServerManager.getLoginServers().size() - 1)); + assertEquals(originalCustomLoginServer, loginServerManager.getLoginServers().get(loginServerManager.getLoginServers().size() - 2)); + assertEquals(otherCustomLoginServer, loginServerManager.getLoginServers().get(loginServerManager.getLoginServers().size() - 1)); // Prepare for negative tests. @@ -408,38 +625,38 @@ public void testReplaceAndReOrderCustomLoginServer() { // Attempt the prohibited replacement of a non-custom login server where the original matches. loginServerManager.replaceCustomLoginServer(production, productionReplacement); Assert.assertTrue(loginServerManager.getLoginServers().contains(production)); - Assert.assertFalse(loginServerManager.getLoginServers().contains(productionReplacement)); + assertFalse(loginServerManager.getLoginServers().contains(productionReplacement)); // Attempt the prohibited replacement of a non-custom login server where the original doesn't exit. loginServerManager.replaceCustomLoginServer(productionMismatch, productionReplacementMismatch); Assert.assertTrue(loginServerManager.getLoginServers().contains(production)); - Assert.assertFalse(loginServerManager.getLoginServers().contains(productionReplacement)); + assertFalse(loginServerManager.getLoginServers().contains(productionReplacement)); // Attempt the prohibited reordering of a non-custom login server. loginServerManager.reorderCustomLoginServer(0, 1); - Assert.assertEquals(loginServerManager.getLoginServers().get(0), production); + assertEquals(loginServerManager.getLoginServers().get(0), production); // Replace the original custom login server with a non-custom server. loginServerManager.replaceCustomLoginServer(originalCustomLoginServer, nonCustomLoginServer); // Verify the original and other custom login servers weren't changed. - Assert.assertFalse(loginServerManager.getLoginServers().contains(nonCustomLoginServer)); - Assert.assertEquals(originalCustomLoginServer, loginServerManager.getLoginServers().get(loginServerManager.getLoginServers().size() - 2)); - Assert.assertEquals(otherCustomLoginServer, loginServerManager.getLoginServers().get(loginServerManager.getLoginServers().size() - 1)); + assertFalse(loginServerManager.getLoginServers().contains(nonCustomLoginServer)); + assertEquals(originalCustomLoginServer, loginServerManager.getLoginServers().get(loginServerManager.getLoginServers().size() - 2)); + assertEquals(otherCustomLoginServer, loginServerManager.getLoginServers().get(loginServerManager.getLoginServers().size() - 1)); // Replace the original custom login server. loginServerManager.replaceCustomLoginServer(originalCustomLoginServer, updatedCustomLoginServer); // Verify the original custom login server is not present. - Assert.assertFalse(loginServerManager.getLoginServers().contains(originalCustomLoginServer)); + assertFalse(loginServerManager.getLoginServers().contains(originalCustomLoginServer)); // Verify the updated and other custom login servers are present. - Assert.assertEquals(updatedCustomLoginServer, loginServerManager.getLoginServers().get(loginServerManager.getLoginServers().size() - 2)); - Assert.assertEquals(otherCustomLoginServer, loginServerManager.getLoginServers().get(loginServerManager.getLoginServers().size() - 1)); + assertEquals(updatedCustomLoginServer, loginServerManager.getLoginServers().get(loginServerManager.getLoginServers().size() - 2)); + assertEquals(otherCustomLoginServer, loginServerManager.getLoginServers().get(loginServerManager.getLoginServers().size() - 1)); // Attempt to move the updated custom login server above the non-custom login servers. loginServerManager.reorderCustomLoginServer(loginServerManager.getLoginServers().indexOf(updatedCustomLoginServer), 0); @@ -453,7 +670,7 @@ public void testReplaceAndReOrderCustomLoginServer() { lastNonCustomIndex = i; } } - Assert.assertEquals(loginServers.get(lastNonCustomIndex + 1), updatedCustomLoginServer); + assertEquals(loginServers.get(lastNonCustomIndex + 1), updatedCustomLoginServer); // Attempt to move the updated custom login server one greater than the upper bounds of the login servers list. @@ -466,40 +683,40 @@ public void testReplaceAndReOrderCustomLoginServer() { loginServerManager.reorderCustomLoginServer(loginServerManager.getLoginServers().indexOf(updatedCustomLoginServer), loginServerManager.getLoginServers().size() - 1); // Verify the updated custom login server is now the last login server in the list. - Assert.assertEquals(loginServerManager.getLoginServers().getLast(), updatedCustomLoginServer); + assertEquals(loginServerManager.getLoginServers().getLast(), updatedCustomLoginServer); } private void assertProduction(LoginServer server) { - Assert.assertEquals("Expected production's name", "Production", server.name); - Assert.assertEquals("Expected production's url", PRODUCTION_URL, server.url); - Assert.assertFalse("Expected production to be marked as not custom", server.isCustom); + assertEquals("Expected production's name", "Production", server.name); + assertEquals("Expected production's url", PRODUCTION_URL, server.url); + assertFalse("Expected production to be marked as not custom", server.isCustom); } private void assertSandbox(LoginServer server) { - Assert.assertEquals("Expected sandbox's name", "Sandbox", server.name); - Assert.assertEquals("Expected sandbox's url", SANDBOX_URL, server.url); - Assert.assertFalse("Expected sandbox to be marked as not custom", server.isCustom); + assertEquals("Expected sandbox's name", "Sandbox", server.name); + assertEquals("Expected sandbox's url", SANDBOX_URL, server.url); + assertFalse("Expected sandbox to be marked as not custom", server.isCustom); } private void assertOther(LoginServer server) { - Assert.assertEquals("Expected other's name", "Other", server.name); - Assert.assertEquals("Expected other's url", OTHER_URL, server.url); - Assert.assertFalse("Expected other to be marked as not custom", server.isCustom); + assertEquals("Expected other's name", "Other", server.name); + assertEquals("Expected other's url", OTHER_URL, server.url); + assertFalse("Expected other to be marked as not custom", server.isCustom); } private void assertCustom(LoginServer server) { - Assert.assertEquals("Expected custom's name", CUSTOM_NAME, server.name); - Assert.assertEquals("Expected custom's url", CUSTOM_URL, server.url); + assertEquals("Expected custom's name", CUSTOM_NAME, server.name); + assertEquals("Expected custom's url", CUSTOM_URL, server.url); Assert.assertTrue("Expected custom to be marked as not custom", server.isCustom); } private void assertCustom2(LoginServer server) { - Assert.assertEquals("Expected custom2's name", CUSTOM_NAME_2, server.name); - Assert.assertEquals("Expected custom2's url", CUSTOM_URL_2, server.url); + assertEquals("Expected custom2's name", CUSTOM_NAME_2, server.name); + assertEquals("Expected custom2's url", CUSTOM_URL_2, server.url); Assert.assertTrue("Expected custom2 to be marked as not custom", server.isCustom); } private void assertLiveData() { - Assert.assertEquals(loginServerManager.getSelectedLoginServer(), loginServerManager.selectedServer.getValue()); + assertEquals(loginServerManager.getSelectedLoginServer(), loginServerManager.selectedServer.getValue()); } } From 7627a7563e83cf45ed64afeea30757ac8230d395 Mon Sep 17 00:00:00 2001 From: "Eric C. Johnson" Date: Tue, 3 Mar 2026 11:16:15 -0700 Subject: [PATCH 07/14] @W-21148602: Internal Server List does not respect changes made to Servers.xml file (Unit Tests For Add, Update And Remove Servers Via Runtime Config Mobile Device Management) --- .../androidsdk/config/LoginServerManager.java | 80 ++++-- .../auth/LoginServerManagerTest.java | 92 ++++--- .../androidsdk/auth/LoginServerManagerTest.kt | 240 ++++++++++++++++++ 3 files changed, 343 insertions(+), 69 deletions(-) create mode 100644 libs/test/SalesforceSDKTest/src/com/salesforce/androidsdk/auth/LoginServerManagerTest.kt diff --git a/libs/SalesforceSDK/src/com/salesforce/androidsdk/config/LoginServerManager.java b/libs/SalesforceSDK/src/com/salesforce/androidsdk/config/LoginServerManager.java index a54b8a64f3..613f96d69a 100644 --- a/libs/SalesforceSDK/src/com/salesforce/androidsdk/config/LoginServerManager.java +++ b/libs/SalesforceSDK/src/com/salesforce/androidsdk/config/LoginServerManager.java @@ -71,17 +71,39 @@ public class LoginServerManager { public static final String WELCOME_LOGIN_URL = "https://welcome.salesforce.com/discovery"; public static final String SANDBOX_LOGIN_URL = "https://test.salesforce.com"; - // Keys used in shared preferences. - private static final String SERVER_URL_FILE = "server_url_file"; - private static final String RUNTIME_PREFS_FILE = "runtime_prefs_file"; + /** + * Shared preferences when non-custom login servers are provided by resources servers.xml + */ + @VisibleForTesting + public static final String SERVER_URL_FILE = "server_url_file"; + + /** + * Shared preferences when non-custom login servers are provided by runtime config Mobile Device Management (MDM) + */ + @VisibleForTesting + public static final String RUNTIME_PREFS_FILE = "runtime_prefs_file"; + private static final String NUMBER_OF_ENTRIES = "number_of_entries"; private static final String SERVER_NAME = "server_name_%d"; private static final String SERVER_URL = "server_url_%d"; private static final String IS_CUSTOM = "is_custom_%d"; - private static final String SERVER_SELECTION_FILE = "server_selection_file"; + /** + * Shared preferences for the selected login server + */ + @VisibleForTesting + public static final String SERVER_SELECTION_FILE = "server_selection_file"; + + /** + * The Android context used for shared preferences access + */ private final Context ctx; + /** + * The Android runtime configuration used for Mobile Device Management (MDM) + */ + private final RuntimeConfig runtimeConfig; + /** The resource id of the servers.xml file */ private final int serversXmlResourceId; @@ -101,7 +123,32 @@ public class LoginServerManager { * @param ctx The context */ public LoginServerManager(final Context ctx) { - this(ctx, servers); + this(ctx, getRuntimeConfig(ctx), servers); + } + + /** + * Constructs a new login server manager. + * + * @param ctx The context + */ + @VisibleForTesting + public LoginServerManager( + final Context ctx, + final RuntimeConfig runtimeConfig, + final int serversXmlResourceId + ) { + this.ctx = ctx; + this.runtimeConfig = runtimeConfig; + this.serversXmlResourceId = serversXmlResourceId; + settings = ctx.getSharedPreferences(SERVER_URL_FILE, MODE_PRIVATE); + runtimePrefs = ctx.getSharedPreferences(RUNTIME_PREFS_FILE, MODE_PRIVATE); + + // (Re-)initialize non-custom login servers provided by the resources servers.xml. + resetNonCustomLoginServers(settings); + initSharedPrefFile(); + + // Select a default login server. + getSelectedLoginServer(); } /** @@ -125,26 +172,6 @@ public LoginServer getLoginServerFromURL(String url) { return null; } - /** - * Constructs a new login server manager. - * - * @param ctx The context - */ - @VisibleForTesting - public LoginServerManager(final Context ctx, final int servers) { - this.ctx = ctx; - this.serversXmlResourceId = servers; - settings = ctx.getSharedPreferences(SERVER_URL_FILE, MODE_PRIVATE); - runtimePrefs = ctx.getSharedPreferences(RUNTIME_PREFS_FILE, MODE_PRIVATE); - - // (Re-)initialize non-custom servers provided by the servers.xml. - resetNonCustomLoginServers(settings); - initSharedPrefFile(); - - // Select a default login server. - getSelectedLoginServer(); - } - /** * Returns the selected login server. This will set a default login server if needed and ensure * the selected login server is available in the current list of login servers. @@ -343,7 +370,7 @@ private SharedPreferences getSharedPreferences() { */ private boolean isRuntimeConfigAppServiceHostsSet() { try { - return getRuntimeConfig(ctx).getStringArrayStoredAsArrayOrCSV(AppServiceHosts) != null; + return runtimeConfig.getStringArrayStoredAsArrayOrCSV(AppServiceHosts) != null; } catch (Exception e) { return false; } @@ -355,7 +382,6 @@ private boolean isRuntimeConfigAppServiceHostsSet() { */ @SuppressWarnings("UnusedReturnValue") public List getLoginServersFromRuntimeConfig() { - final RuntimeConfig runtimeConfig = getRuntimeConfig(ctx); String[] mdmLoginServers = null; try { mdmLoginServers = runtimeConfig.getStringArrayStoredAsArrayOrCSV(AppServiceHosts); diff --git a/libs/test/SalesforceSDKTest/src/com/salesforce/androidsdk/auth/LoginServerManagerTest.java b/libs/test/SalesforceSDKTest/src/com/salesforce/androidsdk/auth/LoginServerManagerTest.java index 054d33e7a5..d129fb4a8e 100644 --- a/libs/test/SalesforceSDKTest/src/com/salesforce/androidsdk/auth/LoginServerManagerTest.java +++ b/libs/test/SalesforceSDKTest/src/com/salesforce/androidsdk/auth/LoginServerManagerTest.java @@ -27,6 +27,7 @@ package com.salesforce.androidsdk.auth; import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; +import static com.salesforce.androidsdk.config.RuntimeConfig.getRuntimeConfig; import static com.salesforce.androidsdk.tests.R.xml.servers_addition; import static com.salesforce.androidsdk.tests.R.xml.servers_empty; import static com.salesforce.androidsdk.tests.R.xml.servers_remove; @@ -37,7 +38,6 @@ import androidx.arch.core.executor.testing.InstantTaskExecutorRule; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; -import androidx.test.platform.app.InstrumentationRegistry; import com.salesforce.androidsdk.config.LoginServerManager; import com.salesforce.androidsdk.config.LoginServerManager.LoginServer; @@ -53,31 +53,6 @@ /** * Tests for LoginServerManager. - *

- * TODO: ✅Each test should include a cold start. ECJ20260302 - *

- * TODO: ✅Default servers from legacy defaults. ECJ20260302 - *

- * TODO: ✅Default servers from resources server.xml. ECJ20260302 - *

- * TODO: ✅Add server from resources server.xml WITH NO custom servers. ECJ20260302 - * TODO: ✅Update server from resources server.xml WITH NO custom servers. ECJ20260302 - * TODO: ✅Remove server from resources server.xml WITH NO custom servers. ECJ20260302 - *

- * TODO: ✅Add server from resources server.xml WITH custom servers. ECJ20260302 - * TODO: ✅Update server from resources server.xml WITH custom servers. ECJ20260302 - * TODO: ✅Remove server from resources server.xml WITH custom servers. ECJ20260302 - *

- * TODO: Default servers from runtime config. ECJ20260302 - *

- * TODO: Add server from runtime config WITH NO custom servers. ECJ20260302 - * TODO: Update server from runtime config WITH NO custom servers. ECJ20260302 - * TODO: Remove server from runtime config WITH NO custom servers. ECJ20260302 - *

- * TODO: Add server from runtime config WITH custom servers. ECJ20260302 - * TODO: Update server from runtime config WITH custom servers. ECJ20260302 - * TODO: Remove server from runtime config WITH custom servers. ECJ20260302 - * TODO: ECJ20260302 */ @RunWith(AndroidJUnit4.class) @SmallTest @@ -124,7 +99,10 @@ public void testGetLoginServerFromURL() { */ @Test public void testGetLegacyDefaultLoginServers() { - loginServerManager = new LoginServerManager(getInstrumentation().getTargetContext(), servers_empty); + loginServerManager = new LoginServerManager( + getInstrumentation().getTargetContext(), + getRuntimeConfig(getInstrumentation().getTargetContext()), + servers_empty); final List servers = loginServerManager.getLoginServers(); assertEquals("Wrong number of servers", 2, servers.size()); @@ -162,7 +140,10 @@ public void testAddDefaultLoginServers() { assertProduction(loginServerManager.getSelectedLoginServer()); - loginServerManager = new LoginServerManager(InstrumentationRegistry.getInstrumentation().getTargetContext(), servers_addition); + loginServerManager = new LoginServerManager( + getInstrumentation().getTargetContext(), + getRuntimeConfig(getInstrumentation().getTargetContext()), + servers_addition); servers = loginServerManager.getLoginServers(); assertEquals("Wrong number of servers", 4, servers.size()); @@ -182,7 +163,10 @@ public void testAddDefaultLoginServers() { @Test public void testUpdateDefaultLoginServers() { - loginServerManager = new LoginServerManager(InstrumentationRegistry.getInstrumentation().getTargetContext(), servers_addition); + loginServerManager = new LoginServerManager( + getInstrumentation().getTargetContext(), + getRuntimeConfig(getInstrumentation().getTargetContext()), + servers_addition); List servers = loginServerManager.getLoginServers(); assertEquals("Wrong number of servers", 4, servers.size()); assertProduction(servers.get(0)); @@ -194,7 +178,10 @@ public void testUpdateDefaultLoginServers() { assertProduction(loginServerManager.getSelectedLoginServer()); - loginServerManager = new LoginServerManager(InstrumentationRegistry.getInstrumentation().getTargetContext(), servers_update); + loginServerManager = new LoginServerManager( + getInstrumentation().getTargetContext(), + getRuntimeConfig(getInstrumentation().getTargetContext()), + servers_update); servers = loginServerManager.getLoginServers(); assertEquals("Wrong number of servers", 4, servers.size()); @@ -214,7 +201,10 @@ public void testUpdateDefaultLoginServers() { @Test public void testRemoveDefaultLoginServers() { - loginServerManager = new LoginServerManager(InstrumentationRegistry.getInstrumentation().getTargetContext(), servers_update); + loginServerManager = new LoginServerManager( + getInstrumentation().getTargetContext(), + getRuntimeConfig(getInstrumentation().getTargetContext()), + servers_update); List servers = loginServerManager.getLoginServers(); assertEquals("Wrong number of servers", 4, servers.size()); assertProduction(servers.get(0)); @@ -226,7 +216,10 @@ public void testRemoveDefaultLoginServers() { assertProduction(loginServerManager.getSelectedLoginServer()); - loginServerManager = new LoginServerManager(InstrumentationRegistry.getInstrumentation().getTargetContext(), servers_remove); + loginServerManager = new LoginServerManager( + getInstrumentation().getTargetContext(), + getRuntimeConfig(getInstrumentation().getTargetContext()), + servers_remove); servers = loginServerManager.getLoginServers(); assertEquals("Wrong number of servers", 2, servers.size()); @@ -254,7 +247,10 @@ public void testAddDefaultLoginServersWithCustomServers() { assertCustom(loginServerManager.getLoginServers().getLast()); assertCustom(loginServerManager.getSelectedLoginServer()); - loginServerManager = new LoginServerManager(InstrumentationRegistry.getInstrumentation().getTargetContext(), servers_addition); + loginServerManager = new LoginServerManager( + getInstrumentation().getTargetContext(), + getRuntimeConfig(getInstrumentation().getTargetContext()), + servers_addition); servers = loginServerManager.getLoginServers(); assertEquals("Wrong number of servers", 5, servers.size()); @@ -275,7 +271,10 @@ public void testAddDefaultLoginServersWithCustomServers() { @Test public void testUpdateDefaultLoginServersWithCustomServers() { - loginServerManager = new LoginServerManager(InstrumentationRegistry.getInstrumentation().getTargetContext(), servers_addition); + loginServerManager = new LoginServerManager( + getInstrumentation().getTargetContext(), + getRuntimeConfig(getInstrumentation().getTargetContext()), + servers_addition); List servers = loginServerManager.getLoginServers(); assertEquals("Wrong number of servers", 4, servers.size()); assertProduction(servers.get(0)); @@ -291,7 +290,10 @@ public void testUpdateDefaultLoginServersWithCustomServers() { assertCustom(loginServerManager.getLoginServers().getLast()); assertCustom(loginServerManager.getSelectedLoginServer()); - loginServerManager = new LoginServerManager(InstrumentationRegistry.getInstrumentation().getTargetContext(), servers_update); + loginServerManager = new LoginServerManager( + getInstrumentation().getTargetContext(), + getRuntimeConfig(getInstrumentation().getTargetContext()), + servers_update); servers = loginServerManager.getLoginServers(); assertEquals("Wrong number of servers", 5, servers.size()); @@ -312,7 +314,10 @@ public void testUpdateDefaultLoginServersWithCustomServers() { @Test public void testRemoveDefaultLoginServersWithCustomServers() { - loginServerManager = new LoginServerManager(InstrumentationRegistry.getInstrumentation().getTargetContext(), servers_update); + loginServerManager = new LoginServerManager( + getInstrumentation().getTargetContext(), + getRuntimeConfig(getInstrumentation().getTargetContext()), + servers_update); List servers = loginServerManager.getLoginServers(); assertEquals("Wrong number of servers", 4, servers.size()); assertProduction(servers.get(0)); @@ -328,7 +333,10 @@ public void testRemoveDefaultLoginServersWithCustomServers() { assertCustom(loginServerManager.getLoginServers().getLast()); assertCustom(loginServerManager.getSelectedLoginServer()); - loginServerManager = new LoginServerManager(InstrumentationRegistry.getInstrumentation().getTargetContext(), servers_remove); + loginServerManager = new LoginServerManager( + getInstrumentation().getTargetContext(), + getRuntimeConfig(getInstrumentation().getTargetContext()), + servers_remove); servers = loginServerManager.getLoginServers(); assertEquals("Wrong number of servers", 3, servers.size()); @@ -686,31 +694,31 @@ public void testReplaceAndReOrderCustomLoginServer() { assertEquals(loginServerManager.getLoginServers().getLast(), updatedCustomLoginServer); } - private void assertProduction(LoginServer server) { + public static void assertProduction(LoginServer server) { assertEquals("Expected production's name", "Production", server.name); assertEquals("Expected production's url", PRODUCTION_URL, server.url); assertFalse("Expected production to be marked as not custom", server.isCustom); } - private void assertSandbox(LoginServer server) { + public static void assertSandbox(LoginServer server) { assertEquals("Expected sandbox's name", "Sandbox", server.name); assertEquals("Expected sandbox's url", SANDBOX_URL, server.url); assertFalse("Expected sandbox to be marked as not custom", server.isCustom); } - private void assertOther(LoginServer server) { + public static void assertOther(LoginServer server) { assertEquals("Expected other's name", "Other", server.name); assertEquals("Expected other's url", OTHER_URL, server.url); assertFalse("Expected other to be marked as not custom", server.isCustom); } - private void assertCustom(LoginServer server) { + public static void assertCustom(LoginServer server) { assertEquals("Expected custom's name", CUSTOM_NAME, server.name); assertEquals("Expected custom's url", CUSTOM_URL, server.url); Assert.assertTrue("Expected custom to be marked as not custom", server.isCustom); } - private void assertCustom2(LoginServer server) { + public static void assertCustom2(LoginServer server) { assertEquals("Expected custom2's name", CUSTOM_NAME_2, server.name); assertEquals("Expected custom2's url", CUSTOM_URL_2, server.url); Assert.assertTrue("Expected custom2 to be marked as not custom", server.isCustom); diff --git a/libs/test/SalesforceSDKTest/src/com/salesforce/androidsdk/auth/LoginServerManagerTest.kt b/libs/test/SalesforceSDKTest/src/com/salesforce/androidsdk/auth/LoginServerManagerTest.kt new file mode 100644 index 0000000000..c2adf42c29 --- /dev/null +++ b/libs/test/SalesforceSDKTest/src/com/salesforce/androidsdk/auth/LoginServerManagerTest.kt @@ -0,0 +1,240 @@ +package com.salesforce.androidsdk.auth + +import android.content.Context +import android.content.Context.MODE_PRIVATE +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation +import com.salesforce.androidsdk.config.LoginServerManager +import com.salesforce.androidsdk.config.LoginServerManager.RUNTIME_PREFS_FILE +import com.salesforce.androidsdk.config.LoginServerManager.SERVER_SELECTION_FILE +import com.salesforce.androidsdk.config.LoginServerManager.SERVER_URL_FILE +import com.salesforce.androidsdk.config.RuntimeConfig +import com.salesforce.androidsdk.config.RuntimeConfig.ConfigKey.AppServiceHostLabels +import com.salesforce.androidsdk.config.RuntimeConfig.ConfigKey.AppServiceHosts +import com.salesforce.androidsdk.tests.R.xml.servers +import io.mockk.every +import io.mockk.mockk +import junit.framework.TestCase.assertEquals +import org.junit.After +import org.junit.Assert +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +@SmallTest +class LoginServerManagerTestKt { + + private var loginServerManager: LoginServerManager? = null + + @Before + @Throws(Exception::class) + fun setUp() { + loginServerManager = LoginServerManager(getInstrumentation().targetContext) + loginServerManager?.reset() + } + + @After + @Throws(Exception::class) + fun tearDown() { + loginServerManager?.reset() + loginServerManager = null + } + + /** + * Test for testGetRuntimeConfigLoginServers. + */ + @Test + fun testGetRuntimeConfigLoginServers() { + val context = mockk() + every { context.resources } returns getInstrumentation().targetContext.resources + every { context.getSharedPreferences(SERVER_SELECTION_FILE, any()) } returns getInstrumentation().targetContext.getSharedPreferences(SERVER_SELECTION_FILE, MODE_PRIVATE) + every { context.getSharedPreferences(SERVER_URL_FILE, any()) } returns getInstrumentation().targetContext.getSharedPreferences(SERVER_URL_FILE, MODE_PRIVATE) + every { context.getSharedPreferences(RUNTIME_PREFS_FILE, any()) } returns getInstrumentation().targetContext.getSharedPreferences(RUNTIME_PREFS_FILE, MODE_PRIVATE) + val runtimeConfig = mockk() + every { runtimeConfig.getStringArrayStoredAsArrayOrCSV(AppServiceHosts) } returns arrayOf("https://mdm1.example.com/1", "https://mdm2.example.com/2") + every { runtimeConfig.getStringArrayStoredAsArrayOrCSV(AppServiceHostLabels) } returns arrayOf("MDM 1", "MDM 2") + + loginServerManager = LoginServerManager(context, runtimeConfig, servers) + + val servers = loginServerManager?.loginServers + + Assert.assertEquals("Wrong number of servers", 2, servers?.size) + assertEquals("MDM 1", servers?.get(0)?.name) + assertEquals("https://mdm1.example.com/1", servers?.get(0)?.url) + assertEquals(false, servers?.get(0)?.isCustom) + assertEquals("MDM 2", servers?.get(1)?.name) + assertEquals("https://mdm2.example.com/2", servers?.get(1)?.url) + assertEquals(false, servers?.get(1)?.isCustom) + + assertEquals("MDM 1", loginServerManager?.getSelectedLoginServer()?.name) + assertEquals("https://mdm1.example.com/1", loginServerManager?.getSelectedLoginServer()?.url) + assertEquals(false, loginServerManager?.getSelectedLoginServer()?.isCustom) + } + + /** + * Test for testAddRuntimeConfigLoginServers. + */ + @Test + fun testAddRuntimeConfigLoginServers() { + val context = mockk() + every { context.resources } returns getInstrumentation().targetContext.resources + every { context.getSharedPreferences(SERVER_SELECTION_FILE, any()) } returns getInstrumentation().targetContext.getSharedPreferences(SERVER_SELECTION_FILE, MODE_PRIVATE) + every { context.getSharedPreferences(SERVER_URL_FILE, any()) } returns getInstrumentation().targetContext.getSharedPreferences(SERVER_URL_FILE, MODE_PRIVATE) + every { context.getSharedPreferences(RUNTIME_PREFS_FILE, any()) } returns getInstrumentation().targetContext.getSharedPreferences(RUNTIME_PREFS_FILE, MODE_PRIVATE) + var runtimeConfig = mockk() + every { runtimeConfig.getStringArrayStoredAsArrayOrCSV(AppServiceHosts) } returns arrayOf("https://mdm1.example.com/1", "https://mdm2.example.com/2") + every { runtimeConfig.getStringArrayStoredAsArrayOrCSV(AppServiceHostLabels) } returns arrayOf("MDM 1", "MDM 2") + + loginServerManager = LoginServerManager(context, runtimeConfig, servers) + + var loginServers = loginServerManager?.loginServers + + Assert.assertEquals("Wrong number of servers", 2, loginServers?.size) + assertEquals("MDM 1", loginServers?.get(0)?.name) + assertEquals("https://mdm1.example.com/1", loginServers?.get(0)?.url) + assertEquals(false, loginServers?.get(0)?.isCustom) + assertEquals("MDM 2", loginServers?.get(1)?.name) + assertEquals("https://mdm2.example.com/2", loginServers?.get(1)?.url) + assertEquals(false, loginServers?.get(1)?.isCustom) + + assertEquals("MDM 1", loginServerManager?.getSelectedLoginServer()?.name) + assertEquals("https://mdm1.example.com/1", loginServerManager?.getSelectedLoginServer()?.url) + assertEquals(false, loginServerManager?.getSelectedLoginServer()?.isCustom) + + runtimeConfig = mockk() + every { runtimeConfig.getStringArrayStoredAsArrayOrCSV(AppServiceHosts) } returns arrayOf("https://mdm1.example.com/1", "https://mdm1.example.com/1/1", "https://mdm2.example.com/2") + every { runtimeConfig.getStringArrayStoredAsArrayOrCSV(AppServiceHostLabels) } returns arrayOf("MDM 1", "MDM 1.1", "MDM 2") + + loginServerManager = LoginServerManager(context, runtimeConfig, servers) + + loginServers = loginServerManager?.loginServers + + Assert.assertEquals("Wrong number of servers", 3, loginServers?.size) + assertEquals("MDM 1", loginServers?.get(0)?.name) + assertEquals("https://mdm1.example.com/1", loginServers?.get(0)?.url) + assertEquals(false, loginServers?.get(0)?.isCustom) + assertEquals("MDM 1.1", loginServers?.get(1)?.name) + assertEquals("https://mdm1.example.com/1/1", loginServers?.get(1)?.url) + assertEquals(false, loginServers?.get(1)?.isCustom) + assertEquals("MDM 2", loginServers?.get(2)?.name) + assertEquals("https://mdm2.example.com/2", loginServers?.get(2)?.url) + assertEquals(false, loginServers?.get(2)?.isCustom) + + assertEquals("MDM 1", loginServerManager?.getSelectedLoginServer()?.name) + assertEquals("https://mdm1.example.com/1", loginServerManager?.getSelectedLoginServer()?.url) + assertEquals(false, loginServerManager?.getSelectedLoginServer()?.isCustom) + } + + /** + * Test for testUpdateRuntimeConfigLoginServers. + */ + @Test + fun testUpdateRuntimeConfigLoginServers() { + val context = mockk() + every { context.resources } returns getInstrumentation().targetContext.resources + every { context.getSharedPreferences(SERVER_SELECTION_FILE, any()) } returns getInstrumentation().targetContext.getSharedPreferences(SERVER_SELECTION_FILE, MODE_PRIVATE) + every { context.getSharedPreferences(SERVER_URL_FILE, any()) } returns getInstrumentation().targetContext.getSharedPreferences(SERVER_URL_FILE, MODE_PRIVATE) + every { context.getSharedPreferences(RUNTIME_PREFS_FILE, any()) } returns getInstrumentation().targetContext.getSharedPreferences(RUNTIME_PREFS_FILE, MODE_PRIVATE) + var runtimeConfig = mockk() + every { runtimeConfig.getStringArrayStoredAsArrayOrCSV(AppServiceHosts) } returns arrayOf("https://mdm1.example.com/1", "https://mdm1.example.com/1/1", "https://mdm2.example.com/2") + every { runtimeConfig.getStringArrayStoredAsArrayOrCSV(AppServiceHostLabels) } returns arrayOf("MDM 1", "MDM 1.1", "MDM 2") + + loginServerManager = LoginServerManager(context, runtimeConfig, servers) + + var loginServers = loginServerManager?.loginServers + + Assert.assertEquals("Wrong number of servers", 3, loginServers?.size) + assertEquals("MDM 1", loginServers?.get(0)?.name) + assertEquals("https://mdm1.example.com/1", loginServers?.get(0)?.url) + assertEquals(false, loginServers?.get(0)?.isCustom) + assertEquals("MDM 1.1", loginServers?.get(1)?.name) + assertEquals("https://mdm1.example.com/1/1", loginServers?.get(1)?.url) + assertEquals(false, loginServers?.get(1)?.isCustom) + assertEquals("MDM 2", loginServers?.get(2)?.name) + assertEquals("https://mdm2.example.com/2", loginServers?.get(2)?.url) + assertEquals(false, loginServers?.get(2)?.isCustom) + + assertEquals("MDM 1", loginServerManager?.getSelectedLoginServer()?.name) + assertEquals("https://mdm1.example.com/1", loginServerManager?.getSelectedLoginServer()?.url) + assertEquals(false, loginServerManager?.getSelectedLoginServer()?.isCustom) + + runtimeConfig = mockk() + every { runtimeConfig.getStringArrayStoredAsArrayOrCSV(AppServiceHosts) } returns arrayOf("https://mdm1.example.com/1", "https://mdm1.example.com/1/2", "https://mdm2.example.com/2") + every { runtimeConfig.getStringArrayStoredAsArrayOrCSV(AppServiceHostLabels) } returns arrayOf("MDM 1", "MDM 1.2", "MDM 2") + + loginServerManager = LoginServerManager(context, runtimeConfig, servers) + + loginServers = loginServerManager?.loginServers + + Assert.assertEquals("Wrong number of servers", 3, loginServers?.size) + assertEquals("MDM 1", loginServers?.get(0)?.name) + assertEquals("https://mdm1.example.com/1", loginServers?.get(0)?.url) + assertEquals(false, loginServers?.get(0)?.isCustom) + assertEquals("MDM 1.2", loginServers?.get(1)?.name) + assertEquals("https://mdm1.example.com/1/2", loginServers?.get(1)?.url) + assertEquals(false, loginServers?.get(1)?.isCustom) + assertEquals("MDM 2", loginServers?.get(2)?.name) + assertEquals("https://mdm2.example.com/2", loginServers?.get(2)?.url) + assertEquals(false, loginServers?.get(2)?.isCustom) + + assertEquals("MDM 1", loginServerManager?.getSelectedLoginServer()?.name) + assertEquals("https://mdm1.example.com/1", loginServerManager?.getSelectedLoginServer()?.url) + assertEquals(false, loginServerManager?.getSelectedLoginServer()?.isCustom) + } + + /** + * Test for testRemoveRuntimeConfigLoginServers. + */ + @Test + fun testRemoveRuntimeConfigLoginServers() { + val context = mockk() + every { context.resources } returns getInstrumentation().targetContext.resources + every { context.getSharedPreferences(SERVER_SELECTION_FILE, any()) } returns getInstrumentation().targetContext.getSharedPreferences(SERVER_SELECTION_FILE, MODE_PRIVATE) + every { context.getSharedPreferences(SERVER_URL_FILE, any()) } returns getInstrumentation().targetContext.getSharedPreferences(SERVER_URL_FILE, MODE_PRIVATE) + every { context.getSharedPreferences(RUNTIME_PREFS_FILE, any()) } returns getInstrumentation().targetContext.getSharedPreferences(RUNTIME_PREFS_FILE, MODE_PRIVATE) + var runtimeConfig = mockk() + every { runtimeConfig.getStringArrayStoredAsArrayOrCSV(AppServiceHosts) } returns arrayOf("https://mdm1.example.com/1", "https://mdm1.example.com/1/1", "https://mdm2.example.com/2") + every { runtimeConfig.getStringArrayStoredAsArrayOrCSV(AppServiceHostLabels) } returns arrayOf("MDM 1", "MDM 1.1", "MDM 2") + + loginServerManager = LoginServerManager(context, runtimeConfig, servers) + + var loginServers = loginServerManager?.loginServers + + Assert.assertEquals("Wrong number of servers", 3, loginServers?.size) + assertEquals("MDM 1", loginServers?.get(0)?.name) + assertEquals("https://mdm1.example.com/1", loginServers?.get(0)?.url) + assertEquals(false, loginServers?.get(0)?.isCustom) + assertEquals("MDM 1.1", loginServers?.get(1)?.name) + assertEquals("https://mdm1.example.com/1/1", loginServers?.get(1)?.url) + assertEquals(false, loginServers?.get(1)?.isCustom) + assertEquals("MDM 2", loginServers?.get(2)?.name) + assertEquals("https://mdm2.example.com/2", loginServers?.get(2)?.url) + assertEquals(false, loginServers?.get(2)?.isCustom) + + assertEquals("MDM 1", loginServerManager?.getSelectedLoginServer()?.name) + assertEquals("https://mdm1.example.com/1", loginServerManager?.getSelectedLoginServer()?.url) + assertEquals(false, loginServerManager?.getSelectedLoginServer()?.isCustom) + + runtimeConfig = mockk() + every { runtimeConfig.getStringArrayStoredAsArrayOrCSV(AppServiceHosts) } returns arrayOf("https://mdm1.example.com/1", "https://mdm2.example.com/2") + every { runtimeConfig.getStringArrayStoredAsArrayOrCSV(AppServiceHostLabels) } returns arrayOf("MDM 1", "MDM 2") + + loginServerManager = LoginServerManager(context, runtimeConfig, servers) + + loginServers = loginServerManager?.loginServers + + Assert.assertEquals("Wrong number of servers", 2, loginServers?.size) + assertEquals("MDM 1", loginServers?.get(0)?.name) + assertEquals("https://mdm1.example.com/1", loginServers?.get(0)?.url) + assertEquals(false, loginServers?.get(0)?.isCustom) + assertEquals("MDM 2", loginServers?.get(1)?.name) + assertEquals("https://mdm2.example.com/2", loginServers?.get(1)?.url) + assertEquals(false, loginServers?.get(1)?.isCustom) + + assertEquals("MDM 1", loginServerManager?.getSelectedLoginServer()?.name) + assertEquals("https://mdm1.example.com/1", loginServerManager?.getSelectedLoginServer()?.url) + assertEquals(false, loginServerManager?.getSelectedLoginServer()?.isCustom) + } +} From 463b95c61e12e6aef613572f513b9783c0953798 Mon Sep 17 00:00:00 2001 From: "Eric C. Johnson" Date: Tue, 3 Mar 2026 13:25:01 -0700 Subject: [PATCH 08/14] @W-21148602: Internal Server List does not respect changes made to Servers.xml file (Enhancements For Null Safety And Related Unit Tests) --- .../androidsdk/config/LoginServerManager.java | 149 +++++++++--------- .../res/xml/servers_nulls.xml | 9 ++ .../auth/LoginServerManagerTest.java | 18 +++ .../androidsdk/auth/LoginServerManagerTest.kt | 125 +++++++++++++++ 4 files changed, 224 insertions(+), 77 deletions(-) create mode 100644 libs/test/SalesforceSDKTest/res/xml/servers_nulls.xml diff --git a/libs/SalesforceSDK/src/com/salesforce/androidsdk/config/LoginServerManager.java b/libs/SalesforceSDK/src/com/salesforce/androidsdk/config/LoginServerManager.java index 613f96d69a..93b4d53892 100644 --- a/libs/SalesforceSDK/src/com/salesforce/androidsdk/config/LoginServerManager.java +++ b/libs/SalesforceSDK/src/com/salesforce/androidsdk/config/LoginServerManager.java @@ -37,6 +37,7 @@ import android.content.Context; import android.content.SharedPreferences; import android.content.SharedPreferences.Editor; +import android.content.res.Resources; import android.content.res.XmlResourceParser; import android.os.Looper; @@ -83,10 +84,17 @@ public class LoginServerManager { @VisibleForTesting public static final String RUNTIME_PREFS_FILE = "runtime_prefs_file"; - private static final String NUMBER_OF_ENTRIES = "number_of_entries"; - private static final String SERVER_NAME = "server_name_%d"; - private static final String SERVER_URL = "server_url_%d"; - private static final String IS_CUSTOM = "is_custom_%d"; + @VisibleForTesting + public static final String NUMBER_OF_ENTRIES = "number_of_entries"; + + @VisibleForTesting + public static final String SERVER_NAME = "server_name_%d"; + + @VisibleForTesting + public static final String SERVER_URL = "server_url_%d"; + + @VisibleForTesting + public static final String IS_CUSTOM = "is_custom_%d"; /** * Shared preferences for the selected login server @@ -204,9 +212,8 @@ public LoginServer getSelectedLoginServer() { // First time selection defaults to the first server on the list. if (loginServers != null) { - final LoginServer server = loginServers.get(0); - if (server != null) { - selectedServer.postValue(server); + if (!loginServers.isEmpty()) { + selectedServer.postValue(loginServers.get(0)); } } @@ -248,12 +255,12 @@ public void useSandbox() { } /** - * Adds a custom login server to the shared pref file. + * Adds a custom login server. * - * @param name Server name. - * @param url Server URL. + * @param name The login server name + * @param url The login server URL */ - public void addCustomLoginServer(String name, String url) { + public void addCustomLoginServer(@NonNull String name, @NonNull String url) { // Prevent duplicate servers. for (LoginServer existingServer : getLoginServers()) { if (url.equals(existingServer.url)) { @@ -369,11 +376,7 @@ private SharedPreferences getSharedPreferences() { * server.xml when false */ private boolean isRuntimeConfigAppServiceHostsSet() { - try { - return runtimeConfig.getStringArrayStoredAsArrayOrCSV(AppServiceHosts) != null; - } catch (Exception e) { - return false; - } + return runtimeConfig.getStringArrayStoredAsArrayOrCSV(AppServiceHosts) != null; } /** @@ -382,20 +385,10 @@ private boolean isRuntimeConfigAppServiceHostsSet() { */ @SuppressWarnings("UnusedReturnValue") public List getLoginServersFromRuntimeConfig() { - String[] mdmLoginServers = null; - try { - mdmLoginServers = runtimeConfig.getStringArrayStoredAsArrayOrCSV(AppServiceHosts); - } catch (Exception e) { - SalesforceSDKLogger.w(TAG, "Exception thrown while attempting to read array, attempting to read string value instead", e); - } + String[] mdmLoginServers = runtimeConfig.getStringArrayStoredAsArrayOrCSV(AppServiceHosts); final List allServers = new ArrayList<>(); if (mdmLoginServers != null) { - String[] mdmLoginServersLabels = null; - try { - mdmLoginServersLabels = runtimeConfig.getStringArrayStoredAsArrayOrCSV(AppServiceHostLabels); - } catch (Exception e) { - SalesforceSDKLogger.w(TAG, "Exception thrown while attempting to read array, attempting to read string value instead", e); - } + String[] mdmLoginServersLabels = runtimeConfig.getStringArrayStoredAsArrayOrCSV(AppServiceHostLabels); if (mdmLoginServersLabels == null || mdmLoginServersLabels.length != mdmLoginServers.length) { SalesforceSDKLogger.w(TAG, "No login servers labels provided or wrong number of login servers labels provided - using URLs for the labels"); mdmLoginServersLabels = mdmLoginServers; @@ -407,6 +400,8 @@ public List getLoginServersFromRuntimeConfig() { for (int i = 0; i < mdmLoginServers.length; i++) { final String name = mdmLoginServersLabels[i]; final String url = mdmLoginServers[i]; + if (name == null || url == null) { continue; } + final LoginServer server = new LoginServer(name, url, false); persistLoginServer( name, @@ -516,7 +511,7 @@ public void replaceCustomLoginServer( * @param sharedPreferences The login server shared preferences * @return The adjusted login server index */ - private int getIndexAdjustedToCustomLoginServerBounds( + private @NonNull Integer getIndexAdjustedToCustomLoginServerBounds( final Integer index, final SharedPreferences sharedPreferences ) { @@ -556,26 +551,31 @@ private List getLegacyLoginServers() { * @return Login servers defined in 'res/xml/servers.xml', or null. */ private List getLoginServersFromXML() { - List loginServers = null; - if (servers != 0) { - loginServers = new ArrayList<>(); - final XmlResourceParser xml = ctx.getResources().getXml(serversXmlResourceId); - int eventType = -1; - while (eventType != XmlResourceParser.END_DOCUMENT) { - if (eventType == XmlResourceParser.START_TAG) { - if (xml.getName().equals("server")) { - final String name = xml.getAttributeValue(null, "name"); - final String url = xml.getAttributeValue(null, "url"); - final LoginServer loginServer = new LoginServer(name, - url, false); + final List loginServers = new ArrayList<>(); + + XmlResourceParser xml; + try { + xml = ctx.getResources().getXml(serversXmlResourceId); + } catch (Resources.NotFoundException e) { + return loginServers; + } + + int eventType = -1; + while (eventType != XmlResourceParser.END_DOCUMENT) { + if (eventType == XmlResourceParser.START_TAG) { + if (xml.getName().equals("server")) { + final String name = xml.getAttributeValue(null, "name"); + final String url = xml.getAttributeValue(null, "url"); + if (name != null && url != null) { + final LoginServer loginServer = new LoginServer(name, url, false); loginServers.add(loginServer); } } - try { - eventType = xml.next(); - } catch (XmlPullParserException | IOException e) { - SalesforceSDKLogger.w(TAG, "Exception thrown while parsing XML", e); - } + } + try { + eventType = xml.next(); + } catch (XmlPullParserException | IOException e) { + SalesforceSDKLogger.w(TAG, "Exception thrown while parsing XML", e); } } return loginServers; @@ -588,7 +588,7 @@ private List getLoginServersFromXML() { * @param sharedPreferences The login server shared preferences * @return The next available non-custom login server index */ - private Integer getNextNonCustomLoginServerIndex(final SharedPreferences sharedPreferences) { + private @NonNull Integer getNextNonCustomLoginServerIndex(final SharedPreferences sharedPreferences) { final List servers = getLoginServersFromPreferences(sharedPreferences); int result = servers.size(); for (int i = result - 1; i >= 0; i--) { @@ -608,7 +608,7 @@ private void initSharedPrefFile() { return; } List servers = getLoginServersFromXML(); - if (servers == null || servers.isEmpty()) { + if (servers.isEmpty()) { servers = getLegacyLoginServers(); } int numServers = servers.size(); @@ -639,21 +639,17 @@ private void initSharedPrefFile() { * custom (user-entered) login servers * @param sharedPreferences The shared preferences */ - private void persistLoginServer(final String name, - final String url, + private void persistLoginServer(final @NonNull String name, + final @NonNull String url, final boolean isCustom, final SharedPreferences sharedPreferences ) { - // Guards. - if (name == null || url == null) { - return; - } - // Fetch the current number of servers. final int numberOfServers = sharedPreferences.getInt(NUMBER_OF_ENTRIES, 0); // Adjust the requested index to the bounds of the non-custom (managed) or custom servers. - Integer adjustedIndex; + @SuppressWarnings("WrapperTypeMayBePrimitive") + @NonNull Integer adjustedIndex; if (isCustom) { adjustedIndex = getIndexAdjustedToCustomLoginServerBounds(null, sharedPreferences); } else { @@ -663,25 +659,23 @@ private void persistLoginServer(final String name, final Editor editor = sharedPreferences.edit(); // Increment existing login servers as needed. - if (adjustedIndex != null) { - for (int i = numberOfServers - 1; i >= adjustedIndex; i--) { - final int incrementedIndex = i + 1; - final String loginServerNameKey = format(US, SERVER_NAME, i); - final String loginServerUrlKey = format(US, SERVER_URL, i); - final String loginServerIsCustomKey = format(US, IS_CUSTOM, i); - - final String loginServerName = sharedPreferences.getString(loginServerNameKey, null); - final String loginServerUrl = sharedPreferences.getString(loginServerUrlKey, null); - final boolean loginServerIsCustom = sharedPreferences.getBoolean(loginServerIsCustomKey, false); - - editor - .remove(loginServerNameKey) - .remove(loginServerUrlKey) - .remove(loginServerIsCustomKey) - .putString(format(US, SERVER_NAME, incrementedIndex), loginServerName) - .putString(format(US, SERVER_URL, incrementedIndex), loginServerUrl) - .putBoolean(format(US, IS_CUSTOM, incrementedIndex), loginServerIsCustom); - } + for (int i = numberOfServers - 1; i >= adjustedIndex; i--) { + final int incrementedIndex = i + 1; + final String loginServerNameKey = format(US, SERVER_NAME, i); + final String loginServerUrlKey = format(US, SERVER_URL, i); + final String loginServerIsCustomKey = format(US, IS_CUSTOM, i); + + final String loginServerName = sharedPreferences.getString(loginServerNameKey, null); + final String loginServerUrl = sharedPreferences.getString(loginServerUrlKey, null); + final boolean loginServerIsCustom = sharedPreferences.getBoolean(loginServerIsCustomKey, false); + + editor + .remove(loginServerNameKey) + .remove(loginServerUrlKey) + .remove(loginServerIsCustomKey) + .putString(format(US, SERVER_NAME, incrementedIndex), loginServerName) + .putString(format(US, SERVER_URL, incrementedIndex), loginServerUrl) + .putBoolean(format(US, IS_CUSTOM, incrementedIndex), loginServerIsCustom); } // Insert the new login server. @@ -700,7 +694,8 @@ private void persistLoginServer(final String name, * @param prefs SharedPreferences file. * @return List of all saved servers. */ - private @NonNull List getLoginServersFromPreferences(final SharedPreferences prefs) { + @VisibleForTesting + public @NonNull List getLoginServersFromPreferences(final SharedPreferences prefs) { int numServers = prefs.getInt(NUMBER_OF_ENTRIES, 0); if (numServers == 0) { return new ArrayList<>(); @@ -715,7 +710,7 @@ private void persistLoginServer(final String name, allServers.add(server); } } - return !allServers.isEmpty() ? allServers : new ArrayList<>(); + return allServers; } /** diff --git a/libs/test/SalesforceSDKTest/res/xml/servers_nulls.xml b/libs/test/SalesforceSDKTest/res/xml/servers_nulls.xml new file mode 100644 index 0000000000..6c0996339f --- /dev/null +++ b/libs/test/SalesforceSDKTest/res/xml/servers_nulls.xml @@ -0,0 +1,9 @@ + + + + + + + diff --git a/libs/test/SalesforceSDKTest/src/com/salesforce/androidsdk/auth/LoginServerManagerTest.java b/libs/test/SalesforceSDKTest/src/com/salesforce/androidsdk/auth/LoginServerManagerTest.java index d129fb4a8e..ef1dd47a35 100644 --- a/libs/test/SalesforceSDKTest/src/com/salesforce/androidsdk/auth/LoginServerManagerTest.java +++ b/libs/test/SalesforceSDKTest/src/com/salesforce/androidsdk/auth/LoginServerManagerTest.java @@ -112,6 +112,24 @@ public void testGetLegacyDefaultLoginServers() { assertProduction(loginServerManager.getSelectedLoginServer()); } + /** + * Test for testGetLegacyDefaultLoginServersWhenResourcesAreMissing. + */ + @Test + public void testGetLegacyDefaultLoginServersWhenResourcesAreMissing() { + loginServerManager = new LoginServerManager( + getInstrumentation().getTargetContext(), + getRuntimeConfig(getInstrumentation().getTargetContext()), + 0); + + final List servers = loginServerManager.getLoginServers(); + assertEquals("Wrong number of servers", 2, servers.size()); + assertProduction(servers.get(0)); + assertSandbox(servers.get(1)); + + assertProduction(loginServerManager.getSelectedLoginServer()); + } + /** * Test for getDefaultLoginServer. */ diff --git a/libs/test/SalesforceSDKTest/src/com/salesforce/androidsdk/auth/LoginServerManagerTest.kt b/libs/test/SalesforceSDKTest/src/com/salesforce/androidsdk/auth/LoginServerManagerTest.kt index c2adf42c29..96c23b5f59 100644 --- a/libs/test/SalesforceSDKTest/src/com/salesforce/androidsdk/auth/LoginServerManagerTest.kt +++ b/libs/test/SalesforceSDKTest/src/com/salesforce/androidsdk/auth/LoginServerManagerTest.kt @@ -2,17 +2,23 @@ package com.salesforce.androidsdk.auth import android.content.Context import android.content.Context.MODE_PRIVATE +import android.content.SharedPreferences import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation import com.salesforce.androidsdk.config.LoginServerManager +import com.salesforce.androidsdk.config.LoginServerManager.IS_CUSTOM +import com.salesforce.androidsdk.config.LoginServerManager.NUMBER_OF_ENTRIES import com.salesforce.androidsdk.config.LoginServerManager.RUNTIME_PREFS_FILE +import com.salesforce.androidsdk.config.LoginServerManager.SERVER_NAME import com.salesforce.androidsdk.config.LoginServerManager.SERVER_SELECTION_FILE +import com.salesforce.androidsdk.config.LoginServerManager.SERVER_URL import com.salesforce.androidsdk.config.LoginServerManager.SERVER_URL_FILE import com.salesforce.androidsdk.config.RuntimeConfig import com.salesforce.androidsdk.config.RuntimeConfig.ConfigKey.AppServiceHostLabels import com.salesforce.androidsdk.config.RuntimeConfig.ConfigKey.AppServiceHosts import com.salesforce.androidsdk.tests.R.xml.servers +import com.salesforce.androidsdk.tests.R.xml.servers_nulls import io.mockk.every import io.mockk.mockk import junit.framework.TestCase.assertEquals @@ -237,4 +243,123 @@ class LoginServerManagerTestKt { assertEquals("https://mdm1.example.com/1", loginServerManager?.getSelectedLoginServer()?.url) assertEquals(false, loginServerManager?.getSelectedLoginServer()?.isCustom) } + + /** + * Test for testNullsInResourcesXmlLoginServers. + */ + @Test + fun testNullsInResourcesXmlLoginServers() { + + val context = mockk() + every { context.resources } returns getInstrumentation().targetContext.resources + every { context.getSharedPreferences(SERVER_SELECTION_FILE, any()) } returns getInstrumentation().targetContext.getSharedPreferences(SERVER_SELECTION_FILE, MODE_PRIVATE) + every { context.getSharedPreferences(SERVER_URL_FILE, any()) } returns getInstrumentation().targetContext.getSharedPreferences(SERVER_URL_FILE, MODE_PRIVATE) + every { context.getSharedPreferences(RUNTIME_PREFS_FILE, any()) } returns getInstrumentation().targetContext.getSharedPreferences(RUNTIME_PREFS_FILE, MODE_PRIVATE) + val runtimeConfig = mockk() + every { runtimeConfig.getStringArrayStoredAsArrayOrCSV(AppServiceHosts) } returns null + every { runtimeConfig.getStringArrayStoredAsArrayOrCSV(AppServiceHostLabels) } returns null + + loginServerManager = LoginServerManager(context, runtimeConfig, servers_nulls) + + val loginServers = loginServerManager?.loginServers + + Assert.assertEquals("Wrong number of servers", 1, loginServers?.size) + assertEquals("Example Login Server", loginServers?.get(0)?.name) + assertEquals("https://www.example.com", loginServers?.get(0)?.url) + assertEquals(false, loginServers?.get(0)?.isCustom) + } + + /** + * Test for testNullsInSharedPreferencesLoginServers. + */ + @Test + fun testNullsInSharedPreferencesLoginServers() { + + val sharedPreferences = mockk(relaxed = true) + every { sharedPreferences.getInt(NUMBER_OF_ENTRIES, 0) } returns 4 + every { sharedPreferences.getString(String.format(SERVER_NAME, 0), null) } returns null + every { sharedPreferences.getString(String.format(SERVER_URL, 0), null) } returns null + every { sharedPreferences.getBoolean(String.format(IS_CUSTOM, 0), false) } returns false + + every { sharedPreferences.getString(String.format(SERVER_NAME, 1), null) } returns "Any String" + every { sharedPreferences.getString(String.format(SERVER_URL, 1), null) } returns null + every { sharedPreferences.getBoolean(String.format(IS_CUSTOM, 1), false) } returns false + + every { sharedPreferences.getString(String.format(SERVER_NAME, 2), null) } returns null + every { sharedPreferences.getString(String.format(SERVER_URL, 2), null) } returns "Any String" + every { sharedPreferences.getBoolean(String.format(IS_CUSTOM, 2), false) } returns false + + every { sharedPreferences.getString(String.format(SERVER_NAME, 3), null) } returns "Example Login Server" + every { sharedPreferences.getString(String.format(SERVER_URL, 3), null) } returns "https://login.example.com" + every { sharedPreferences.getBoolean(String.format(IS_CUSTOM, 3), false) } returns false + + val context = mockk() + every { context.resources } returns getInstrumentation().targetContext.resources + every { context.getSharedPreferences(SERVER_SELECTION_FILE, any()) } returns getInstrumentation().targetContext.getSharedPreferences(SERVER_SELECTION_FILE, MODE_PRIVATE) + every { context.getSharedPreferences(SERVER_URL_FILE, any()) } returns sharedPreferences + every { context.getSharedPreferences(RUNTIME_PREFS_FILE, any()) } returns getInstrumentation().targetContext.getSharedPreferences(RUNTIME_PREFS_FILE, MODE_PRIVATE) + val runtimeConfig = mockk() + every { runtimeConfig.getStringArrayStoredAsArrayOrCSV(AppServiceHosts) } returns null + every { runtimeConfig.getStringArrayStoredAsArrayOrCSV(AppServiceHostLabels) } returns null + + loginServerManager = LoginServerManager(context, runtimeConfig, servers) + + var loginServers = loginServerManager?.loginServers + + Assert.assertEquals("Wrong number of servers", 1, loginServers?.size) + + loginServers = loginServerManager?.getLoginServersFromPreferences(sharedPreferences) + + Assert.assertEquals("Wrong number of servers", 1, loginServers?.size) + assertEquals("Example Login Server", loginServers?.get(0)?.name) + assertEquals("https://login.example.com", loginServers?.get(0)?.url) + assertEquals(false, loginServers?.get(0)?.isCustom) + } + + /** + * Test for testNullsInSharedPreferencesLoginServers. + */ + @Test + fun testEmptyInSharedPreferencesLoginServers() { + + val sharedPreferences = mockk(relaxed = true) + every { sharedPreferences.getInt(NUMBER_OF_ENTRIES, 0) } returns 4 + every { sharedPreferences.getString(String.format(SERVER_NAME, 0), null) } returns null + every { sharedPreferences.getString(String.format(SERVER_URL, 0), null) } returns null + every { sharedPreferences.getBoolean(String.format(IS_CUSTOM, 0), false) } returns false + + every { sharedPreferences.getString(String.format(SERVER_NAME, 1), null) } returns "Any String" + every { sharedPreferences.getString(String.format(SERVER_URL, 1), null) } returns null + every { sharedPreferences.getBoolean(String.format(IS_CUSTOM, 1), false) } returns false + + every { sharedPreferences.getString(String.format(SERVER_NAME, 2), null) } returns null + every { sharedPreferences.getString(String.format(SERVER_URL, 2), null) } returns "Any String" + every { sharedPreferences.getBoolean(String.format(IS_CUSTOM, 2), false) } returns false + + every { sharedPreferences.getString(String.format(SERVER_NAME, 3), null) } returns "Example Login Server" + every { sharedPreferences.getString(String.format(SERVER_URL, 3), null) } returns "https://login.example.com" + every { sharedPreferences.getBoolean(String.format(IS_CUSTOM, 3), false) } returns false + + val context = mockk() + every { context.resources } returns getInstrumentation().targetContext.resources + every { context.getSharedPreferences(SERVER_SELECTION_FILE, any()) } returns getInstrumentation().targetContext.getSharedPreferences(SERVER_SELECTION_FILE, MODE_PRIVATE) + every { context.getSharedPreferences(SERVER_URL_FILE, any()) } returns sharedPreferences + every { context.getSharedPreferences(RUNTIME_PREFS_FILE, any()) } returns getInstrumentation().targetContext.getSharedPreferences(RUNTIME_PREFS_FILE, MODE_PRIVATE) + val runtimeConfig = mockk() + every { runtimeConfig.getStringArrayStoredAsArrayOrCSV(AppServiceHosts) } returns null + every { runtimeConfig.getStringArrayStoredAsArrayOrCSV(AppServiceHostLabels) } returns null + + loginServerManager = LoginServerManager(context, runtimeConfig, servers) + + var loginServers = loginServerManager?.loginServers + + Assert.assertEquals("Wrong number of servers", 1, loginServers?.size) + + loginServers = loginServerManager?.getLoginServersFromPreferences(sharedPreferences) + + Assert.assertEquals("Wrong number of servers", 1, loginServers?.size) + assertEquals("Example Login Server", loginServers?.get(0)?.name) + assertEquals("https://login.example.com", loginServers?.get(0)?.url) + assertEquals(false, loginServers?.get(0)?.isCustom) + } } From 7daebb6572692dbfd67be4c3b794388191415a25 Mon Sep 17 00:00:00 2001 From: "Eric C. Johnson" Date: Tue, 3 Mar 2026 15:50:23 -0700 Subject: [PATCH 09/14] @W-21148602: Internal Server List does not respect changes made to Servers.xml file (Additional Enhancements For Null Safety And Related Unit Tests) --- .../androidsdk/config/LoginServerManager.java | 3 + .../androidsdk/auth/LoginServerManagerTest.kt | 111 +++++++++++++++--- 2 files changed, 100 insertions(+), 14 deletions(-) diff --git a/libs/SalesforceSDK/src/com/salesforce/androidsdk/config/LoginServerManager.java b/libs/SalesforceSDK/src/com/salesforce/androidsdk/config/LoginServerManager.java index 93b4d53892..7e2925746e 100644 --- a/libs/SalesforceSDK/src/com/salesforce/androidsdk/config/LoginServerManager.java +++ b/libs/SalesforceSDK/src/com/salesforce/androidsdk/config/LoginServerManager.java @@ -708,6 +708,9 @@ private void persistLoginServer(final @NonNull String name, if (name != null && url != null) { final LoginServer server = new LoginServer(name, url.trim(), isCustom); allServers.add(server); + } else { + // TODO: Coverage Needed. ECJ20260303 + SalesforceSDKLogger.w(TAG, "Invalid login server found in preferences"); } } return allServers; diff --git a/libs/test/SalesforceSDKTest/src/com/salesforce/androidsdk/auth/LoginServerManagerTest.kt b/libs/test/SalesforceSDKTest/src/com/salesforce/androidsdk/auth/LoginServerManagerTest.kt index 96c23b5f59..12104b4a67 100644 --- a/libs/test/SalesforceSDKTest/src/com/salesforce/androidsdk/auth/LoginServerManagerTest.kt +++ b/libs/test/SalesforceSDKTest/src/com/salesforce/androidsdk/auth/LoginServerManagerTest.kt @@ -21,9 +21,9 @@ import com.salesforce.androidsdk.tests.R.xml.servers import com.salesforce.androidsdk.tests.R.xml.servers_nulls import io.mockk.every import io.mockk.mockk -import junit.framework.TestCase.assertEquals import org.junit.After -import org.junit.Assert +import org.junit.Assert.assertEquals +import org.junit.Assert.assertNull import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @@ -48,6 +48,27 @@ class LoginServerManagerTestKt { loginServerManager = null } + /** + * Test for testGetLoginServersFromRuntimeConfigWhenRuntimeConfigHasNull. + */ + @Test + fun testGetLoginServersFromRuntimeConfigWhenRuntimeConfigHasNull() { + val context = mockk() + every { context.resources } returns getInstrumentation().targetContext.resources + every { context.getSharedPreferences(SERVER_SELECTION_FILE, any()) } returns getInstrumentation().targetContext.getSharedPreferences(SERVER_SELECTION_FILE, MODE_PRIVATE) + every { context.getSharedPreferences(SERVER_URL_FILE, any()) } returns getInstrumentation().targetContext.getSharedPreferences(SERVER_URL_FILE, MODE_PRIVATE) + every { context.getSharedPreferences(RUNTIME_PREFS_FILE, any()) } returns getInstrumentation().targetContext.getSharedPreferences(RUNTIME_PREFS_FILE, MODE_PRIVATE) + val runtimeConfig = mockk() + every { runtimeConfig.getStringArrayStoredAsArrayOrCSV(AppServiceHosts) } returns null + every { runtimeConfig.getStringArrayStoredAsArrayOrCSV(AppServiceHostLabels) } returns null + + loginServerManager = LoginServerManager(context, runtimeConfig, servers) + + val servers = loginServerManager?.loginServersFromRuntimeConfig + + assertNull(servers) + } + /** * Test for testGetRuntimeConfigLoginServers. */ @@ -66,7 +87,7 @@ class LoginServerManagerTestKt { val servers = loginServerManager?.loginServers - Assert.assertEquals("Wrong number of servers", 2, servers?.size) + assertEquals("Wrong number of servers", 2, servers?.size) assertEquals("MDM 1", servers?.get(0)?.name) assertEquals("https://mdm1.example.com/1", servers?.get(0)?.url) assertEquals(false, servers?.get(0)?.isCustom) @@ -79,6 +100,68 @@ class LoginServerManagerTestKt { assertEquals(false, loginServerManager?.getSelectedLoginServer()?.isCustom) } + /** + * Test for testGetRuntimeConfigLoginServersWithoutLabels. + */ + @Test + fun testGetRuntimeConfigLoginServersWithoutLabels() { + val context = mockk() + every { context.resources } returns getInstrumentation().targetContext.resources + every { context.getSharedPreferences(SERVER_SELECTION_FILE, any()) } returns getInstrumentation().targetContext.getSharedPreferences(SERVER_SELECTION_FILE, MODE_PRIVATE) + every { context.getSharedPreferences(SERVER_URL_FILE, any()) } returns getInstrumentation().targetContext.getSharedPreferences(SERVER_URL_FILE, MODE_PRIVATE) + every { context.getSharedPreferences(RUNTIME_PREFS_FILE, any()) } returns getInstrumentation().targetContext.getSharedPreferences(RUNTIME_PREFS_FILE, MODE_PRIVATE) + val runtimeConfig = mockk() + every { runtimeConfig.getStringArrayStoredAsArrayOrCSV(AppServiceHosts) } returns arrayOf("https://mdm1.example.com/1", "https://mdm2.example.com/2") + every { runtimeConfig.getStringArrayStoredAsArrayOrCSV(AppServiceHostLabels) } returns null + + loginServerManager = LoginServerManager(context, runtimeConfig, servers) + + val servers = loginServerManager?.loginServers + + assertEquals("Wrong number of servers", 2, servers?.size) + assertEquals(servers?.get(0)?.url, servers?.get(0)?.name) + assertEquals("https://mdm1.example.com/1", servers?.get(0)?.url) + assertEquals(false, servers?.get(0)?.isCustom) + assertEquals(servers?.get(1)?.url, servers?.get(1)?.name) + assertEquals("https://mdm2.example.com/2", servers?.get(1)?.url) + assertEquals(false, servers?.get(1)?.isCustom) + + assertEquals("https://mdm1.example.com/1", loginServerManager?.getSelectedLoginServer()?.name) + assertEquals("https://mdm1.example.com/1", loginServerManager?.getSelectedLoginServer()?.url) + assertEquals(false, loginServerManager?.getSelectedLoginServer()?.isCustom) + } + + /** + * Test for testGetRuntimeConfigLoginServersWithoutIncorrectLabelCount. + */ + @Test + fun testGetRuntimeConfigLoginServersWithoutIncorrectLabelCount() { + val context = mockk() + every { context.resources } returns getInstrumentation().targetContext.resources + every { context.getSharedPreferences(SERVER_SELECTION_FILE, any()) } returns getInstrumentation().targetContext.getSharedPreferences(SERVER_SELECTION_FILE, MODE_PRIVATE) + every { context.getSharedPreferences(SERVER_URL_FILE, any()) } returns getInstrumentation().targetContext.getSharedPreferences(SERVER_URL_FILE, MODE_PRIVATE) + every { context.getSharedPreferences(RUNTIME_PREFS_FILE, any()) } returns getInstrumentation().targetContext.getSharedPreferences(RUNTIME_PREFS_FILE, MODE_PRIVATE) + val runtimeConfig = mockk() + every { runtimeConfig.getStringArrayStoredAsArrayOrCSV(AppServiceHosts) } returns arrayOf("https://mdm1.example.com/1", "https://mdm2.example.com/2") + every { runtimeConfig.getStringArrayStoredAsArrayOrCSV(AppServiceHostLabels) } returns arrayOf("MDM 1") + + loginServerManager = LoginServerManager(context, runtimeConfig, servers) + + val servers = loginServerManager?.loginServers + + assertEquals("Wrong number of servers", 2, servers?.size) + assertEquals(servers?.get(0)?.url, servers?.get(0)?.name) + assertEquals("https://mdm1.example.com/1", servers?.get(0)?.url) + assertEquals(false, servers?.get(0)?.isCustom) + assertEquals(servers?.get(1)?.url, servers?.get(1)?.name) + assertEquals("https://mdm2.example.com/2", servers?.get(1)?.url) + assertEquals(false, servers?.get(1)?.isCustom) + + assertEquals("https://mdm1.example.com/1", loginServerManager?.getSelectedLoginServer()?.name) + assertEquals("https://mdm1.example.com/1", loginServerManager?.getSelectedLoginServer()?.url) + assertEquals(false, loginServerManager?.getSelectedLoginServer()?.isCustom) + } + /** * Test for testAddRuntimeConfigLoginServers. */ @@ -97,7 +180,7 @@ class LoginServerManagerTestKt { var loginServers = loginServerManager?.loginServers - Assert.assertEquals("Wrong number of servers", 2, loginServers?.size) + assertEquals("Wrong number of servers", 2, loginServers?.size) assertEquals("MDM 1", loginServers?.get(0)?.name) assertEquals("https://mdm1.example.com/1", loginServers?.get(0)?.url) assertEquals(false, loginServers?.get(0)?.isCustom) @@ -117,7 +200,7 @@ class LoginServerManagerTestKt { loginServers = loginServerManager?.loginServers - Assert.assertEquals("Wrong number of servers", 3, loginServers?.size) + assertEquals("Wrong number of servers", 3, loginServers?.size) assertEquals("MDM 1", loginServers?.get(0)?.name) assertEquals("https://mdm1.example.com/1", loginServers?.get(0)?.url) assertEquals(false, loginServers?.get(0)?.isCustom) @@ -151,7 +234,7 @@ class LoginServerManagerTestKt { var loginServers = loginServerManager?.loginServers - Assert.assertEquals("Wrong number of servers", 3, loginServers?.size) + assertEquals("Wrong number of servers", 3, loginServers?.size) assertEquals("MDM 1", loginServers?.get(0)?.name) assertEquals("https://mdm1.example.com/1", loginServers?.get(0)?.url) assertEquals(false, loginServers?.get(0)?.isCustom) @@ -174,7 +257,7 @@ class LoginServerManagerTestKt { loginServers = loginServerManager?.loginServers - Assert.assertEquals("Wrong number of servers", 3, loginServers?.size) + assertEquals("Wrong number of servers", 3, loginServers?.size) assertEquals("MDM 1", loginServers?.get(0)?.name) assertEquals("https://mdm1.example.com/1", loginServers?.get(0)?.url) assertEquals(false, loginServers?.get(0)?.isCustom) @@ -208,7 +291,7 @@ class LoginServerManagerTestKt { var loginServers = loginServerManager?.loginServers - Assert.assertEquals("Wrong number of servers", 3, loginServers?.size) + assertEquals("Wrong number of servers", 3, loginServers?.size) assertEquals("MDM 1", loginServers?.get(0)?.name) assertEquals("https://mdm1.example.com/1", loginServers?.get(0)?.url) assertEquals(false, loginServers?.get(0)?.isCustom) @@ -231,7 +314,7 @@ class LoginServerManagerTestKt { loginServers = loginServerManager?.loginServers - Assert.assertEquals("Wrong number of servers", 2, loginServers?.size) + assertEquals("Wrong number of servers", 2, loginServers?.size) assertEquals("MDM 1", loginServers?.get(0)?.name) assertEquals("https://mdm1.example.com/1", loginServers?.get(0)?.url) assertEquals(false, loginServers?.get(0)?.isCustom) @@ -263,7 +346,7 @@ class LoginServerManagerTestKt { val loginServers = loginServerManager?.loginServers - Assert.assertEquals("Wrong number of servers", 1, loginServers?.size) + assertEquals("Wrong number of servers", 1, loginServers?.size) assertEquals("Example Login Server", loginServers?.get(0)?.name) assertEquals("https://www.example.com", loginServers?.get(0)?.url) assertEquals(false, loginServers?.get(0)?.isCustom) @@ -306,11 +389,11 @@ class LoginServerManagerTestKt { var loginServers = loginServerManager?.loginServers - Assert.assertEquals("Wrong number of servers", 1, loginServers?.size) + assertEquals("Wrong number of servers", 1, loginServers?.size) loginServers = loginServerManager?.getLoginServersFromPreferences(sharedPreferences) - Assert.assertEquals("Wrong number of servers", 1, loginServers?.size) + assertEquals("Wrong number of servers", 1, loginServers?.size) assertEquals("Example Login Server", loginServers?.get(0)?.name) assertEquals("https://login.example.com", loginServers?.get(0)?.url) assertEquals(false, loginServers?.get(0)?.isCustom) @@ -353,11 +436,11 @@ class LoginServerManagerTestKt { var loginServers = loginServerManager?.loginServers - Assert.assertEquals("Wrong number of servers", 1, loginServers?.size) + assertEquals("Wrong number of servers", 1, loginServers?.size) loginServers = loginServerManager?.getLoginServersFromPreferences(sharedPreferences) - Assert.assertEquals("Wrong number of servers", 1, loginServers?.size) + assertEquals("Wrong number of servers", 1, loginServers?.size) assertEquals("Example Login Server", loginServers?.get(0)?.name) assertEquals("https://login.example.com", loginServers?.get(0)?.url) assertEquals(false, loginServers?.get(0)?.isCustom) From 686e433a54c534ae872d718d376209f1b268bd8d Mon Sep 17 00:00:00 2001 From: "Eric C. Johnson" Date: Tue, 3 Mar 2026 18:01:43 -0700 Subject: [PATCH 10/14] @W-21148602: Internal Server List does not respect changes made to Servers.xml file (Improve Logic Branching In `getSelectedLoginServer`) --- .../androidsdk/config/LoginServerManager.java | 56 +++-- .../androidsdk/auth/LoginServerManagerTest.kt | 206 +++++++++++++++++- 2 files changed, 241 insertions(+), 21 deletions(-) diff --git a/libs/SalesforceSDK/src/com/salesforce/androidsdk/config/LoginServerManager.java b/libs/SalesforceSDK/src/com/salesforce/androidsdk/config/LoginServerManager.java index 7e2925746e..6562f31cdd 100644 --- a/libs/SalesforceSDK/src/com/salesforce/androidsdk/config/LoginServerManager.java +++ b/libs/SalesforceSDK/src/com/salesforce/androidsdk/config/LoginServerManager.java @@ -48,9 +48,6 @@ import com.salesforce.androidsdk.R; import com.salesforce.androidsdk.util.SalesforceSDKLogger; -import org.xmlpull.v1.XmlPullParserException; - -import java.io.IOException; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Deque; @@ -187,10 +184,12 @@ public LoginServer getLoginServerFromURL(String url) { * @return The selected login server */ public LoginServer getSelectedLoginServer() { + // Fetch the selected login server. final SharedPreferences selectedServerPrefs = ctx.getSharedPreferences(SERVER_SELECTION_FILE, MODE_PRIVATE); - final String name = selectedServerPrefs.getString(SERVER_NAME, null); - final String url = selectedServerPrefs.getString(SERVER_URL, null); - boolean isCustom = selectedServerPrefs.getBoolean(IS_CUSTOM, false); + final String selectedServerName = selectedServerPrefs.getString(SERVER_NAME, null); + final String selectedServerUrl = selectedServerPrefs.getString(SERVER_URL, null); + final boolean selectedServerIsCustom = selectedServerPrefs.getBoolean(IS_CUSTOM, false); + final boolean selectedServerHasValidNameAndUrl = selectedServerName != null && selectedServerUrl != null; // Refresh the list of mobile device management (MDM) servers from the runtime config. if (isRuntimeConfigAppServiceHostsSet()) { @@ -200,26 +199,36 @@ public LoginServer getSelectedLoginServer() { // Get the active list of login servers. final List loginServers = getLoginServers(); - // Selection has been saved before and is available in the active list of login servers. - if (name != null && url != null && loginServers.stream().anyMatch(server -> server.name.equals(name) && server.url.equals(url))) { - final LoginServer server = new LoginServer(name, url, isCustom); + // Check if the selected login server is available in the active list of login servers. + final boolean selectedLoginServerIsAvailable = loginServers.stream().anyMatch(server -> + server.name.equals(selectedServerName) && server.url.equals(selectedServerUrl) + ); - // Only notify live data consumers if the value has changed. - if (!server.equals(selectedServer.getValue())) { - selectedServer.postValue(server); - } - } else { + // If the selected login server is valid and is available in the active list of login servers. + LoginServer selectedLoginServer = null; + if (selectedServerHasValidNameAndUrl) { + if (selectedLoginServerIsAvailable) { + selectedLoginServer = new LoginServer(selectedServerName, selectedServerUrl, selectedServerIsCustom); - // First time selection defaults to the first server on the list. - if (loginServers != null) { - if (!loginServers.isEmpty()) { - selectedServer.postValue(loginServers.get(0)); + // Notify live data consumers if the value has changed. + if (!selectedLoginServer.equals(selectedServer.getValue())) { + selectedServer.postValue(selectedLoginServer); } } + } + + // If the selected login server is invalid or not available in the active list of login servers. + if (selectedLoginServer == null) { + + // Default to the first login server on the list. + if (!loginServers.isEmpty()) { + selectedServer.postValue(loginServers.get(0)); + } - // Stores the selection for the future. + // Store the selected login server. setSelectedLoginServer(selectedServer.getValue()); } + return selectedServer.getValue(); } @@ -321,6 +330,7 @@ private void removeServer( int index = servers.indexOf(server); + // TODO: Unrelated Coverage Needed. ECJ20260303 if (allowNonCustomRemoval || server.isCustom && index != -1) { int numServers = servers.size(); Deque stack = new ArrayDeque<>(servers.subList(index + 1, numServers)); @@ -387,10 +397,13 @@ private boolean isRuntimeConfigAppServiceHostsSet() { public List getLoginServersFromRuntimeConfig() { String[] mdmLoginServers = runtimeConfig.getStringArrayStoredAsArrayOrCSV(AppServiceHosts); final List allServers = new ArrayList<>(); + // TODO: Coverage Needed. ECJ20260303 if (mdmLoginServers != null) { String[] mdmLoginServersLabels = runtimeConfig.getStringArrayStoredAsArrayOrCSV(AppServiceHostLabels); if (mdmLoginServersLabels == null || mdmLoginServersLabels.length != mdmLoginServers.length) { + // TODO: Coverage Needed. ECJ20260303 SalesforceSDKLogger.w(TAG, "No login servers labels provided or wrong number of login servers labels provided - using URLs for the labels"); + // TODO: Coverage Needed. ECJ20260303 mdmLoginServersLabels = mdmLoginServers; } @@ -400,6 +413,7 @@ public List getLoginServersFromRuntimeConfig() { for (int i = 0; i < mdmLoginServers.length; i++) { final String name = mdmLoginServersLabels[i]; final String url = mdmLoginServers[i]; + // TODO: Coverage Needed. ECJ20260303 if (name == null || url == null) { continue; } final LoginServer server = new LoginServer(name, url, false); @@ -412,6 +426,7 @@ public List getLoginServersFromRuntimeConfig() { allServers.add(server); } } + // TODO: Coverage Needed. ECJ20260303 return (!allServers.isEmpty() ? allServers : null); } @@ -574,8 +589,9 @@ private List getLoginServersFromXML() { } try { eventType = xml.next(); - } catch (XmlPullParserException | IOException e) { + } catch (Exception e) { SalesforceSDKLogger.w(TAG, "Exception thrown while parsing XML", e); + break; } } return loginServers; diff --git a/libs/test/SalesforceSDKTest/src/com/salesforce/androidsdk/auth/LoginServerManagerTest.kt b/libs/test/SalesforceSDKTest/src/com/salesforce/androidsdk/auth/LoginServerManagerTest.kt index 12104b4a67..e7073f7ac3 100644 --- a/libs/test/SalesforceSDKTest/src/com/salesforce/androidsdk/auth/LoginServerManagerTest.kt +++ b/libs/test/SalesforceSDKTest/src/com/salesforce/androidsdk/auth/LoginServerManagerTest.kt @@ -3,9 +3,13 @@ package com.salesforce.androidsdk.auth import android.content.Context import android.content.Context.MODE_PRIVATE import android.content.SharedPreferences +import android.content.res.Resources +import android.content.res.XmlResourceParser import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation +import com.salesforce.androidsdk.R.string.sf__auth_login_production +import com.salesforce.androidsdk.R.string.sf__auth_login_sandbox import com.salesforce.androidsdk.config.LoginServerManager import com.salesforce.androidsdk.config.LoginServerManager.IS_CUSTOM import com.salesforce.androidsdk.config.LoginServerManager.NUMBER_OF_ENTRIES @@ -27,6 +31,7 @@ import org.junit.Assert.assertNull import org.junit.Before import org.junit.Test import org.junit.runner.RunWith +import org.xmlpull.v1.XmlPullParserException @RunWith(AndroidJUnit4::class) @SmallTest @@ -352,6 +357,177 @@ class LoginServerManagerTestKt { assertEquals(false, loginServers?.get(0)?.isCustom) } + /** + * Test for testNullsInSelectedLoginServer. + */ + @Test + fun testNullsInSelectedLoginServer() { + + val sharedPreferencesSelectedServer = mockk(relaxed = true) + every { sharedPreferencesSelectedServer.getString(SERVER_NAME, null) } returns null + every { sharedPreferencesSelectedServer.getString(SERVER_URL, null) } returns null + every { sharedPreferencesSelectedServer.getBoolean(IS_CUSTOM, false) } returns false + + val sharedPreferences = mockk(relaxed = true) + every { sharedPreferences.getInt(NUMBER_OF_ENTRIES, 0) } returns 1 + every { sharedPreferences.getString(String.format(SERVER_NAME, 0), null) } returns "Default Login Server" + every { sharedPreferences.getString(String.format(SERVER_URL, 0), null) } returns "https://default.example.com" + every { sharedPreferences.getBoolean(String.format(IS_CUSTOM, 0), false) } returns false + + val context = mockk() + every { context.resources } returns getInstrumentation().targetContext.resources + every { context.getSharedPreferences(SERVER_SELECTION_FILE, any()) } returns sharedPreferencesSelectedServer + every { context.getSharedPreferences(SERVER_URL_FILE, any()) } returns sharedPreferences + every { context.getSharedPreferences(RUNTIME_PREFS_FILE, any()) } returns getInstrumentation().targetContext.getSharedPreferences(RUNTIME_PREFS_FILE, MODE_PRIVATE) + val runtimeConfig = mockk() + every { runtimeConfig.getStringArrayStoredAsArrayOrCSV(AppServiceHosts) } returns null + every { runtimeConfig.getStringArrayStoredAsArrayOrCSV(AppServiceHostLabels) } returns null + + loginServerManager = LoginServerManager(context, runtimeConfig, servers) + + val selectedLoginServer = loginServerManager?.selectedLoginServer + + assertEquals("Default Login Server", selectedLoginServer?.name) + assertEquals("https://default.example.com", selectedLoginServer?.url) + } + + /** + * Test for testNullNameInSelectedLoginServer. + */ + @Test + fun testNullNameInSelectedLoginServer() { + + val sharedPreferencesSelectedServer = mockk(relaxed = true) + every { sharedPreferencesSelectedServer.getString(SERVER_NAME, null) } returns null + every { sharedPreferencesSelectedServer.getString(SERVER_URL, null) } returns "https://selected.example.com" + every { sharedPreferencesSelectedServer.getBoolean(IS_CUSTOM, false) } returns false + + val sharedPreferences = mockk(relaxed = true) + every { sharedPreferences.getInt(NUMBER_OF_ENTRIES, 0) } returns 1 + every { sharedPreferences.getString(String.format(SERVER_NAME, 0), null) } returns "Default Login Server" + every { sharedPreferences.getString(String.format(SERVER_URL, 0), null) } returns "https://default.example.com" + every { sharedPreferences.getBoolean(String.format(IS_CUSTOM, 0), false) } returns false + + val context = mockk() + every { context.resources } returns getInstrumentation().targetContext.resources + every { context.getSharedPreferences(SERVER_SELECTION_FILE, any()) } returns sharedPreferencesSelectedServer + every { context.getSharedPreferences(SERVER_URL_FILE, any()) } returns sharedPreferences + every { context.getSharedPreferences(RUNTIME_PREFS_FILE, any()) } returns getInstrumentation().targetContext.getSharedPreferences(RUNTIME_PREFS_FILE, MODE_PRIVATE) + val runtimeConfig = mockk() + every { runtimeConfig.getStringArrayStoredAsArrayOrCSV(AppServiceHosts) } returns null + every { runtimeConfig.getStringArrayStoredAsArrayOrCSV(AppServiceHostLabels) } returns null + + loginServerManager = LoginServerManager(context, runtimeConfig, servers) + + val selectedLoginServer = loginServerManager?.selectedLoginServer + + assertEquals("Default Login Server", selectedLoginServer?.name) + assertEquals("https://default.example.com", selectedLoginServer?.url) + } + + /** + * Test for testNullUrlInSelectedLoginServer. + */ + @Test + fun testNullUrlInSelectedLoginServer() { + + val sharedPreferencesSelectedServer = mockk(relaxed = true) + every { sharedPreferencesSelectedServer.getString(SERVER_NAME, null) } returns "Selected Login Server" + every { sharedPreferencesSelectedServer.getString(SERVER_URL, null) } returns null + every { sharedPreferencesSelectedServer.getBoolean(IS_CUSTOM, false) } returns false + + val sharedPreferences = mockk(relaxed = true) + every { sharedPreferences.getInt(NUMBER_OF_ENTRIES, 0) } returns 1 + every { sharedPreferences.getString(String.format(SERVER_NAME, 0), null) } returns "Default Login Server" + every { sharedPreferences.getString(String.format(SERVER_URL, 0), null) } returns "https://default.example.com" + every { sharedPreferences.getBoolean(String.format(IS_CUSTOM, 0), false) } returns false + + val context = mockk() + every { context.resources } returns getInstrumentation().targetContext.resources + every { context.getSharedPreferences(SERVER_SELECTION_FILE, any()) } returns sharedPreferencesSelectedServer + every { context.getSharedPreferences(SERVER_URL_FILE, any()) } returns sharedPreferences + every { context.getSharedPreferences(RUNTIME_PREFS_FILE, any()) } returns getInstrumentation().targetContext.getSharedPreferences(RUNTIME_PREFS_FILE, MODE_PRIVATE) + val runtimeConfig = mockk() + every { runtimeConfig.getStringArrayStoredAsArrayOrCSV(AppServiceHosts) } returns null + every { runtimeConfig.getStringArrayStoredAsArrayOrCSV(AppServiceHostLabels) } returns null + + loginServerManager = LoginServerManager(context, runtimeConfig, servers) + + val selectedLoginServer = loginServerManager?.selectedLoginServer + + assertEquals("Default Login Server", selectedLoginServer?.name) + assertEquals("https://default.example.com", selectedLoginServer?.url) + } + + /** + * Test for testRemovedSelectedLoginServer. + */ + @Test + fun testRemovedSelectedLoginServer() { + + val sharedPreferencesSelectedServer = mockk(relaxed = true) + every { sharedPreferencesSelectedServer.getString(SERVER_NAME, null) } returns "Selected Login Server" + every { sharedPreferencesSelectedServer.getString(SERVER_URL, null) } returns "https://selected.example.com" + every { sharedPreferencesSelectedServer.getBoolean(IS_CUSTOM, false) } returns false + + val sharedPreferences = mockk(relaxed = true) + every { sharedPreferences.getInt(NUMBER_OF_ENTRIES, 0) } returns 1 + every { sharedPreferences.getString(String.format(SERVER_NAME, 0), null) } returns "Default Login Server" + every { sharedPreferences.getString(String.format(SERVER_URL, 0), null) } returns "https://default.example.com" + every { sharedPreferences.getBoolean(String.format(IS_CUSTOM, 0), false) } returns false + + val context = mockk() + every { context.resources } returns getInstrumentation().targetContext.resources + every { context.getSharedPreferences(SERVER_SELECTION_FILE, any()) } returns sharedPreferencesSelectedServer + every { context.getSharedPreferences(SERVER_URL_FILE, any()) } returns sharedPreferences + every { context.getSharedPreferences(RUNTIME_PREFS_FILE, any()) } returns getInstrumentation().targetContext.getSharedPreferences(RUNTIME_PREFS_FILE, MODE_PRIVATE) + val runtimeConfig = mockk() + every { runtimeConfig.getStringArrayStoredAsArrayOrCSV(AppServiceHosts) } returns null + every { runtimeConfig.getStringArrayStoredAsArrayOrCSV(AppServiceHostLabels) } returns null + + loginServerManager = LoginServerManager(context, runtimeConfig, servers) + + val selectedLoginServer = loginServerManager?.selectedLoginServer + + assertEquals("Default Login Server", selectedLoginServer?.name) + assertEquals("https://default.example.com", selectedLoginServer?.url) + } + + /** + * Test for testNullSelectedLoginServer. + * TODO: Correct this test. ECJ20260303 + */ + @Test + fun testNullSelectedLoginServer() { + + val sharedPreferencesSelectedServer = mockk(relaxed = true) + every { sharedPreferencesSelectedServer.getString(SERVER_NAME, null) } returns null + every { sharedPreferencesSelectedServer.getString(SERVER_URL, null) } returns null + every { sharedPreferencesSelectedServer.getBoolean(IS_CUSTOM, false) } returns false + + val sharedPreferences = mockk(relaxed = true) + every { sharedPreferences.getInt(NUMBER_OF_ENTRIES, 0) } returns 1 + every { sharedPreferences.getString(String.format(SERVER_NAME, 0), null) } returns null + every { sharedPreferences.getString(String.format(SERVER_URL, 0), null) } returns null + every { sharedPreferences.getBoolean(String.format(IS_CUSTOM, 0), false) } returns false + + val context = mockk() + every { context.resources } returns getInstrumentation().targetContext.resources + every { context.getSharedPreferences(SERVER_SELECTION_FILE, any()) } returns sharedPreferencesSelectedServer + every { context.getSharedPreferences(SERVER_URL_FILE, any()) } returns sharedPreferences + every { context.getSharedPreferences(RUNTIME_PREFS_FILE, any()) } returns getInstrumentation().targetContext.getSharedPreferences(RUNTIME_PREFS_FILE, MODE_PRIVATE) + val runtimeConfig = mockk() + every { runtimeConfig.getStringArrayStoredAsArrayOrCSV(AppServiceHosts) } returns null + every { runtimeConfig.getStringArrayStoredAsArrayOrCSV(AppServiceHostLabels) } returns null + + loginServerManager = LoginServerManager(context, runtimeConfig, servers) + + val selectedLoginServer = loginServerManager?.selectedLoginServer + + assertEquals("Production", selectedLoginServer?.name) + assertEquals("https://login.salesforce.com", selectedLoginServer?.url) + } + /** * Test for testNullsInSharedPreferencesLoginServers. */ @@ -400,7 +576,7 @@ class LoginServerManagerTestKt { } /** - * Test for testNullsInSharedPreferencesLoginServers. + * Test for testEmptyInSharedPreferencesLoginServers. */ @Test fun testEmptyInSharedPreferencesLoginServers() { @@ -445,4 +621,32 @@ class LoginServerManagerTestKt { assertEquals("https://login.example.com", loginServers?.get(0)?.url) assertEquals(false, loginServers?.get(0)?.isCustom) } + + /** + * Test for testErrorInSharedPreferencesLoginServers. + */ + @Test + fun testErrorInSharedPreferencesLoginServers() { + val xmlResourceParser = mockk(relaxed = true) + every { xmlResourceParser.next() } throws XmlPullParserException("Error in XML") + + val resources = mockk(relaxed = true) + every { resources.getXml(any()) } returns xmlResourceParser + + val context = mockk() + every { context.resources } returns resources + every { context.getString(sf__auth_login_production) } returns "Production" + every { context.getString(sf__auth_login_sandbox) } returns "Sandbox" + every { context.getSharedPreferences(SERVER_SELECTION_FILE, any()) } returns getInstrumentation().targetContext.getSharedPreferences(SERVER_SELECTION_FILE, MODE_PRIVATE) + every { context.getSharedPreferences(SERVER_URL_FILE, any()) } returns getInstrumentation().targetContext.getSharedPreferences(SERVER_URL_FILE, MODE_PRIVATE) + every { context.getSharedPreferences(RUNTIME_PREFS_FILE, any()) } returns getInstrumentation().targetContext.getSharedPreferences(RUNTIME_PREFS_FILE, MODE_PRIVATE) + val runtimeConfig = mockk() + every { runtimeConfig.getStringArrayStoredAsArrayOrCSV(AppServiceHosts) } returns null + every { runtimeConfig.getStringArrayStoredAsArrayOrCSV(AppServiceHostLabels) } returns null + + loginServerManager = LoginServerManager(context, runtimeConfig, servers) + + val loginServers = loginServerManager?.loginServers + assertEquals("Wrong number of servers", 2, loginServers?.size) + } } From 51d888c124802913e7976fe80c55445fde0d70d9 Mon Sep 17 00:00:00 2001 From: "Eric C. Johnson" Date: Tue, 3 Mar 2026 20:18:57 -0700 Subject: [PATCH 11/14] @W-21148602: Internal Server List does not respect changes made to Servers.xml file (Additional Tests And Improved Branching Logic In `removeServer`) --- .../androidsdk/config/LoginServerManager.java | 13 +- .../androidsdk/auth/LoginServerManagerTest.kt | 171 +++++++++++++++++- 2 files changed, 174 insertions(+), 10 deletions(-) diff --git a/libs/SalesforceSDK/src/com/salesforce/androidsdk/config/LoginServerManager.java b/libs/SalesforceSDK/src/com/salesforce/androidsdk/config/LoginServerManager.java index 6562f31cdd..b1fd785970 100644 --- a/libs/SalesforceSDK/src/com/salesforce/androidsdk/config/LoginServerManager.java +++ b/libs/SalesforceSDK/src/com/salesforce/androidsdk/config/LoginServerManager.java @@ -321,7 +321,8 @@ public void removeServer(final LoginServer server) { * @param allowNonCustomRemoval Boolean true allows the removal of non-custom login servers and * false does not */ - private void removeServer( + @VisibleForTesting + public void removeServer( final LoginServer server, final SharedPreferences sharedPreferences, final boolean allowNonCustomRemoval @@ -330,8 +331,8 @@ private void removeServer( int index = servers.indexOf(server); - // TODO: Unrelated Coverage Needed. ECJ20260303 - if (allowNonCustomRemoval || server.isCustom && index != -1) { + boolean removalAlwaysAllowed = server.isCustom && index != -1; + if (allowNonCustomRemoval || removalAlwaysAllowed) { int numServers = servers.size(); Deque stack = new ArrayDeque<>(servers.subList(index + 1, numServers)); @@ -397,13 +398,10 @@ private boolean isRuntimeConfigAppServiceHostsSet() { public List getLoginServersFromRuntimeConfig() { String[] mdmLoginServers = runtimeConfig.getStringArrayStoredAsArrayOrCSV(AppServiceHosts); final List allServers = new ArrayList<>(); - // TODO: Coverage Needed. ECJ20260303 if (mdmLoginServers != null) { String[] mdmLoginServersLabels = runtimeConfig.getStringArrayStoredAsArrayOrCSV(AppServiceHostLabels); if (mdmLoginServersLabels == null || mdmLoginServersLabels.length != mdmLoginServers.length) { - // TODO: Coverage Needed. ECJ20260303 SalesforceSDKLogger.w(TAG, "No login servers labels provided or wrong number of login servers labels provided - using URLs for the labels"); - // TODO: Coverage Needed. ECJ20260303 mdmLoginServersLabels = mdmLoginServers; } @@ -413,7 +411,6 @@ public List getLoginServersFromRuntimeConfig() { for (int i = 0; i < mdmLoginServers.length; i++) { final String name = mdmLoginServersLabels[i]; final String url = mdmLoginServers[i]; - // TODO: Coverage Needed. ECJ20260303 if (name == null || url == null) { continue; } final LoginServer server = new LoginServer(name, url, false); @@ -426,7 +423,6 @@ public List getLoginServersFromRuntimeConfig() { allServers.add(server); } } - // TODO: Coverage Needed. ECJ20260303 return (!allServers.isEmpty() ? allServers : null); } @@ -725,7 +721,6 @@ private void persistLoginServer(final @NonNull String name, final LoginServer server = new LoginServer(name, url.trim(), isCustom); allServers.add(server); } else { - // TODO: Coverage Needed. ECJ20260303 SalesforceSDKLogger.w(TAG, "Invalid login server found in preferences"); } } diff --git a/libs/test/SalesforceSDKTest/src/com/salesforce/androidsdk/auth/LoginServerManagerTest.kt b/libs/test/SalesforceSDKTest/src/com/salesforce/androidsdk/auth/LoginServerManagerTest.kt index e7073f7ac3..21be482d34 100644 --- a/libs/test/SalesforceSDKTest/src/com/salesforce/androidsdk/auth/LoginServerManagerTest.kt +++ b/libs/test/SalesforceSDKTest/src/com/salesforce/androidsdk/auth/LoginServerManagerTest.kt @@ -12,6 +12,7 @@ import com.salesforce.androidsdk.R.string.sf__auth_login_production import com.salesforce.androidsdk.R.string.sf__auth_login_sandbox import com.salesforce.androidsdk.config.LoginServerManager import com.salesforce.androidsdk.config.LoginServerManager.IS_CUSTOM +import com.salesforce.androidsdk.config.LoginServerManager.LoginServer import com.salesforce.androidsdk.config.LoginServerManager.NUMBER_OF_ENTRIES import com.salesforce.androidsdk.config.LoginServerManager.RUNTIME_PREFS_FILE import com.salesforce.androidsdk.config.LoginServerManager.SERVER_NAME @@ -493,9 +494,76 @@ class LoginServerManagerTestKt { assertEquals("https://default.example.com", selectedLoginServer?.url) } + /** + * Test for testRemovedNameSelectedLoginServer. + */ + @Test + fun testRemovedNameSelectedLoginServer() { + + val sharedPreferencesSelectedServer = mockk(relaxed = true) + every { sharedPreferencesSelectedServer.getString(SERVER_NAME, null) } returns "Selected Login Server" + every { sharedPreferencesSelectedServer.getString(SERVER_URL, null) } returns "https://selected.example.com" + every { sharedPreferencesSelectedServer.getBoolean(IS_CUSTOM, false) } returns false + + val sharedPreferences = mockk(relaxed = true) + every { sharedPreferences.getInt(NUMBER_OF_ENTRIES, 0) } returns 1 + every { sharedPreferences.getString(String.format(SERVER_NAME, 0), null) } returns "Default Login Server" + every { sharedPreferences.getString(String.format(SERVER_URL, 0), null) } returns "https://selected.example.com" + every { sharedPreferences.getBoolean(String.format(IS_CUSTOM, 0), false) } returns false + + val context = mockk() + every { context.resources } returns getInstrumentation().targetContext.resources + every { context.getSharedPreferences(SERVER_SELECTION_FILE, any()) } returns sharedPreferencesSelectedServer + every { context.getSharedPreferences(SERVER_URL_FILE, any()) } returns sharedPreferences + every { context.getSharedPreferences(RUNTIME_PREFS_FILE, any()) } returns getInstrumentation().targetContext.getSharedPreferences(RUNTIME_PREFS_FILE, MODE_PRIVATE) + val runtimeConfig = mockk() + every { runtimeConfig.getStringArrayStoredAsArrayOrCSV(AppServiceHosts) } returns null + every { runtimeConfig.getStringArrayStoredAsArrayOrCSV(AppServiceHostLabels) } returns null + + loginServerManager = LoginServerManager(context, runtimeConfig, servers) + + val selectedLoginServer = loginServerManager?.selectedLoginServer + + assertEquals("Default Login Server", selectedLoginServer?.name) + assertEquals("https://selected.example.com", selectedLoginServer?.url) + } + + /** + * Test for testRemovedUrlSelectedLoginServer. + */ + @Test + fun testRemovedUrlSelectedLoginServer() { + + val sharedPreferencesSelectedServer = mockk(relaxed = true) + every { sharedPreferencesSelectedServer.getString(SERVER_NAME, null) } returns "Selected Login Server" + every { sharedPreferencesSelectedServer.getString(SERVER_URL, null) } returns "https://selected.example.com" + every { sharedPreferencesSelectedServer.getBoolean(IS_CUSTOM, false) } returns false + + val sharedPreferences = mockk(relaxed = true) + every { sharedPreferences.getInt(NUMBER_OF_ENTRIES, 0) } returns 1 + every { sharedPreferences.getString(String.format(SERVER_NAME, 0), null) } returns "Selected Login Server" + every { sharedPreferences.getString(String.format(SERVER_URL, 0), null) } returns "https://default.example.com" + every { sharedPreferences.getBoolean(String.format(IS_CUSTOM, 0), false) } returns false + + val context = mockk() + every { context.resources } returns getInstrumentation().targetContext.resources + every { context.getSharedPreferences(SERVER_SELECTION_FILE, any()) } returns sharedPreferencesSelectedServer + every { context.getSharedPreferences(SERVER_URL_FILE, any()) } returns sharedPreferences + every { context.getSharedPreferences(RUNTIME_PREFS_FILE, any()) } returns getInstrumentation().targetContext.getSharedPreferences(RUNTIME_PREFS_FILE, MODE_PRIVATE) + val runtimeConfig = mockk() + every { runtimeConfig.getStringArrayStoredAsArrayOrCSV(AppServiceHosts) } returns null + every { runtimeConfig.getStringArrayStoredAsArrayOrCSV(AppServiceHostLabels) } returns null + + loginServerManager = LoginServerManager(context, runtimeConfig, servers) + + val selectedLoginServer = loginServerManager?.selectedLoginServer + + assertEquals("Selected Login Server", selectedLoginServer?.name) + assertEquals("https://default.example.com", selectedLoginServer?.url) + } + /** * Test for testNullSelectedLoginServer. - * TODO: Correct this test. ECJ20260303 */ @Test fun testNullSelectedLoginServer() { @@ -649,4 +717,105 @@ class LoginServerManagerTestKt { val loginServers = loginServerManager?.loginServers assertEquals("Wrong number of servers", 2, loginServers?.size) } + + @Test + fun testRemoveServerNonCustomNotFound() { + val context = mockk() + every { context.resources } returns getInstrumentation().targetContext.resources + every { context.getSharedPreferences(SERVER_SELECTION_FILE, any()) } returns getInstrumentation().targetContext.getSharedPreferences(SERVER_SELECTION_FILE, MODE_PRIVATE) + every { context.getSharedPreferences(SERVER_URL_FILE, any()) } returns getInstrumentation().targetContext.getSharedPreferences(SERVER_URL_FILE, MODE_PRIVATE) + every { context.getSharedPreferences(RUNTIME_PREFS_FILE, any()) } returns getInstrumentation().targetContext.getSharedPreferences(RUNTIME_PREFS_FILE, MODE_PRIVATE) + val runtimeConfig = mockk() + every { runtimeConfig.getStringArrayStoredAsArrayOrCSV(AppServiceHosts) } returns arrayOf("https://mdm1.example.com/1", "https://mdm2.example.com/2") + every { runtimeConfig.getStringArrayStoredAsArrayOrCSV(AppServiceHostLabels) } returns arrayOf("MDM 1", "MDM 2") + + loginServerManager = LoginServerManager(context, runtimeConfig, servers) + + val loginServer = LoginServer("MDM 3", "https://mdm3.example.com/3", false) + loginServerManager?.removeServer(loginServer) + + val servers = loginServerManager?.loginServers + + assertEquals("Wrong number of servers", 2, servers?.size) + assertEquals("MDM 1", servers?.get(0)?.name) + assertEquals("https://mdm1.example.com/1", servers?.get(0)?.url) + assertEquals(false, servers?.get(0)?.isCustom) + assertEquals("MDM 2", servers?.get(1)?.name) + assertEquals("https://mdm2.example.com/2", servers?.get(1)?.url) + assertEquals(false, servers?.get(1)?.isCustom) + + assertEquals("MDM 1", loginServerManager?.getSelectedLoginServer()?.name) + assertEquals("https://mdm1.example.com/1", loginServerManager?.getSelectedLoginServer()?.url) + assertEquals(false, loginServerManager?.getSelectedLoginServer()?.isCustom) + } + + @Test + fun testRemoveServerCustomNotFound() { + val context = mockk() + every { context.resources } returns getInstrumentation().targetContext.resources + every { context.getSharedPreferences(SERVER_SELECTION_FILE, any()) } returns getInstrumentation().targetContext.getSharedPreferences(SERVER_SELECTION_FILE, MODE_PRIVATE) + every { context.getSharedPreferences(SERVER_URL_FILE, any()) } returns getInstrumentation().targetContext.getSharedPreferences(SERVER_URL_FILE, MODE_PRIVATE) + every { context.getSharedPreferences(RUNTIME_PREFS_FILE, any()) } returns getInstrumentation().targetContext.getSharedPreferences(RUNTIME_PREFS_FILE, MODE_PRIVATE) + val runtimeConfig = mockk() + every { runtimeConfig.getStringArrayStoredAsArrayOrCSV(AppServiceHosts) } returns arrayOf("https://mdm1.example.com/1", "https://mdm2.example.com/2") + every { runtimeConfig.getStringArrayStoredAsArrayOrCSV(AppServiceHostLabels) } returns arrayOf("MDM 1", "MDM 2") + + loginServerManager = LoginServerManager(context, runtimeConfig, servers) + + val loginServer = LoginServer("MDM 3", "https://mdm3.example.com/3", true) + loginServerManager?.removeServer(loginServer) + + val servers = loginServerManager?.loginServers + + assertEquals("Wrong number of servers", 2, servers?.size) + assertEquals("MDM 1", servers?.get(0)?.name) + assertEquals("https://mdm1.example.com/1", servers?.get(0)?.url) + assertEquals(false, servers?.get(0)?.isCustom) + assertEquals("MDM 2", servers?.get(1)?.name) + assertEquals("https://mdm2.example.com/2", servers?.get(1)?.url) + assertEquals(false, servers?.get(1)?.isCustom) + + assertEquals("MDM 1", loginServerManager?.getSelectedLoginServer()?.name) + assertEquals("https://mdm1.example.com/1", loginServerManager?.getSelectedLoginServer()?.url) + assertEquals(false, loginServerManager?.getSelectedLoginServer()?.isCustom) + } + + @Test + fun testRemoveServerCustomFoundWithAllowsAllowNonCustomRemoval() { + val context = mockk() + every { context.resources } returns getInstrumentation().targetContext.resources + every { context.getSharedPreferences(SERVER_SELECTION_FILE, any()) } returns getInstrumentation().targetContext.getSharedPreferences(SERVER_SELECTION_FILE, MODE_PRIVATE) + every { context.getSharedPreferences(SERVER_URL_FILE, any()) } returns getInstrumentation().targetContext.getSharedPreferences(SERVER_URL_FILE, MODE_PRIVATE) + every { context.getSharedPreferences(RUNTIME_PREFS_FILE, any()) } returns getInstrumentation().targetContext.getSharedPreferences(RUNTIME_PREFS_FILE, MODE_PRIVATE) + val runtimeConfig = mockk() + every { runtimeConfig.getStringArrayStoredAsArrayOrCSV(AppServiceHosts) } returns arrayOf("https://mdm1.example.com/1", "https://mdm2.example.com/2") + every { runtimeConfig.getStringArrayStoredAsArrayOrCSV(AppServiceHostLabels) } returns arrayOf("MDM 1", "MDM 2") + + loginServerManager = LoginServerManager(context, runtimeConfig, servers) + loginServerManager?.addCustomLoginServer("MDM 3", "https://mdm3.example.com/3") + + var servers = loginServerManager?.loginServers + + assertEquals("Wrong number of servers", 3, servers?.size) + assertEquals("MDM 3", servers?.get(2)?.name) + assertEquals("https://mdm3.example.com/3", servers?.get(2)?.url) + assertEquals(true, servers?.get(2)?.isCustom) + + val loginServer = LoginServer("MDM 3", "https://mdm3.example.com/3", true) + loginServerManager?.removeServer(loginServer, context.getSharedPreferences(RUNTIME_PREFS_FILE, MODE_PRIVATE), true) + + servers = loginServerManager?.loginServers + + assertEquals("Wrong number of servers", 2, servers?.size) + assertEquals("MDM 1", servers?.get(0)?.name) + assertEquals("https://mdm1.example.com/1", servers?.get(0)?.url) + assertEquals(false, servers?.get(0)?.isCustom) + assertEquals("MDM 2", servers?.get(1)?.name) + assertEquals("https://mdm2.example.com/2", servers?.get(1)?.url) + assertEquals(false, servers?.get(1)?.isCustom) + + assertEquals("MDM 1", loginServerManager?.getSelectedLoginServer()?.name) + assertEquals("https://mdm1.example.com/1", loginServerManager?.getSelectedLoginServer()?.url) + assertEquals(false, loginServerManager?.getSelectedLoginServer()?.isCustom) + } } From a48ed4d96295c1f3f64557814679b661e2fb9633 Mon Sep 17 00:00:00 2001 From: "Eric C. Johnson" Date: Wed, 4 Mar 2026 12:10:21 -0700 Subject: [PATCH 12/14] @W-21148602: Internal Server List does not respect changes made to Servers.xml file (Improved Null Handling In `getLoginServersFromRuntimeConfig`) --- .../androidsdk/config/LoginServerManager.java | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/libs/SalesforceSDK/src/com/salesforce/androidsdk/config/LoginServerManager.java b/libs/SalesforceSDK/src/com/salesforce/androidsdk/config/LoginServerManager.java index b1fd785970..ebcd3e8ad8 100644 --- a/libs/SalesforceSDK/src/com/salesforce/androidsdk/config/LoginServerManager.java +++ b/libs/SalesforceSDK/src/com/salesforce/androidsdk/config/LoginServerManager.java @@ -32,6 +32,7 @@ import static com.salesforce.androidsdk.config.RuntimeConfig.ConfigKey.AppServiceHosts; import static com.salesforce.androidsdk.config.RuntimeConfig.getRuntimeConfig; import static java.lang.String.format; +import static java.util.Arrays.asList; import static java.util.Locale.US; import android.content.Context; @@ -52,6 +53,7 @@ import java.util.ArrayList; import java.util.Deque; import java.util.List; +import java.util.Objects; /** * Class to manage login hosts (default and user entered). @@ -396,7 +398,7 @@ private boolean isRuntimeConfigAppServiceHostsSet() { */ @SuppressWarnings("UnusedReturnValue") public List getLoginServersFromRuntimeConfig() { - String[] mdmLoginServers = runtimeConfig.getStringArrayStoredAsArrayOrCSV(AppServiceHosts); + final String[] mdmLoginServers = runtimeConfig.getStringArrayStoredAsArrayOrCSV(AppServiceHosts); final List allServers = new ArrayList<>(); if (mdmLoginServers != null) { String[] mdmLoginServersLabels = runtimeConfig.getStringArrayStoredAsArrayOrCSV(AppServiceHostLabels); @@ -408,10 +410,15 @@ public List getLoginServersFromRuntimeConfig() { // Reset non-custom servers from Mobile Device Management (MDM). resetNonCustomLoginServers(runtimePrefs); - for (int i = 0; i < mdmLoginServers.length; i++) { - final String name = mdmLoginServersLabels[i]; - final String url = mdmLoginServers[i]; - if (name == null || url == null) { continue; } + // Null-cleanse MDM login server URLs and names. + final List mdmLoginServersList = asList(mdmLoginServers); + mdmLoginServersList.removeIf(Objects::isNull); + final List mdmLoginServersLabelsList = asList(mdmLoginServersLabels); + mdmLoginServersLabelsList.removeIf(Objects::isNull); + + for (int i = 0; i < mdmLoginServersList.size(); i++) { + final String name = mdmLoginServersLabelsList.get(i); + final String url = mdmLoginServersList.get(i); final LoginServer server = new LoginServer(name, url, false); persistLoginServer( From 8724b7eefaa6e41f42a9888b1fb5ff97e1df627d Mon Sep 17 00:00:00 2001 From: "Eric C. Johnson" Date: Wed, 4 Mar 2026 16:21:51 -0700 Subject: [PATCH 13/14] @W-21148602: Internal Server List does not respect changes made to Servers.xml file (Light Cleanup) --- .../androidsdk/config/LoginServerManager.java | 15 +++++++++------ .../androidsdk/auth/LoginServerManagerTest.kt | 6 +----- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/libs/SalesforceSDK/src/com/salesforce/androidsdk/config/LoginServerManager.java b/libs/SalesforceSDK/src/com/salesforce/androidsdk/config/LoginServerManager.java index ebcd3e8ad8..e978af4e67 100644 --- a/libs/SalesforceSDK/src/com/salesforce/androidsdk/config/LoginServerManager.java +++ b/libs/SalesforceSDK/src/com/salesforce/androidsdk/config/LoginServerManager.java @@ -31,6 +31,8 @@ import static com.salesforce.androidsdk.config.RuntimeConfig.ConfigKey.AppServiceHostLabels; import static com.salesforce.androidsdk.config.RuntimeConfig.ConfigKey.AppServiceHosts; import static com.salesforce.androidsdk.config.RuntimeConfig.getRuntimeConfig; +import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT; +import static org.xmlpull.v1.XmlPullParser.START_TAG; import static java.lang.String.format; import static java.util.Arrays.asList; import static java.util.Locale.US; @@ -38,7 +40,7 @@ import android.content.Context; import android.content.SharedPreferences; import android.content.SharedPreferences.Editor; -import android.content.res.Resources; +import android.content.res.Resources.NotFoundException; import android.content.res.XmlResourceParser; import android.os.Looper; @@ -395,6 +397,9 @@ private boolean isRuntimeConfigAppServiceHostsSet() { /** * Resets the list of Mobile Device Management (MDM) login servers from the runtime * configuration. This does not remove the user's custom login servers. + * + * @return The list of login servers from the runtime configuration, not + * including the user's custom login servers */ @SuppressWarnings("UnusedReturnValue") public List getLoginServersFromRuntimeConfig() { @@ -574,13 +579,13 @@ private List getLoginServersFromXML() { XmlResourceParser xml; try { xml = ctx.getResources().getXml(serversXmlResourceId); - } catch (Resources.NotFoundException e) { + } catch (NotFoundException e) { return loginServers; } int eventType = -1; - while (eventType != XmlResourceParser.END_DOCUMENT) { - if (eventType == XmlResourceParser.START_TAG) { + while (eventType != END_DOCUMENT) { + if (eventType == START_TAG) { if (xml.getName().equals("server")) { final String name = xml.getAttributeValue(null, "name"); final String url = xml.getAttributeValue(null, "url"); @@ -727,8 +732,6 @@ private void persistLoginServer(final @NonNull String name, if (name != null && url != null) { final LoginServer server = new LoginServer(name, url.trim(), isCustom); allServers.add(server); - } else { - SalesforceSDKLogger.w(TAG, "Invalid login server found in preferences"); } } return allServers; diff --git a/libs/test/SalesforceSDKTest/src/com/salesforce/androidsdk/auth/LoginServerManagerTest.kt b/libs/test/SalesforceSDKTest/src/com/salesforce/androidsdk/auth/LoginServerManagerTest.kt index 21be482d34..07d425e503 100644 --- a/libs/test/SalesforceSDKTest/src/com/salesforce/androidsdk/auth/LoginServerManagerTest.kt +++ b/libs/test/SalesforceSDKTest/src/com/salesforce/androidsdk/auth/LoginServerManagerTest.kt @@ -781,7 +781,7 @@ class LoginServerManagerTestKt { } @Test - fun testRemoveServerCustomFoundWithAllowsAllowNonCustomRemoval() { + fun testRemoveServerCustomFoundWithAllowNonCustomRemoval() { val context = mockk() every { context.resources } returns getInstrumentation().targetContext.resources every { context.getSharedPreferences(SERVER_SELECTION_FILE, any()) } returns getInstrumentation().targetContext.getSharedPreferences(SERVER_SELECTION_FILE, MODE_PRIVATE) @@ -813,9 +813,5 @@ class LoginServerManagerTestKt { assertEquals("MDM 2", servers?.get(1)?.name) assertEquals("https://mdm2.example.com/2", servers?.get(1)?.url) assertEquals(false, servers?.get(1)?.isCustom) - - assertEquals("MDM 1", loginServerManager?.getSelectedLoginServer()?.name) - assertEquals("https://mdm1.example.com/1", loginServerManager?.getSelectedLoginServer()?.url) - assertEquals(false, loginServerManager?.getSelectedLoginServer()?.isCustom) } } From 0406497ce9787147a8e45436b20b881786ad660f Mon Sep 17 00:00:00 2001 From: "Eric C. Johnson" Date: Thu, 5 Mar 2026 17:08:00 -0700 Subject: [PATCH 14/14] @W-21148602: Internal Server List does not respect changes made to Servers.xml file (Rename Kotlin/Mockk Tests For `LoginServerManager` To `LoginServerManagerMockTest.kt`) --- .../androidsdk/auth/LoginServerManagerTest.kt | 28 ++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/libs/test/SalesforceSDKTest/src/com/salesforce/androidsdk/auth/LoginServerManagerTest.kt b/libs/test/SalesforceSDKTest/src/com/salesforce/androidsdk/auth/LoginServerManagerTest.kt index 07d425e503..900c0b83b6 100644 --- a/libs/test/SalesforceSDKTest/src/com/salesforce/androidsdk/auth/LoginServerManagerTest.kt +++ b/libs/test/SalesforceSDKTest/src/com/salesforce/androidsdk/auth/LoginServerManagerTest.kt @@ -1,3 +1,29 @@ +/* + * Copyright (c) 2026-present, salesforce.com, inc. + * All rights reserved. + * Redistribution and use of this software in source and binary forms, with or + * without modification, are permitted provided that the following conditions + * are met: + * - Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of salesforce.com, inc. nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission of salesforce.com, inc. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ package com.salesforce.androidsdk.auth import android.content.Context @@ -36,7 +62,7 @@ import org.xmlpull.v1.XmlPullParserException @RunWith(AndroidJUnit4::class) @SmallTest -class LoginServerManagerTestKt { +class LoginServerManagerMockTest { private var loginServerManager: LoginServerManager? = null