Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 837b7ead authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "[multi-part] Enabled bidirectional sorting" into nyc-andromeda-dev

parents ee1efcb6 28bbd8de
Loading
Loading
Loading
Loading
+197 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2016 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.documentsui;

import android.net.Uri;
import android.support.test.filters.LargeTest;

import com.android.documentsui.sorting.SortDimension;
import com.android.documentsui.sorting.SortModel;

@LargeTest
public class SortDocumentUiTest extends ActivityTest<FilesActivity> {

    private static final String DIR_1 = "folder_1";
    private static final String DIR_2 = "dir_2";

    private static final String FILE_1 = "file_1";
    private static final String FILE_2 = "doc_2";
    private static final String FILE_3 = "image_3";

    private static final String[] FILES = { FILE_1, FILE_3, FILE_2 };
    private static final String[] DIRS = { DIR_1, DIR_2 };

    private static final String[] DIRS_IN_NAME_ASC = { DIR_2, DIR_1 };
    private static final String[] DIRS_IN_NAME_DESC = reverse(DIRS_IN_NAME_ASC);
    private static final String[] FILES_IN_NAME_ASC = { FILE_2, FILE_1, FILE_3 };
    private static final String[] FILES_IN_NAME_DESC = reverse(FILES_IN_NAME_ASC);

    private static final String[] FILES_IN_SIZE_ASC = { FILE_2, FILE_1, FILE_3 };
    private static final String[] FILES_IN_SIZE_DESC = reverse(FILES_IN_SIZE_ASC);

    private static final String[] DIRS_IN_MODIFIED_DESC = reverse(DIRS);
    private static final String[] FILES_IN_MODIFIED_DESC = reverse(FILES);

    public SortDocumentUiTest() {
        super(FilesActivity.class);
    }

    @Override
    public void setUp() throws Exception {
        super.setUp();
        bots.roots.closeDrawer();
    }

    private void initFiles() throws Exception {
        initFiles(0);
    }

    /**
     * Initiate test files. It allows waiting between creations of files, so that we can assure
     * the modified date of each document is different.
     * @param sleep time to sleep in ms
     */
    private void initFiles(long sleep) throws Exception {
        for (String file : FILES) {
            Uri uri = mDocsHelper.createDocument(rootDir0, "text/plain", file);
            mDocsHelper.writeDocument(uri, file.getBytes());

            Thread.sleep(sleep);
        }

        for (String dir : DIRS) {
            mDocsHelper.createFolder(rootDir0, dir);

            Thread.sleep(sleep);
        }
    }

    public void testDefaultSortByNameAscending() throws Exception {
        initFiles();
        bots.directory.assertOrder(DIRS_IN_NAME_ASC, FILES_IN_NAME_ASC);
    }

    public void testSortByName_Descending_listMode() throws Exception {
        initFiles();

        bots.main.switchToListMode();

        bots.directory.sortBy(
                SortModel.SORT_DIMENSION_ID_TITLE, SortDimension.SORT_DIRECTION_DESCENDING);
        bots.directory.assertOrder(DIRS_IN_NAME_DESC, FILES_IN_NAME_DESC);
    }

    public void testSortBySize_Ascending_listMode() throws Exception {
        initFiles();

        bots.main.switchToListMode();

        bots.directory.sortBy(
                SortModel.SORT_DIMENSION_ID_SIZE, SortDimension.SORT_DIRECTION_ASCENDING);
        bots.directory.assertOrder(DIRS_IN_NAME_ASC, FILES_IN_SIZE_ASC);
    }

    public void testSortBySize_Descending_listMode() throws Exception {
        initFiles();

        bots.main.switchToListMode();

        bots.directory.sortBy(
                SortModel.SORT_DIMENSION_ID_SIZE, SortDimension.SORT_DIRECTION_DESCENDING);
        bots.directory.assertOrder(DIRS_IN_NAME_ASC, FILES_IN_SIZE_DESC);
    }

    public void testSortByModified_Ascending_listMode() throws Exception {
        initFiles(1000);

        bots.main.switchToListMode();

        bots.directory.sortBy(
                SortModel.SORT_DIMENSION_ID_DATE, SortDimension.SORT_DIRECTION_ASCENDING);
        bots.directory.assertOrder(DIRS, FILES);
    }

    public void testSortByModified_Descending_listMode() throws Exception {
        initFiles(1000);

        bots.main.switchToListMode();

        bots.directory.sortBy(
                SortModel.SORT_DIMENSION_ID_DATE, SortDimension.SORT_DIRECTION_DESCENDING);
        bots.directory.assertOrder(DIRS_IN_MODIFIED_DESC, FILES_IN_MODIFIED_DESC);
    }

    public void testSortByName_Descending_gridMode() throws Exception {
        initFiles();

        bots.main.switchToGridMode();

        bots.directory.sortBy(
                SortModel.SORT_DIMENSION_ID_TITLE, SortDimension.SORT_DIRECTION_DESCENDING);
        bots.directory.assertOrder(DIRS_IN_NAME_DESC, FILES_IN_NAME_DESC);
    }

    public void testSortBySize_Ascending_gridMode() throws Exception {
        initFiles();

        bots.main.switchToGridMode();

        bots.directory.sortBy(
                SortModel.SORT_DIMENSION_ID_SIZE, SortDimension.SORT_DIRECTION_ASCENDING);
        bots.directory.assertOrder(DIRS_IN_NAME_ASC, FILES_IN_SIZE_ASC);
    }

    public void testSortBySize_Descending_gridMode() throws Exception {
        initFiles();

        bots.main.switchToGridMode();

        bots.directory.sortBy(
                SortModel.SORT_DIMENSION_ID_SIZE, SortDimension.SORT_DIRECTION_DESCENDING);
        bots.directory.assertOrder(DIRS_IN_NAME_ASC, FILES_IN_SIZE_DESC);
    }

    public void testSortByModified_Ascending_gridMode() throws Exception {
        initFiles(1000);

        bots.main.switchToGridMode();

        bots.directory.sortBy(
                SortModel.SORT_DIMENSION_ID_DATE, SortDimension.SORT_DIRECTION_ASCENDING);
        bots.directory.assertOrder(DIRS, FILES);
    }

    public void testSortByModified_Descending_gridMode() throws Exception {
        initFiles(1000);

        bots.main.switchToGridMode();

        bots.directory.sortBy(
                SortModel.SORT_DIMENSION_ID_DATE, SortDimension.SORT_DIRECTION_DESCENDING);
        bots.directory.assertOrder(DIRS_IN_MODIFIED_DESC, FILES_IN_MODIFIED_DESC);
    }

    private static String[] reverse(String[] array) {
        String[] ret = new String[array.length];

        for (int i = 0; i < array.length; ++i) {
            ret[ret.length - i - 1] = array[i];
        }

        return ret;
    }
}
+171 −8
Original line number Diff line number Diff line
@@ -16,13 +16,26 @@

package com.android.documentsui.bots;

import static android.support.test.espresso.Espresso.onView;
import static android.support.test.espresso.action.ViewActions.click;
import static android.support.test.espresso.matcher.ViewMatchers.isDescendantOfA;
import static android.support.test.espresso.matcher.ViewMatchers.withChild;
import static android.support.test.espresso.matcher.ViewMatchers.withContentDescription;
import static android.support.test.espresso.matcher.ViewMatchers.withId;
import static android.support.test.espresso.matcher.ViewMatchers.withParent;
import static android.support.test.espresso.matcher.ViewMatchers.withText;

import static com.android.documentsui.sorting.SortDimension.SORT_DIRECTION_ASCENDING;

import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertTrue;

import static org.hamcrest.Matchers.allOf;

import android.content.Context;
import android.support.test.espresso.Espresso;
import android.support.test.espresso.matcher.ViewMatchers;
import android.graphics.Rect;
import android.support.annotation.StringRes;
import android.support.test.uiautomator.By;
import android.support.test.uiautomator.BySelector;
import android.support.test.uiautomator.Configurator;
@@ -32,15 +45,20 @@ import android.support.test.uiautomator.UiObject2;
import android.support.test.uiautomator.UiObjectNotFoundException;
import android.support.test.uiautomator.UiSelector;
import android.support.test.uiautomator.Until;
import android.support.v7.widget.RecyclerView;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.widget.RelativeLayout;
import android.view.View;

import com.android.documentsui.R;
import com.android.documentsui.sorting.SortDimension;
import com.android.documentsui.sorting.SortDimension.SortDirection;
import com.android.documentsui.sorting.SortModel;
import com.android.documentsui.sorting.SortModel.SortDimensionId;

import junit.framework.Assert;

import org.hamcrest.Matcher;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -51,13 +69,21 @@ import java.util.regex.Pattern;
 * and making assertions against the state of it.
 */
public class DirectoryListBot extends Bots.BaseBot {
    private static final String DIR_CONTAINER_ID = "com.android.documentsui:id/container_directory";
    private static final String DIR_LIST_ID = "com.android.documentsui:id/dir_list";

    private static final BySelector SNACK_DELETE =
            By.desc(Pattern.compile("^Deleting [0-9]+ file.+"));

    private final SortModel mSortModel = SortModel.createModel();

    private final DropdownSortBot mDropdownBot;
    private final HeaderSortBot mHeaderBot;

    public DirectoryListBot(UiDevice device, Context context, int timeout) {
        super(device, context, timeout);
        mDropdownBot = new DropdownSortBot();
        mHeaderBot = new HeaderSortBot();
    }

    public void assertDocumentsCount(int count) throws UiObjectNotFoundException {
@@ -110,7 +136,7 @@ public class DirectoryListBot extends Bots.BaseBot {

    private UiObject findMessageTextView() {
        return findObject(
                "com.android.documentsui:id/container_directory",
                DIR_CONTAINER_ID,
                "com.android.documentsui:id/message");
    }

@@ -167,7 +193,7 @@ public class DirectoryListBot extends Bots.BaseBot {

    public UiObject findDocument(String label) throws UiObjectNotFoundException {
        final UiSelector docList = new UiSelector().resourceId(
                "com.android.documentsui:id/container_directory").childSelector(
                DIR_CONTAINER_ID).childSelector(
                        new UiSelector().resourceId(DIR_LIST_ID));

        // Wait for the first list item to appear
@@ -188,7 +214,7 @@ public class DirectoryListBot extends Bots.BaseBot {

    public void assertFirstDocumentHasFocus() throws UiObjectNotFoundException {
        final UiSelector docList = new UiSelector().resourceId(
                "com.android.documentsui:id/container_directory").childSelector(
                DIR_CONTAINER_ID).childSelector(
                        new UiSelector().resourceId(DIR_LIST_ID));

        // Wait for the first list item to appear
@@ -200,11 +226,148 @@ public class DirectoryListBot extends Bots.BaseBot {

    public UiObject findDocumentsList() {
        return findObject(
                "com.android.documentsui:id/container_directory",
                DIR_CONTAINER_ID,
                DIR_LIST_ID);
    }

    public void assertHasFocus() {
        assertHasFocus(DIR_LIST_ID);
    }

    public void sortBy(@SortDimensionId int id, @SortDirection int direction) {
        assert(direction != SortDimension.SORT_DIRECTION_NONE);

        final @StringRes int labelId = mSortModel.getDimensionById(id).getLabelId();
        final String label = mContext.getString(labelId);
        final boolean result;
        if (Matchers.present(mDropdownBot.MATCHER)) {
            result = mDropdownBot.sortBy(label, direction);
        } else {
            result = mHeaderBot.sortBy(label, direction);
        }

        assertTrue("Sorting by id: " + id + " in direction: " + direction + " failed.",
                result);
    }

    public void assertOrder(String[] dirs, String[] files) throws UiObjectNotFoundException {
        for (int i = 0; i < dirs.length - 1; ++i) {
            assertOrder(dirs[i], dirs[i + 1]);
        }

        if (dirs.length > 0 && files.length > 0) {
            assertOrder(dirs[dirs.length - 1], files[0]);
        }

        for (int i = 0; i < files.length - 1; ++i) {
            assertOrder(files[i], files[i + 1]);
        }
    }

    private void assertOrder(String first, String second) throws UiObjectNotFoundException {

        final UiObject firstObj = findDocument(first);
        final UiObject secondObj = findDocument(second);

        final int layoutDirection = mContext.getResources().getConfiguration().getLayoutDirection();
        final Rect firstBound = firstObj.getVisibleBounds();
        final Rect secondBound = secondObj.getVisibleBounds();
        if (layoutDirection == View.LAYOUT_DIRECTION_LTR) {
            assertTrue(
                    "\"" + first + "\" is not located above or to the left of \"" + second
                            + "\" in LTR",
                    firstBound.bottom < secondBound.top || firstBound.right < secondBound.left);
        } else {
            assertTrue(
                    "\"" + first + "\" is not located above or to the right of \"" + second +
                            "\" in RTL",
                    firstBound.bottom < secondBound.top || firstBound.left > secondBound.right);
        }
    }

    private static class DropdownSortBot {

        private static final Matcher<View> MATCHER = withId(R.id.dropdown_sort_widget);
        private static final Matcher<View> DROPDOWN_MATCHER = allOf(
                withId(R.id.sort_dimen_dropdown),
                withParent(MATCHER));
        private static final Matcher<View> SORT_ARROW_MATCHER = allOf(
                withId(R.id.sort_arrow),
                withParent(MATCHER));

        private boolean sortBy(String label, @SortDirection int direction) {
            onView(DROPDOWN_MATCHER).perform(click());
            onView(withText(label)).perform(click());

            if (direction != getDirection()) {
                onView(SORT_ARROW_MATCHER).perform(click());
            }

            return Matchers.present(allOf(
                    DROPDOWN_MATCHER,
                    withText(label)))
                    && getDirection() == direction;
        }

        private @SortDirection int getDirection() {
            final boolean ascending = Matchers.present(
                    allOf(
                            SORT_ARROW_MATCHER,
                            withContentDescription(R.string.sort_direction_ascending)));

            if (ascending) {
                return SORT_DIRECTION_ASCENDING;
            }

            final boolean descending = Matchers.present(
                    allOf(
                            SORT_ARROW_MATCHER,
                            withContentDescription(R.string.sort_direction_descending)));

            return descending
                    ? SortDimension.SORT_DIRECTION_DESCENDING
                    : SortDimension.SORT_DIRECTION_NONE;
        }
    }

    private static class HeaderSortBot {

        private static final Matcher<View> MATCHER = withId(R.id.table_header);

        private boolean sortBy(String label, @SortDirection int direction) {
            final Matcher<View> cellMatcher = allOf(
                    withChild(withText(label)),
                    isDescendantOfA(MATCHER));
            onView(cellMatcher).perform(click());

            final @SortDirection int viewDirection = getDirection(cellMatcher);

            if (viewDirection != direction) {
                onView(cellMatcher).perform(click());
            }

            return getDirection(cellMatcher) == direction;
        }

        private @SortDirection int getDirection(Matcher<View> cellMatcher) {
            final boolean ascending =
                    Matchers.present(
                            allOf(
                                    withContentDescription(R.string.sort_direction_ascending),
                                    withParent(cellMatcher)));
            if (ascending) {
                return SORT_DIRECTION_ASCENDING;
            }

            final boolean descending =
                    Matchers.present(
                            allOf(
                                    withContentDescription(R.string.sort_direction_descending),
                                    withParent(cellMatcher)));

            return descending
                    ? SortDimension.SORT_DIRECTION_DESCENDING
                    : SortDimension.SORT_DIRECTION_NONE;
        }
    }
}
+6 −1
Original line number Diff line number Diff line
@@ -23,6 +23,8 @@ import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
import android.support.test.espresso.ViewInteraction;
import android.view.View;

import junit.framework.AssertionFailedError;

import org.hamcrest.Matcher;

/**
@@ -41,7 +43,10 @@ public final class Matchers {
            vi.check(matches(matcher));
            vi.check(matches(isDisplayed()));
            return true;
        } catch (Exception e) {
        }
        // Catch AssertionFailedError because vi.check(matches(isDisplayed())) throws that if
        // assertion fails.
        catch (Exception|AssertionFailedError e) {
            return false;
        }
    }
+11 −6
Original line number Diff line number Diff line
@@ -130,12 +130,17 @@ public class UiBot extends Bots.BaseBot {
        return !inFixedLayout();
    }

    void switchViewMode() {
        UiObject2 mode = menuGridMode();
        if (mode != null) {
            mode.click();
        } else {
            menuListMode().click();
    public void switchToListMode() {
        final UiObject2 listMode = menuListMode();
        if (listMode != null) {
            listMode.click();
        }
    }

    public void switchToGridMode() {
        final UiObject2 gridMode = menuGridMode();
        if (gridMode != null) {
            gridMode.click();
        }
    }