Bug 907768 - Rewrite search suggestion test hooks, fix network bypass in SuggestClient. r=bnicholson

This patch fixes all testSearchSuggestions intermittent failures, except for those that appear to be caused by update checks on 2.3.

It also replaces all waitForTest (deprecated) calls with waitForCondition.
This commit is contained in:
Richard Newman 2015-01-05 15:12:03 -08:00
parent ea0230fdb7
commit 6adb3eb176
4 changed files with 86 additions and 82 deletions

View File

@ -4,19 +4,6 @@
package org.mozilla.gecko;
import org.mozilla.gecko.AppConstants;
import org.mozilla.gecko.GeckoAppShell;
import org.mozilla.gecko.mozglue.RobocopTarget;
import org.mozilla.gecko.util.HardwareUtils;
import org.json.JSONArray;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.text.TextUtils;
import android.util.Log;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
@ -25,6 +12,16 @@ import java.net.URL;
import java.net.URLEncoder;
import java.util.ArrayList;
import org.json.JSONArray;
import org.mozilla.gecko.mozglue.RobocopTarget;
import org.mozilla.gecko.util.HardwareUtils;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.text.TextUtils;
import android.util.Log;
/**
* Use network-based search suggestions.
*/
@ -52,20 +49,13 @@ public class SuggestClient {
private String mPrevQuery;
private ArrayList<String> mPrevResults;
public SuggestClient(Context context, String suggestTemplate, int timeout, int maxResults) {
@RobocopTarget
public SuggestClient(Context context, String suggestTemplate, int timeout, int maxResults, boolean checkNetwork) {
mContext = context;
mMaxResults = maxResults;
mSuggestTemplate = suggestTemplate;
mTimeout = timeout;
mCheckNetwork = true;
}
/**
* This constructor is used exclusively by Robocop.
*/
@RobocopTarget
public SuggestClient(Context context, String suggestTemplate, int timeout) {
this(context, suggestTemplate, timeout, Integer.MAX_VALUE);
mCheckNetwork = checkNetwork;
}
/**

View File

@ -66,6 +66,26 @@ import android.widget.TextView;
*/
public class BrowserSearch extends HomeFragment
implements GeckoEventListener {
@RobocopTarget
public interface SuggestClientFactory {
public SuggestClient getSuggestClient(Context context, String template, int timeout, int max);
}
@RobocopTarget
public static class DefaultSuggestClientFactory implements SuggestClientFactory {
@Override
public SuggestClient getSuggestClient(Context context, String template, int timeout, int max) {
return new SuggestClient(context, template, timeout, max, true);
}
}
/**
* Set this to mock the suggestion mechanism. Public for access from tests.
*/
@RobocopTarget
public static volatile SuggestClientFactory sSuggestClientFactory = new DefaultSuggestClientFactory();
// Logging tag name
private static final String LOGTAG = "GeckoBrowserSearch";
@ -104,8 +124,10 @@ public class BrowserSearch extends HomeFragment
// The list showing search results
private HomeListView mList;
// Client that performs search suggestion queries
private volatile SuggestClient mSuggestClient;
// Client that performs search suggestion queries.
// Public for testing.
@RobocopTarget
public volatile SuggestClient mSuggestClient;
// List of search engines from Gecko.
// Do not mutate this list.
@ -549,12 +571,8 @@ public class BrowserSearch extends HomeFragment
final boolean isPrivate = (tab != null && tab.isPrivate());
// Only create a new instance of SuggestClient if it hasn't been
// set yet. e.g. Robocop tests might set it directly before search
// engines are loaded.
if (mSuggestClient == null && !isPrivate) {
setSuggestClient(new SuggestClient(getActivity(), suggestTemplate,
SUGGESTION_TIMEOUT, SUGGESTION_MAX));
}
// set yet.
maybeSetSuggestClient(suggestTemplate, isPrivate);
} else {
searchEngines.add(engine);
}
@ -579,18 +597,12 @@ public class BrowserSearch extends HomeFragment
filterSuggestions();
}
/**
* Sets the private SuggestClient instance. Should only be called if the suggestClient is
* null (i.e. has not yet been initialized or has been nulled). Non-private access is
* for testing purposes only.
*/
@RobocopTarget
public void setSuggestClient(final SuggestClient client) {
if (mSuggestClient != null) {
throw new IllegalStateException("Can only set the SuggestClient if it has not " +
"yet been initialized!");
private void maybeSetSuggestClient(final String suggestTemplate, final boolean isPrivate) {
if (mSuggestClient != null || isPrivate) {
return;
}
mSuggestClient = client;
mSuggestClient = sSuggestClientFactory.getSuggestClient(getActivity(), suggestTemplate, SUGGESTION_TIMEOUT, SUGGESTION_MAX);
}
private void showSuggestionsOptIn() {

View File

@ -3,17 +3,17 @@ package org.mozilla.gecko.tests;
import java.util.ArrayList;
import java.util.HashMap;
import org.mozilla.gecko.Actions;
import org.mozilla.gecko.R;
import org.mozilla.gecko.SuggestClient;
import org.mozilla.gecko.home.BrowserSearch;
import android.app.Activity;
import android.support.v4.app.Fragment;
import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.jayway.android.robotium.solo.Condition;
/**
* Test for search suggestions.
* Sends queries from AwesomeBar input and verifies that suggestions match
@ -21,11 +21,17 @@ import android.widget.TextView;
*/
public class testSearchSuggestions extends BaseTest {
private static final int SUGGESTION_MAX = 3;
private static final int SUGGESTION_TIMEOUT = 5000;
private static final int SUGGESTION_TIMEOUT = 15000;
private static final String TEST_QUERY = "foo barz";
private static final String SUGGESTION_TEMPLATE = "/robocop/robocop_suggestions.sjs?query=__searchTerms__";
public void testSearchSuggestions() {
// Mock the search system.
// The BrowserSearch UI only shows up once a non-empty
// search term is entered, but we swizzle in a new factory beforehand.
mockSuggestClientFactory();
blockForGeckoReady();
// Map of expected values. See robocop_suggestions.sjs.
@ -34,43 +40,44 @@ public class testSearchSuggestions extends BaseTest {
focusUrlBar();
// At this point we rely on our swizzling having worked -- which relies
// on us not having previously run a search.
// The test will fail later if there's already a BrowserSearch object with a
// suggest client set, so fail here.
BrowserSearch browserSearch = (BrowserSearch) getBrowserSearch();
mAsserter.ok(browserSearch == null ||
browserSearch.mSuggestClient == null,
"There is no existing search client.", "");
// Now test the incremental suggestions.
for (int i = 0; i < TEST_QUERY.length(); i++) {
Actions.EventExpecter enginesEventExpecter = null;
if (i == 0) {
enginesEventExpecter = mActions.expectGeckoEvent("SearchEngines:Data");
}
mActions.sendKeys(TEST_QUERY.substring(i, i+1));
// The BrowserSearch UI only shows up once a non-empty
// search term is entered
if (enginesEventExpecter != null) {
connectSuggestClient(getActivity());
enginesEventExpecter.blockForEvent();
enginesEventExpecter.unregisterListener();
enginesEventExpecter = null;
}
final String query = TEST_QUERY.substring(0, i+1);
mSolo.waitForView(R.id.suggestion_text);
boolean success = waitForTest(new BooleanTest() {
boolean success = waitForCondition(new Condition() {
@Override
public boolean test() {
// get the first suggestion row
public boolean isSatisfied() {
// Get the first suggestion row.
ViewGroup suggestionGroup = (ViewGroup) getActivity().findViewById(R.id.suggestion_layout);
if (suggestionGroup == null)
if (suggestionGroup == null) {
mAsserter.dumpLog("Fail: suggestionGroup is null.");
return false;
}
ArrayList<String> expected = suggestMap.get(query);
final ArrayList<String> expected = suggestMap.get(query);
for (int i = 0; i < expected.size(); i++) {
View queryChild = suggestionGroup.getChildAt(i);
if (queryChild == null || queryChild.getVisibility() == View.GONE)
if (queryChild == null || queryChild.getVisibility() == View.GONE) {
mAsserter.dumpLog("Fail: queryChild is null or GONE.");
return false;
}
String suggestion = ((TextView) queryChild.findViewById(R.id.suggestion_text)).getText().toString();
if (!suggestion.equals(expected.get(i)))
if (!suggestion.equals(expected.get(i))) {
mAsserter.dumpLog("Suggestion '" + suggestion + "' not equal to expected '" + expected.get(i) + "'.");
return false;
}
}
return true;
@ -93,21 +100,16 @@ public class testSearchSuggestions extends BaseTest {
suggestMap.put("foo barz", new ArrayList<String>() {{ add("foo barz"); }});
}
private void connectSuggestClient(final Activity activity) {
waitForTest(new BooleanTest() {
private void mockSuggestClientFactory() {
BrowserSearch.sSuggestClientFactory = new BrowserSearch.SuggestClientFactory() {
@Override
public boolean test() {
final Fragment browserSearch = getBrowserSearch();
return (browserSearch != null);
public SuggestClient getSuggestClient(Context context, String template, int timeout, int max) {
final String suggestTemplate = getAbsoluteRawUrl(SUGGESTION_TEMPLATE);
// This one uses our template, and also doesn't check for network accessibility.
return new SuggestClient(context, suggestTemplate, SUGGESTION_TIMEOUT, Integer.MAX_VALUE, false);
}
}, SUGGESTION_TIMEOUT);
final BrowserSearch browserSearch = (BrowserSearch) getBrowserSearch();
final String suggestTemplate = getAbsoluteRawUrl(SUGGESTION_TEMPLATE);
final SuggestClient client = new SuggestClient(activity, suggestTemplate,
SUGGESTION_TIMEOUT);
browserSearch.setSuggestClient(client);
};
}
}

View File

@ -133,7 +133,7 @@ public class SuggestionsFragment extends Fragment {
public void setEngine(SearchEngine engine) {
suggestClient = new SuggestClient(getActivity(), engine.getSuggestionTemplate(GECKO_SEARCH_TERMS_URL_PARAM),
SUGGESTION_TIMEOUT, SUGGESTION_MAX);
SUGGESTION_TIMEOUT, SUGGESTION_MAX, true);
}
public void loadSuggestions(String query) {