text: // Copyright 2017 The Chromium Authors. All rights reserved.
  // Use of this source code is governed by a BSD-style license that can be
  // found in the LICENSE file.
  
  #include "base/test/gtest_util.h"
  #include "testing/gtest/include/gtest/gtest.h"
  #include "ui/android/event_forwarder.h"
  #include "ui/android/view_android.h"
  #include "ui/android/view_android_observer.h"
  #include "ui/android/window_android.h"
  #include "ui/events/android/event_handler_android.h"
  #include "ui/events/android/motion_event_android.h"
  #include "ui/events/test/scoped_event_test_tick_clock.h"
  
  namespace ui {
  
  using base::android::JavaParamRef;
  
  class TestViewAndroid : public ViewAndroid {
   public:
    TestViewAndroid(ViewAndroid::LayoutType layout_type)
        : ViewAndroid(layout_type) {}
  
    float GetDipScale() override { return 1.f; }
  };
  
  class TestEventHandler : public EventHandlerAndroid {
   public:
    TestEventHandler() {}
  
    bool OnTouchEvent(const MotionEventAndroid& event) override {
      touch_called_ = true;
      return handle_event_;
    }
    void OnSizeChanged() override { onsize_called_ = true; }
  
    void SetHandleEvent(bool handle_event) { handle_event_ = handle_event; }
    bool TouchEventHandled() { return touch_called_ && handle_event_; }
    bool TouchEventCalled() { return touch_called_; }
    bool OnSizeCalled() { return onsize_called_; }
    void Reset() {
      touch_called_ = false;
      onsize_called_ = false;
    }
  
   private:
    bool handle_event_{true};  // Marks as event was consumed. True by default.
    bool touch_called_{false};
    bool onsize_called_{false};
  };
  
  class ViewAndroidBoundsTest : public testing::Test {
   public:
    ViewAndroidBoundsTest()
        : root_(ViewAndroid::LayoutType::MATCH_PARENT),
          view1_(ViewAndroid::LayoutType::NORMAL),
          view2_(ViewAndroid::LayoutType::NORMAL),
          view3_(ViewAndroid::LayoutType::NORMAL),
          viewm_(ViewAndroid::LayoutType::MATCH_PARENT) {
      root_.GetEventForwarder();
      view1_.set_event_handler(&handler1_);
      view2_.set_event_handler(&handler2_);
      view3_.set_event_handler(&handler3_);
      viewm_.set_event_handler(&handlerm_);
    }
  
    void Reset() {
      handler1_.Reset();
      handler2_.Reset();
      handler3_.Reset();
      handlerm_.Reset();
      test_clock_.SetNowTicks(base::TimeTicks());
    }
  
    void GenerateTouchEventAt(float x, float y) {
      ui::MotionEventAndroid::Pointer pointer0(0, x, y, 0, 0, 0, 0, 0);
      ui::MotionEventAndroid::Pointer pointer1(0, 0, 0, 0, 0, 0, 0, 0);
      ui::MotionEventAndroid event(nullptr, JavaParamRef<jobject>(nullptr), 1.f,
                                   0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, false,
                                   &pointer0, &pointer1);
      root_.OnTouchEvent(event);
    }
  
    void ExpectHit(const TestEventHandler& hitHandler) {
      TestEventHandler* handlers[4] = {&handler1_, &handler2_, &handler3_,
                                       &handlerm_};
      for (auto* handler : handlers) {
        if (&hitHandler == handler)
          EXPECT_TRUE(handler->TouchEventHandled());
        else
          EXPECT_FALSE(handler->TouchEventHandled());
      }
      Reset();
    }
  
    TestViewAndroid root_;
    TestViewAndroid view1_;
    TestViewAndroid view2_;
    TestViewAndroid view3_;
    TestViewAndroid viewm_;  // match-parent view
    TestEventHandler handler1_;
    TestEventHandler handler2_;
    TestEventHandler handler3_;
    TestEventHandler handlerm_;
    ui::test::ScopedEventTestTickClock test_clock_;
  };
  
  TEST_F(ViewAndroidBoundsTest, MatchesViewInFront) {
    view1_.SetLayoutForTesting(50, 50, 400, 600);
    view2_.SetLayoutForTesting(50, 50, 400, 600);
    root_.AddChild(&view2_);
    root_.AddChild(&view1_);
  
    GenerateTouchEventAt(100.f, 100.f);
    ExpectHit(handler1_);
  
    // View 2 moves up to the top, and events should hit it from now.
    root_.MoveToFront(&view2_);
    GenerateTouchEventAt(100.f, 100.f);
    ExpectHit(handler2_);
  
    // View 2 moves back to the bottom, and events should hit View 1 again.
    root_.MoveToBack(&view2_);
    GenerateTouchEventAt(100.f, 100.f);
    ExpectHit(handler1_);
  }
  
  TEST_F(ViewAndroidBoundsTest, MatchesViewArea) {
    view1_.SetLayoutForTesting(50, 50, 200, 200);
    view2_.SetLayoutForTesting(20, 20, 400, 600);
  
    root_.AddChild(&view2_);
    root_.AddChild(&view1_);
  
    // Falls within |view1_|'s bounds
    GenerateTouchEventAt(100.f, 100.f);
    ExpectHit(handler1_);
  
    // Falls within |view2_|'s bounds
    GenerateTouchEventAt(300.f, 400.f);
    ExpectHit(handler2_);
  }
  
  TEST_F(ViewAndroidBoundsTest, MatchesViewAfterMove) {
    view1_.SetLayoutForTesting(50, 50, 200, 200);
    view2_.SetLayoutForTesting(20, 20, 400, 600);
    root_.AddChild(&view2_);
    root_.AddChild(&view1_);
  
    GenerateTouchEventAt(100.f, 100.f);
    ExpectHit(handler1_);
  
    view1_.SetLayoutForTesting(150, 150, 200, 200);
    GenerateTouchEventAt(100.f, 100.f);
    ExpectHit(handler2_);
  }
  
  TEST_F(ViewAndroidBoundsTest, MatchesViewSizeOfkMatchParent) {
    view1_.SetLayoutForTesting(20, 20, 400, 600);
    view2_.SetLayoutForTesting(50, 50, 200, 200);
  
    root_.AddChild(&view1_);
    root_.AddChild(&view2_);
    view1_.AddChild(&viewm_);
  
    GenerateTouchEventAt(100.f, 100.f);
    ExpectHit(handler2_);
  
    GenerateTouchEventAt(300.f, 400.f);
    ExpectHit(handler1_);
  
    handler1_.SetHandleEvent(false);
    GenerateTouchEventAt(300.f, 400.f);
    EXPECT_TRUE(handler1_.TouchEventCalled());
    ExpectHit(handlerm_);
  }
  
  TEST_F(ViewAndroidBoundsTest, MatchesViewsWithOffset) {
    view1_.SetLayoutForTesting(10, 20, 150, 100);
    view2_.SetLayoutForTesting(20, 30, 40, 30);
    view3_.SetLayoutForTesting(70, 30, 40, 30);
  
    root_.AddChild(&view1_);
    view1_.AddChild(&view2_);
    view1_.AddChild(&view3_);
  
    GenerateTouchEventAt(70, 30);
    ExpectHit(handler1_);
  
    handler1_.SetHandleEvent(false);
    GenerateTouchEventAt(40, 60);
    EXPECT_TRUE(handler1_.TouchEventCalled());
    ExpectHit(handler2_);
  
    GenerateTouchEventAt(100, 70);
    EXPECT_TRUE(handler1_.TouchEventCalled());
    ExpectHit(handler3_);
  }
  
  TEST_F(ViewAndroidBoundsTest, OnSizeChanged) {
    root_.AddChild(&view1_);
    view1_.AddChild(&viewm_);
    view1_.AddChild(&view3_);
  
    // Size event propagates to non-match-parent children only.
    view1_.OnSizeChanged(100, 100);
    EXPECT_TRUE(handler1_.OnSizeCalled());
    EXPECT_TRUE(handlerm_.OnSizeCalled());
    EXPECT_FALSE(handler3_.OnSizeCalled());
  
    Reset();
  
    // Match-parent view should not receivee size events in the first place.
    EXPECT_DCHECK_DEATH(viewm_.OnSizeChanged(100, 200));
    EXPECT_FALSE(handlerm_.OnSizeCalled());
    EXPECT_FALSE(handler3_.OnSizeCalled());
  
    viewm_.RemoveFromParent();
    viewm_.OnSizeChangedInternal(gfx::Size(0, 0));  // Reset the size.
  
    Reset();
  
    view1_.OnSizeChanged(100, 100);
  
    // Size event is generated for a newly added, match-parent child view.
    EXPECT_FALSE(handlerm_.OnSizeCalled());
    view1_.AddChild(&viewm_);
    EXPECT_TRUE(handlerm_.OnSizeCalled());
    EXPECT_FALSE(handler3_.OnSizeCalled());
  
    viewm_.RemoveFromParent();
  
    Reset();
  
    view1_.OnSizeChanged(100, 100);
  
    // Size event won't propagate if the children already have the same size.
    view1_.AddChild(&viewm_);
    EXPECT_FALSE(handlerm_.OnSizeCalled());
    EXPECT_FALSE(handler3_.OnSizeCalled());
  }
  
  TEST(ViewAndroidTest, ChecksMultipleEventForwarders) {
    ViewAndroid parent;
    ViewAndroid child;
    parent.GetEventForwarder();
    child.GetEventForwarder();
    EXPECT_DCHECK_DEATH(parent.AddChild(&child));
  
    ViewAndroid parent2;
    ViewAndroid child2;
    parent2.GetEventForwarder();
    parent2.AddChild(&child2);
    EXPECT_DCHECK_DEATH(child2.GetEventForwarder());
  
    ViewAndroid window;
    ViewAndroid wcv1, wcv2;
    ViewAndroid rwhv1a, rwhv1b, rwhv2;
    wcv1.GetEventForwarder();
    wcv2.GetEventForwarder();
  
    window.AddChild(&wcv1);
    wcv1.AddChild(&rwhv1a);
    wcv1.AddChild(&rwhv1b);
  
    wcv2.AddChild(&rwhv2);
  
    // window should be able to add wcv2 since there's only one event forwarder
    // in the path window - wcv2* - rwvh2
    window.AddChild(&wcv2);
  
    // Additional event forwarder will cause failure.
    EXPECT_DCHECK_DEATH(rwhv2.GetEventForwarder());
  }
  
  class Observer : public ViewAndroidObserver {
   public:
    Observer() : attached_(false), destroyed_(false) {}
  
    void OnAttachedToWindow() override { attached_ = true; }
  
    void OnDetachedFromWindow() override { attached_ = false; }
  
    void OnViewAndroidDestroyed() override { destroyed_ = true; }
  
    bool attached_;
    bool destroyed_;
  };
  
  TEST(ViewAndroidTest, Observer) {
    std::unique_ptr<WindowAndroid> window(WindowAndroid::CreateForTesting());
  
    Observer top_observer;
    Observer bottom_observer;
    Observer top_observer2;
  
    {
      ViewAndroid top;
      ViewAndroid bottom;
  
      top.AddObserver(&top_observer);
      bottom.AddObserver(&bottom_observer);
  
      top.AddChild(&bottom);
  
      EXPECT_FALSE(top_observer.attached_);
      EXPECT_FALSE(bottom_observer.attached_);
  
      // Views in a tree all get notified of 'attached' event.
      window->AddChild(&top);
      EXPECT_TRUE(top_observer.attached_);
      EXPECT_TRUE(bottom_observer.attached_);
  
      // Observer, upon addition, does not get notified of the current
      // attached state.
      top.AddObserver(&top_observer2);
      EXPECT_FALSE(top_observer2.attached_);
  
      bottom.RemoveFromParent();
      EXPECT_FALSE(bottom_observer.attached_);
      top.RemoveFromParent();
      EXPECT_FALSE(top_observer.attached_);
  
      window->AddChild(&top);
      EXPECT_TRUE(top_observer.attached_);
  
      // View, upon addition to a tree in the attached state, should be notified.
      top.AddChild(&bottom);
      EXPECT_TRUE(bottom_observer.attached_);
  
      // Views in a tree all get notified of 'detached' event.
      top.RemoveFromParent();
      EXPECT_FALSE(top_observer.attached_);
      EXPECT_FALSE(bottom_observer.attached_);
  
      // Remove the second top observer to test the destruction notification.
      top.RemoveObserver(&top_observer2);
    }
  
    EXPECT_TRUE(top_observer.destroyed_);
    EXPECT_FALSE(top_observer2.destroyed_);
    EXPECT_TRUE(bottom_observer.destroyed_);
  }
  
  TEST(ViewAndroidTest, WindowAndroidDestructionDetachesAllViewAndroid) {
    std::unique_ptr<WindowAndroid> window(WindowAndroid::CreateForTesting());
    ViewAndroid top;
    ViewAndroid bottom;
  
    Observer top_observer;
    Observer bottom_observer;
  
    top.AddObserver(&top_observer);
    bottom.AddObserver(&bottom_observer);
  
    window->AddChild(&top);
    top.AddChild(&bottom);
  
    EXPECT_TRUE(top_observer.attached_);
    EXPECT_TRUE(bottom_observer.attached_);
  
    window.reset();
  
    EXPECT_FALSE(top_observer.attached_);
    EXPECT_FALSE(bottom_observer.attached_);
  
    top.RemoveObserver(&top_observer);
    bottom.RemoveObserver(&bottom_observer);
  }
  
  }  // namespace ui
  