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

Commit 6f315fc8 authored by András Kurucz's avatar András Kurucz Committed by Android (Google) Code Review
Browse files

Merge "Convert ShadeViewDifferTest to Kotlin" into tm-qpr-dev

parents eb4c03b8 228f0cb5
Loading
Loading
Loading
Loading
+0 −317
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 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.systemui.statusbar.notification.collection.render;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;

import android.content.Context;
import android.testing.AndroidTestingRunner;
import android.view.View;
import android.widget.FrameLayout;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.test.filters.SmallTest;

import com.android.systemui.SysuiTestCase;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

import java.util.List;

@SmallTest
@RunWith(AndroidTestingRunner.class)
public class ShadeViewDifferTest extends SysuiTestCase {
    private ShadeViewDiffer mDiffer;

    private FakeController mRootController = new FakeController(mContext, "RootController");
    private FakeController mController1 = new FakeController(mContext, "Controller1");
    private FakeController mController2 = new FakeController(mContext, "Controller2");
    private FakeController mController3 = new FakeController(mContext, "Controller3");
    private FakeController mController4 = new FakeController(mContext, "Controller4");
    private FakeController mController5 = new FakeController(mContext, "Controller5");
    private FakeController mController6 = new FakeController(mContext, "Controller6");
    private FakeController mController7 = new FakeController(mContext, "Controller7");

    @Mock
    ShadeViewDifferLogger mLogger;

    @Before
    public void setUp() {
        MockitoAnnotations.initMocks(this);

        mDiffer = new ShadeViewDiffer(mRootController, mLogger);
    }

    @Test
    public void testAddInitialViews() {
        // WHEN a spec is applied to an empty root
        // THEN the final tree matches the spec
        applySpecAndCheck(
                node(mController1),
                node(mController2,
                        node(mController3),
                        node(mController4)
                ),
                node(mController5)
        );
    }

    @Test
    public void testDetachViews() {
        // GIVEN a preexisting tree of controllers
        applySpecAndCheck(
                node(mController1),
                node(mController2,
                        node(mController3),
                        node(mController4)
                ),
                node(mController5)
        );

        // WHEN the new spec removes nodes
        // THEN the final tree matches the spec
        applySpecAndCheck(
                node(mController5)
        );
    }

    @Test
    public void testReparentChildren() {
        // GIVEN a preexisting tree of controllers
        applySpecAndCheck(
                node(mController1),
                node(mController2,
                        node(mController3),
                        node(mController4)
                ),
                node(mController5)
        );

        // WHEN the parents of the controllers are all shuffled around
        // THEN the final tree matches the spec
        applySpecAndCheck(
                node(mController1),
                node(mController4),
                node(mController3,
                        node(mController2)
                )
        );
    }

    @Test
    public void testReorderChildren() {
        // GIVEN a preexisting tree of controllers
        applySpecAndCheck(
                node(mController1),
                node(mController2),
                node(mController3),
                node(mController4)
        );

        // WHEN the children change order
        // THEN the final tree matches the spec
        applySpecAndCheck(
                node(mController3),
                node(mController2),
                node(mController4),
                node(mController1)
        );
    }

    @Test
    public void testRemovedGroupsAreBrokenApart() {
        // GIVEN a preexisting tree with a group
        applySpecAndCheck(
                node(mController1),
                node(mController2,
                        node(mController3),
                        node(mController4),
                        node(mController5)
                )
        );

        // WHEN the new spec removes the entire group
        applySpecAndCheck(
                node(mController1)
        );

        // THEN the group children are no longer attached to their parent
        assertNull(mController3.getView().getParent());
        assertNull(mController4.getView().getParent());
        assertNull(mController5.getView().getParent());
    }

    @Test
    public void testUnmanagedViews() {
        // GIVEN a preexisting tree of controllers
        applySpecAndCheck(
                node(mController1),
                node(mController2,
                        node(mController3),
                        node(mController4)
                ),
                node(mController5)
        );

        // GIVEN some additional unmanaged views attached to the tree
        View unmanagedView1 = new View(mContext);
        View unmanagedView2 = new View(mContext);

        mRootController.getView().addView(unmanagedView1, 1);
        mController2.getView().addView(unmanagedView2, 0);

        // WHEN a new spec is applied with additional nodes
        // THEN the final tree matches the spec
        applySpecAndCheck(
                node(mController1),
                node(mController2,
                        node(mController3),
                        node(mController4),
                        node(mController6)
                ),
                node(mController5),
                node(mController7)
        );

        // THEN the unmanaged views have been pushed to the end of their parents
        assertEquals(unmanagedView1, mRootController.view.getChildAt(4));
        assertEquals(unmanagedView2, mController2.view.getChildAt(3));
    }

    private void applySpecAndCheck(NodeSpec spec) {
        mDiffer.applySpec(spec);
        checkMatchesSpec(spec);
    }

    private void applySpecAndCheck(SpecBuilder... children) {
        applySpecAndCheck(node(mRootController, children).build());
    }

    private void checkMatchesSpec(NodeSpec spec) {
        final NodeController parent = spec.getController();
        final List<NodeSpec> children = spec.getChildren();

        for (int i = 0; i < children.size(); i++) {
            NodeSpec childSpec = children.get(i);
            View view = parent.getChildAt(i);

            assertEquals(
                    "Child " + i + " of parent " + parent.getNodeLabel() + " should be "
                            + childSpec.getController().getNodeLabel() + " but is instead "
                            + (view != null ? mDiffer.getViewLabel(view) : "null"),
                    view,
                    childSpec.getController().getView());

            if (!childSpec.getChildren().isEmpty()) {
                checkMatchesSpec(childSpec);
            }
        }
    }

    private static class FakeController implements NodeController {

        public final FrameLayout view;
        private final String mLabel;

        FakeController(Context context, String label) {
            view = new FrameLayout(context);
            mLabel = label;
        }

        @NonNull
        @Override
        public String getNodeLabel() {
            return mLabel;
        }

        @NonNull
        @Override
        public FrameLayout getView() {
            return view;
        }

        @Override
        public int getChildCount() {
            return view.getChildCount();
        }

        @Override
        public View getChildAt(int index) {
            return view.getChildAt(index);
        }

        @Override
        public void addChildAt(@NonNull NodeController child, int index) {
            view.addView(child.getView(), index);
        }

        @Override
        public void moveChildTo(@NonNull NodeController child, int index) {
            view.removeView(child.getView());
            view.addView(child.getView(), index);
        }

        @Override
        public void removeChild(@NonNull NodeController child, boolean isTransfer) {
            view.removeView(child.getView());
        }

        @Override
        public void onViewAdded() {
        }

        @Override
        public void onViewMoved() {
        }

        @Override
        public void onViewRemoved() {
        }
    }

    private static class SpecBuilder {
        private final NodeController mController;
        private final SpecBuilder[] mChildren;

        SpecBuilder(NodeController controller, SpecBuilder... children) {
            mController = controller;
            mChildren = children;
        }

        public NodeSpec build() {
            return build(null);
        }

        public NodeSpec build(@Nullable NodeSpec parent) {
            final NodeSpecImpl spec = new NodeSpecImpl(parent, mController);
            for (SpecBuilder childBuilder : mChildren) {
                spec.getChildren().add(childBuilder.build(spec));
            }
            return spec;
        }
    }

    private static SpecBuilder node(NodeController controller, SpecBuilder... children) {
        return new SpecBuilder(controller, children);
    }
}
+234 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 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.systemui.statusbar.notification.collection.render

import android.content.Context
import android.testing.AndroidTestingRunner
import android.view.View
import android.widget.FrameLayout
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.util.mockito.mock
import org.junit.Assert
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith

@SmallTest
@RunWith(AndroidTestingRunner::class)
class ShadeViewDifferTest : SysuiTestCase() {
    private lateinit var differ: ShadeViewDiffer
    private val rootController = FakeController(mContext, "RootController")
    private val controller1 = FakeController(mContext, "Controller1")
    private val controller2 = FakeController(mContext, "Controller2")
    private val controller3 = FakeController(mContext, "Controller3")
    private val controller4 = FakeController(mContext, "Controller4")
    private val controller5 = FakeController(mContext, "Controller5")
    private val controller6 = FakeController(mContext, "Controller6")
    private val controller7 = FakeController(mContext, "Controller7")
    private val logger: ShadeViewDifferLogger = mock()

    @Before
    fun setUp() {
        differ = ShadeViewDiffer(rootController, logger)
    }

    @Test
    fun testAddInitialViews() {
        // WHEN a spec is applied to an empty root
        // THEN the final tree matches the spec
        applySpecAndCheck(
            node(controller1),
            node(controller2, node(controller3), node(controller4)),
            node(controller5)
        )
    }

    @Test
    fun testDetachViews() {
        // GIVEN a preexisting tree of controllers
        applySpecAndCheck(
            node(controller1),
            node(controller2, node(controller3), node(controller4)),
            node(controller5)
        )

        // WHEN the new spec removes nodes
        // THEN the final tree matches the spec
        applySpecAndCheck(node(controller5))
    }

    @Test
    fun testReparentChildren() {
        // GIVEN a preexisting tree of controllers
        applySpecAndCheck(
            node(controller1),
            node(controller2, node(controller3), node(controller4)),
            node(controller5)
        )

        // WHEN the parents of the controllers are all shuffled around
        // THEN the final tree matches the spec
        applySpecAndCheck(
            node(controller1),
            node(controller4),
            node(controller3, node(controller2))
        )
    }

    @Test
    fun testReorderChildren() {
        // GIVEN a preexisting tree of controllers
        applySpecAndCheck(
            node(controller1),
            node(controller2),
            node(controller3),
            node(controller4)
        )

        // WHEN the children change order
        // THEN the final tree matches the spec
        applySpecAndCheck(
            node(controller3),
            node(controller2),
            node(controller4),
            node(controller1)
        )
    }

    @Test
    fun testRemovedGroupsAreBrokenApart() {
        // GIVEN a preexisting tree with a group
        applySpecAndCheck(
            node(controller1),
            node(controller2, node(controller3), node(controller4), node(controller5))
        )

        // WHEN the new spec removes the entire group
        applySpecAndCheck(node(controller1))

        // THEN the group children are no longer attached to their parent
        Assert.assertNull(controller3.view.parent)
        Assert.assertNull(controller4.view.parent)
        Assert.assertNull(controller5.view.parent)
    }

    @Test
    fun testUnmanagedViews() {
        // GIVEN a preexisting tree of controllers
        applySpecAndCheck(
            node(controller1),
            node(controller2, node(controller3), node(controller4)),
            node(controller5)
        )

        // GIVEN some additional unmanaged views attached to the tree
        val unmanagedView1 = View(mContext)
        val unmanagedView2 = View(mContext)
        rootController.view.addView(unmanagedView1, 1)
        controller2.view.addView(unmanagedView2, 0)

        // WHEN a new spec is applied with additional nodes
        // THEN the final tree matches the spec
        applySpecAndCheck(
            node(controller1),
            node(controller2, node(controller3), node(controller4), node(controller6)),
            node(controller5),
            node(controller7)
        )

        // THEN the unmanaged views have been pushed to the end of their parents
        Assert.assertEquals(unmanagedView1, rootController.view.getChildAt(4))
        Assert.assertEquals(unmanagedView2, controller2.view.getChildAt(3))
    }

    private fun applySpecAndCheck(spec: NodeSpec) {
        differ.applySpec(spec)
        checkMatchesSpec(spec)
    }

    private fun applySpecAndCheck(vararg children: SpecBuilder) {
        applySpecAndCheck(node(rootController, *children).build())
    }

    private fun checkMatchesSpec(spec: NodeSpec) {
        val parent = spec.controller
        val children = spec.children
        for (i in children.indices) {
            val childSpec = children[i]
            val view = parent.getChildAt(i)
            Assert.assertEquals(
                "Child $i of parent ${parent.nodeLabel} " +
                    "should be ${childSpec.controller.nodeLabel} " +
                    "but instead " +
                    view?.let(differ::getViewLabel),
                view,
                childSpec.controller.view
            )
            if (childSpec.children.isNotEmpty()) {
                checkMatchesSpec(childSpec)
            }
        }
    }

    private class FakeController(context: Context, label: String) : NodeController {
        override val view: FrameLayout = FrameLayout(context)
        override val nodeLabel: String = label
        override fun getChildCount(): Int = view.childCount

        override fun getChildAt(index: Int): View? {
            return view.getChildAt(index)
        }

        override fun addChildAt(child: NodeController, index: Int) {
            view.addView(child.view, index)
        }

        override fun moveChildTo(child: NodeController, index: Int) {
            view.removeView(child.view)
            view.addView(child.view, index)
        }

        override fun removeChild(child: NodeController, isTransfer: Boolean) {
            view.removeView(child.view)
        }

        override fun onViewAdded() {}
        override fun onViewMoved() {}
        override fun onViewRemoved() {}
    }

    private class SpecBuilder(
        private val mController: NodeController,
        private val children: Array<out SpecBuilder>
    ) {

        @JvmOverloads
        fun build(parent: NodeSpec? = null): NodeSpec {
            val spec = NodeSpecImpl(parent, mController)
            for (childBuilder in children) {
                spec.children.add(childBuilder.build(spec))
            }
            return spec
        }
    }

    companion object {
        private fun node(controller: NodeController, vararg children: SpecBuilder): SpecBuilder {
            return SpecBuilder(controller, children)
        }
    }
}