From 11fa37ebb49e4ec4a975d111bf3512139b905062 Mon Sep 17 00:00:00 2001 From: Matthew Fritze Date: Tue, 17 Jul 2018 13:47:39 -0700 Subject: [PATCH] Add Settings Search Regression test Take a snapshot of the currently available search results, and verify that search results aren't accidentally removed. We use 4 items to identify a search result: - Title - Data Key - Slice Uri We use Title & Key to identify the search result, since they should not change. The Slice Uri is used to make sure we don't regress on Slice Availability. Test: runtest --path packages/apps/Settings/tests/unit/src/com/android/settings/search/SettingsSearchResultRegressionTest.java Change-Id: I22498229bbcd1e90c9e0a026af9df4367a98190a --- tests/uitests/AndroidManifest.xml | 1 + tests/uitests/assets/search_results_list | 623 ++++++++++++++++++ .../android/settings/search/SearchData.java | 81 +++ .../SettingsSearchResultRegressionTest.java | 210 ++++++ 4 files changed, 915 insertions(+) create mode 100644 tests/uitests/assets/search_results_list create mode 100644 tests/uitests/src/com/android/settings/search/SearchData.java create mode 100644 tests/uitests/src/com/android/settings/search/SettingsSearchResultRegressionTest.java diff --git a/tests/uitests/AndroidManifest.xml b/tests/uitests/AndroidManifest.xml index 91e15b8b43d..49a2fd110aa 100644 --- a/tests/uitests/AndroidManifest.xml +++ b/tests/uitests/AndroidManifest.xml @@ -28,6 +28,7 @@ + + * The data set used here (/tests/unit/assets/search_results_list) needs to be updated + * every once in a while so that we can check newly added results. + *

+ */ + @Test + @Presubmit + public void searchResultsDoNotRegress() { + final ContentResolver resolver = mContext.getContentResolver(); + final Uri uri = getTestProviderUri(); + final Cursor cursor = resolver.query(uri, null, null, null, null); + + if (cursor == null) { + // Assume Settings Intelligence is wrong. + return; + } + + final Set availableSearchResults = getSearchDataFromCursor(cursor); + final Set registeredSearchResults = getRegisteredResults(); + + // Seed with results that we expect + final Set missingSearchResults = new HashSet<>(registeredSearchResults); + // Seed with results that are available + final Set newSearchResults = new HashSet<>(availableSearchResults); + + // Remove all available results, leaving results that have been removed. + missingSearchResults.removeAll(availableSearchResults); + // Remove all results we expect, leaving results that have not yet been registered. + newSearchResults.removeAll(registeredSearchResults); + + assertWithMessage(ERROR_RESULTS_MISSING + ERROR_RERUN_TEST) + .that(missingSearchResults).isEmpty(); + assertWithMessage(ERROR_NEW_RESULTS + ERROR_RERUN_TEST).that(newSearchResults).isEmpty(); + } + + // TODO (b/113907111) add a test to catch duplicate title search results. + + /** + * Test to generate a new list of search results. Uncomment the Test annotation and run the + * test to generate the list. + */ + @Ignore + @Test + public void generate_search_result_list() { + final ContentResolver resolver = mContext.getContentResolver(); + final Uri uri = getTestProviderUri(); + final Cursor cursor = resolver.query(uri, null, null, null, null); + final List availableSearchResults = + new ArrayList<>(getSearchDataFromCursor(cursor)); + + Collections.sort(availableSearchResults, Comparator.comparing(SearchData::getTitle) + .thenComparing(SearchData::getKey)); + + assertThat(generateListFromSearchData(availableSearchResults)).isNull(); + } + + private Set getSearchDataFromCursor(Cursor cursor) { + final Set searchData = new HashSet<>(); + + final int titleIndex = cursor.getColumnIndex( + IndexColumns.DATA_TITLE); + final int keyIndex = cursor.getColumnIndex( + IndexColumns.DATA_KEY_REF); + + while (cursor.moveToNext()) { + String title = cursor.getString(titleIndex); + String key = cursor.getString(keyIndex); + + if (TextUtils.isEmpty(title)) { + title = ""; + } + + if (TextUtils.isEmpty(key)) { + key = ""; + } + + searchData.add(new SearchData.Builder() + .setTitle(title) + .setKey(key) + .build()); + } + + return searchData; + } + + /** + * Utility method to generate the list of search results that this class uses to validate + * results. + */ + private String generateListFromSearchData(List searchData) { + StringBuilder builder = new StringBuilder(); + for (SearchData searchResult : searchData) { + builder.append(searchResult.title) + .append( + SearchData.DELIM) + .append(searchResult.key) + .append("\n"); + } + return builder.toString(); + } + + private Uri getTestProviderUri() { + return new Uri.Builder() + .scheme(ContentResolver.SCHEME_CONTENT) + .authority("com.google.android.settings.intelligence.modules.search.regression") + .build(); + } + + private Set getRegisteredResults() { + final String filename = "search_results_list"; + final Set registeredResults = new HashSet<>(); + + try { + final InputStream in = mContext.getAssets().open(filename); + BufferedReader reader = new BufferedReader(new InputStreamReader(in)); + String line; + while ((line = reader.readLine()) != null) { + registeredResults.add( + SearchData.from(line)); + } + } catch (Exception e) { + throw new IllegalArgumentException("Error initializing registered result list " + + filename, e); + } + + return registeredResults; + } +} \ No newline at end of file