text:
namespace viz {
namespace test {

class HitTestQueryTest : public testing::Test {
 public:
  HitTestQueryTest() = default;

  HitTestQueryTest(const HitTestQueryTest&) = delete;
  HitTestQueryTest& operator=(const HitTestQueryTest&) = delete;

  ~HitTestQueryTest() override = default;

  void SendHitTestData() {
    hit_test_query_.OnAggregatedHitTestRegionListUpdated(active_data_);
  }

 protected:
  HitTestQuery& hit_test_query() { return hit_test_query_; }

  std::vector<AggregatedHitTestRegion> active_data_;

 private:
  HitTestQuery hit_test_query_;
};

// One surface.
//
//  +e---------+
//  |          |
//  |          |
//  |          |
//  +----------+
//
TEST_F(HitTestQueryTest, OneSurface) {
  FrameSinkId e_id = FrameSinkId(1, 1);
  gfx::Rect e_bounds = gfx::Rect(0, 0, 600, 600);
  gfx::Transform transform_e_to_e;
  active_data_.push_back(AggregatedHitTestRegion(
      e_id,
      HitTestRegionFlags::kHitTestMine | HitTestRegionFlags::kHitTestMouse,
      e_bounds, transform_e_to_e, 0));  // e
  SendHitTestData();

  // All points are in e's coordinate system when we reach this case.
  gfx::PointF point1(1, 1);
  gfx::PointF point2(600, 600);
  gfx::PointF point3(0, 0);

  Target target1 =
      hit_test_query().FindTargetForLocation(EventSource::MOUSE, point1);
  EXPECT_EQ(target1.frame_sink_id, e_id);
  EXPECT_EQ(target1.location_in_target, point1);
  EXPECT_EQ(target1.flags, HitTestRegionFlags::kHitTestMine |
                               HitTestRegionFlags::kHitTestMouse);

  // point2 is on the bounds of e so no target found.
  Target target2 =
      hit_test_query().FindTargetForLocation(EventSource::MOUSE, point2);
  EXPECT_EQ(target2.frame_sink_id, FrameSinkId());
  EXPECT_EQ(target2.location_in_target, gfx::PointF());
  EXPECT_FALSE(target2.flags);

  // There's a valid Target for point3, see Rect::Contains.
  Target target3 =
      hit_test_query().FindTargetForLocation(EventSource::MOUSE, point3);
  EXPECT_EQ(target3.frame_sink_id, e_id);
  EXPECT_EQ(target3.location_in_target, point3);
  EXPECT_EQ(target3.flags, HitTestRegionFlags::kHitTestMine |
                               HitTestRegionFlags::kHitTestMouse);
}

// One embedder with two children.
//
//  +e------------+     Point   maps to
//  | +c1-+ +c2---|     -----   -------
//  |1|   | |     |      1        e
//  | | 2 | | 3   | 4    2        c1
//  | +---+ |     |      3        c2
//  +-------------+      4        none
//
TEST_F(HitTestQueryTest, OneEmbedderTwoChildren) {
  FrameSinkId e_id = FrameSinkId(1, 1);
  FrameSinkId c1_id = FrameSinkId(2, 2);
  FrameSinkId c2_id = FrameSinkId(3, 3);
  gfx::Rect e_bounds_in_e = gfx::Rect(0, 0, 600, 600);
  gfx::Rect c1_bounds_in_e = gfx::Rect(0, 0, 200, 200);
  gfx::Rect c2_bounds_in_e = gfx::Rect(0, 0, 400, 400);
  gfx::Transform transform_e_to_e, transform_e_to_c1, transform_e_to_c2;
  transform_e_to_c1.Translate(-100, -100);
  transform_e_to_c2.Translate(-300, -300);
  active_data_.push_back(AggregatedHitTestRegion(
      e_id,
      HitTestRegionFlags::kHitTestMine | HitTestRegionFlags::kHitTestMouse,
      e_bounds_in_e, transform_e_to_e, 2));  // e
  active_data_.push_back(AggregatedHitTestRegion(
      c1_id,
      HitTestRegionFlags::kHitTestMine | HitTestRegionFlags::kHitTestMouse,
      c1_bounds_in_e, transform_e_to_c1, 0));  // c1
  active_data_.push_back(AggregatedHitTestRegion(
      c2_id,
      HitTestRegionFlags::kHitTestMine | HitTestRegionFlags::kHitTestMouse,
      c2_bounds_in_e, transform_e_to_c2, 0));  // c2
  SendHitTestData();

  // All points are in e's coordinate system when we reach this case.
  gfx::PointF point1(99, 200);
  gfx::PointF point2(150, 150);
  gfx::PointF point3(400, 400);
  gfx::PointF point4(650, 350);

  Target target1 =
      hit_test_query().FindTargetForLocation(EventSource::MOUSE, point1);
  EXPECT_EQ(target1.frame_sink_id, e_id);
  EXPECT_EQ(target1.location_in_target, point1);
  EXPECT_EQ(target1.flags, HitTestRegionFlags::kHitTestMine |
                               HitTestRegionFlags::kHitTestMouse);

  Target target2 =
      hit_test_query().FindTargetForLocation(EventSource::MOUSE, point2);
  EXPECT_EQ(target2.frame_sink_id, c1_id);
  EXPECT_EQ(target2.location_in_target, gfx::PointF(50, 50));
  EXPECT_EQ(target2.flags, HitTestRegionFlags::kHitTestMine |
                               HitTestRegionFlags::kHitTestMouse);

  Target target3 =
      hit_test_query().FindTargetForLocation(EventSource::MOUSE, point3);
  EXPECT_EQ(target3.frame_sink_id, c2_id);
  EXPECT_EQ(target3.location_in_target, gfx::PointF(100, 100));
  EXPECT_EQ(target3.flags, HitTestRegionFlags::kHitTestMine |
                               HitTestRegionFlags::kHitTestMouse);

  Target target4 =
      hit_test_query().FindTargetForLocation(EventSource::MOUSE, point4);
  EXPECT_EQ(target4.frame_sink_id, FrameSinkId());
  EXPECT_EQ(target4.location_in_target, gfx::PointF());
  EXPECT_FALSE(target4.flags);
}

// One embedder with a rotated child.
TEST_F(HitTestQueryTest, OneEmbedderRotatedChild) {
  FrameSinkId e_id = FrameSinkId(1, 1);
  FrameSinkId c_id = FrameSinkId(2, 2);
  gfx::Rect e_bounds_in_e = gfx::Rect(0, 0, 600, 600);
  gfx::Rect c_bounds_in_e = gfx::Rect(0, 0, 1000, 1000);
  gfx::Transform transform_e_to_e, transform_e_to_c;
  transform_e_to_c.Translate(-100, -100);
  transform_e_to_c.Skew(2, 3);
  transform_e_to_c.Scale(.5f, .7f);
  active_data_.push_back(AggregatedHitTestRegion(
      e_id,
      HitTestRegionFlags::kHitTestMine | HitTestRegionFlags::kHitTestMouse,
      e_bounds_in_e, transform_e_to_e, 1));  // e
  active_data_.push_back(AggregatedHitTestRegion(
      c_id,
      HitTestRegionFlags::kHitTestMine | HitTestRegionFlags::kHitTestMouse,
      c_bounds_in_e, transform_e_to_c, 0));  // c
  SendHitTestData();

  // All points are in e's coordinate system when we reach this case.
  gfx::PointF point1(150, 120);  // Point(-22.07, -12.07) after transform.
  gfx::PointF point2(550, 400);  // Point(184.78, 194.41) after transform.

  Target target1 =
      hit_test_query().FindTargetForLocation(EventSource::MOUSE, point1);
  EXPECT_EQ(target1.frame_sink_id, e_id);
  EXPECT_EQ(target1.location_in_target, point1);
  EXPECT_EQ(target1.flags, HitTestRegionFlags::kHitTestMine |
                               HitTestRegionFlags::kHitTestMouse);

  Target target2 =
      hit_test_query().FindTargetForLocation(EventSource::MOUSE, point2);
  EXPECT_EQ(target2.frame_sink_id, c_id);
  EXPECT_NEAR(target2.location_in_target.x(), 185, .5);
  EXPECT_NEAR(target2.location_in_target.y(), 194, .5);
  EXPECT_EQ(target2.flags, HitTestRegionFlags::kHitTestMine |
                               HitTestRegionFlags::kHitTestMouse);
}

// One embedder with a clipped child with a tab and transparent background.
//
//  +e-------------+
//  |   +c---------|     Point   maps to
//  | 1 |+a--+     |     -----   -------
//  |   || 2 |  3  |       1        e
//  |   |+b--------|       2        a
//  |   ||         |       3        e ( transparent area in c )
//  |   ||   4     |       4        b
//  +--------------+
//
TEST_F(HitTestQueryTest, ClippedChildWithTabAndTransparentBackground) {
  FrameSinkId e_id = FrameSinkId(1, 1);
  FrameSinkId c_id = FrameSinkId(2, 2);
  FrameSinkId a_id = FrameSinkId(3, 3);
  FrameSinkId b_id = FrameSinkId(4, 4);
  gfx::Rect e_bounds_in_e = gfx::Rect(0, 0, 600, 600);
  gfx::Rect c_bounds_in_e = gfx::Rect(0, 0, 800, 800);
  gfx::Rect a_bounds_in_c = gfx::Rect(0, 0, 200, 100);
  gfx::Rect b_bounds_in_c = gfx::Rect(0, 0, 800, 600);
  gfx::Transform transform_e_to_e, transform_e_to_c, transform_c_to_a,
      transform_c_to_b;
  transform_e_to_c.Translate(-200, -100);
  transform_c_to_b.Translate(0, -100);
  active_data_.push_back(AggregatedHitTestRegion(
      e_id,
      HitTestRegionFlags::kHitTestMine | HitTestRegionFlags::kHitTestMouse,
      e_bounds_in_e, transform_e_to_e, 3));  // e
  active_data_.push_back(
      AggregatedHitTestRegion(c_id, HitTestRegionFlags::kHitTestChildSurface,
                              c_bounds_in_e, transform_e_to_c, 2));  // c
  active_data_.push_back(AggregatedHitTestRegion(
      a_id,
      HitTestRegionFlags::kHitTestMine | HitTestRegionFlags::kHitTestMouse,
      a_bounds_in_c, transform_c_to_a, 0));  // a
  active_data_.push_back(AggregatedHitTestRegion(
      b_id,
      HitTestRegionFlags::kHitTestMine | HitTestRegionFlags::kHitTestMouse,
      b_bounds_in_c, transform_c_to_b, 0));  // b
  SendHitTestData();

  // All points are in e's coordinate system when we reach this case.
  gfx::PointF point1(1, 1);
  gfx::PointF point2(202, 102);
  gfx::PointF point3(403, 103);
  gfx::PointF point4(202, 202);

  Target target1 =
      hit_test_query().FindTargetForLocation(EventSource::MOUSE, point1);
  EXPECT_EQ(target1.frame_sink_id, e_id);
  EXPECT_EQ(target1.location_in_target, point1);
  EXPECT_EQ(target1.flags, HitTestRegionFlags::kHitTestMine |
                               HitTestRegionFlags::kHitTestMouse);

  Target target2 =
      hit_test_query().FindTargetForLocation(EventSource::MOUSE, point2);
  EXPECT_EQ(target2.frame_sink_id, a_id);
  EXPECT_EQ(target2.location_in_target, gfx::PointF(2, 2));
  EXPECT_EQ(target2.flags, HitTestRegionFlags::kHitTestMine |
                               HitTestRegionFlags::kHitTestMouse);

  Target target3 =
      hit_test_query().FindTargetForLocation(EventSource::MOUSE, point3);
  EXPECT_EQ(target3.frame_sink_id, e_id);
  EXPECT_EQ(target3.location_in_target, point3);
  EXPECT_EQ(target3.flags, HitTestRegionFlags::kHitTestMine |
                               HitTestRegionFlags::kHitTestMouse);

  Target target4 =
      hit_test_query().FindTargetForLocation(EventSource::MOUSE, point4);
  EXPECT_EQ(target4.frame_sink_id, b_id);
  EXPECT_EQ(target4.location_in_target, gfx::PointF(2, 2));
  EXPECT_EQ(target4.flags, HitTestRegionFlags::kHitTestMine |
                               HitTestRegionFlags::kHitTestMouse);
}

// One embedder with a clipped child with a tab and transparent background, and
// a child d under that.
//
//  +e-------------+
//  |      +d------|
//  |   +c-|-------|     Point   maps to
//  | 1 |+a|-+     |     -----   -------
//  |   || 2 |  3  |       1        e
//  |   |+b|-------|       2        a
//  |   || |       |       3        d
//  |   || | 4     |       4        b
//  +--------------+
//
TEST_F(HitTestQueryTest, ClippedChildWithChildUnderneath) {
  FrameSinkId e_id = FrameSinkId(1, 1);
  FrameSinkId c_id = FrameSinkId(2, 2);
  FrameSinkId a_id = FrameSinkId(3, 3);
  FrameSinkId b_id = FrameSinkId(4, 4);
  FrameSinkId d_id = FrameSinkId(5, 5);
  gfx::Rect e_bounds_in_e = gfx::Rect(0, 0, 600, 600);
  gfx::Rect c_bounds_in_e = gfx::Rect(0, 0, 800, 800);
  gfx::Rect a_bounds_in_c = gfx::Rect(0, 0, 200, 100);
  gfx::Rect b_bounds_in_c = gfx::Rect(0, 0, 800, 600);
  gfx::Rect d_bounds_in_e = gfx::Rect(0, 0, 800, 800);
  gfx::Transform transform_e_to_e, transform_e_to_c, transform_c_to_a,
      transform_c_to_b, transform_e_to_d;
  transform_e_to_c.Translate(-200, -100);
  transform_c_to_b.Translate(0, -100);
  transform_e_to_d.Translate(-400, -50);
  active_data_.push_back(AggregatedHitTestRegion(
      e_id,
      HitTestRegionFlags::kHitTestMine | HitTestRegionFlags::kHitTestMouse,
      e_bounds_in_e, transform_e_to_e, 4));  // e
  active_data_.push_back(
      AggregatedHitTestRegion(c_id, HitTestRegionFlags::kHitTestChildSurface,
                              c_bounds_in_e, transform_e_to_c, 2));  // c
  active_data_.push_back(AggregatedHitTestRegion(
      a_id,
      HitTestRegionFlags::kHitTestMine | HitTestRegionFlags::kHitTestMouse,
      a_bounds_in_c, transform_c_to_a, 0));  // a
  active_data_.push_back(AggregatedHitTestRegion(
      b_id,
      HitTestRegionFlags::kHitTestMine | HitTestRegionFlags::kHitTestMouse,
      b_bounds_in_c, transform_c_to_b, 0));  // b
  active_data_.push_back(AggregatedHitTestRegion(
      d_id,
      HitTestRegionFlags::kHitTestMine | HitTestRegionFlags::kHitTestMouse,
      d_bounds_in_e, transform_e_to_d, 0));  // d
  SendHitTestData();

  // All points are in e's coordinate system when we reach this case.
  gfx::PointF point1(1, 1);
  gfx::PointF point2(202, 102);
  gfx::PointF point3(450, 150);
  gfx::PointF point4(202, 202);

  Target target1 =
      hit_test_query().FindTargetForLocation(EventSource::MOUSE, point1);
  EXPECT_EQ(target1.frame_sink_id, e_id);
  EXPECT_EQ(target1.location_in_target, point1);
  EXPECT_EQ(target1.flags, HitTestRegionFlags::kHitTestMine |
                               HitTestRegionFlags::kHitTestMouse);

  Target target2 =
      hit_test_query().FindTargetForLocation(EventSource::MOUSE, point2);
  EXPECT_EQ(target2.frame_sink_id, a_id);
  EXPECT_EQ(target2.location_in_target, gfx::PointF(2, 2));
  EXPECT_EQ(target2.flags, HitTestRegionFlags::kHitTestMine |
                               HitTestRegionFlags::kHitTestMouse);

  Target target3 =
      hit_test_query().FindTargetForLocation(EventSource::MOUSE, point3);
  EXPECT_EQ(target3.frame_sink_id, d_id);
  EXPECT_EQ(target3.location_in_target, gfx::PointF(50, 100));
  EXPECT_EQ(target3.flags, HitTestRegionFlags::kHitTestMine |
                               HitTestRegionFlags::kHitTestMouse);

  Target target4 =
      hit_test_query().FindTargetForLocation(EventSource::MOUSE, point4);
  EXPECT_EQ(target4.frame_sink_id, b_id);
  EXPECT_EQ(target4.location_in_target, gfx::PointF(2, 2));
  EXPECT_EQ(target4.flags, HitTestRegionFlags::kHitTestMine |
                               HitTestRegionFlags::kHitTestMouse);
}

// Tests transforming location to be in target's coordinate system given the
// target's ancestor list, in the case of ClippedChildWithChildUnderneath test.
TEST_F(HitTestQueryTest, ClippedChildWithChildUnderneathTransform) {
  FrameSinkId e_id = FrameSinkId(1, 1);
  FrameSinkId c_id = FrameSinkId(2, 2);
  FrameSinkId a_id = FrameSinkId(3, 3);
  FrameSinkId b_id = FrameSinkId(4, 4);
  FrameSinkId d_id = FrameSinkId(5, 5);
  gfx::Rect e_bounds_in_e = gfx::Rect(0, 0, 600, 600);
  gfx::Rect c_bounds_in_e = gfx::Rect(0, 0, 800, 800);
  gfx::Rect a_bounds_in_c = gfx::Rect(0, 0, 200, 100);
  gfx::Rect b_bounds_in_c = gfx::Rect(0, 0, 800, 600);
  gfx::Rect d_bounds_in_e = gfx::Rect(0, 0, 800, 800);
  gfx::Transform transform_e_to_e, transform_e_to_c, transform_c_to_a,
      transform_c_to_b, transform_e_to_d;
  transform_e_to_c.Translate(-200, -100);
  transform_e_to_d.Translate(-400, -50);
  transform_c_to_b.Translate(0, -100);
  active_data_.push_back(AggregatedHitTestRegion(
      e_id,
      HitTestRegionFlags::kHitTestMine | HitTestRegionFlags::kHitTestMouse,
      e_bounds_in_e, transform_e_to_e, 4));  // e
  active_data_.push_back(
      AggregatedHitTestRegion(c_id, HitTestRegionFlags::kHitTestChildSurface,
                              c_bounds_in_e, transform_e_to_c, 2));  // c
  active_data_.push_back(AggregatedHitTestRegion(
      a_id,
      HitTestRegionFlags::kHitTestMine | HitTestRegionFlags::kHitTestMouse,
      a_bounds_in_c, transform_c_to_a, 0));  // a
  active_data_.push_back(AggregatedHitTestRegion(
      b_id,
      HitTestRegionFlags::kHitTestMine | HitTestRegionFlags::kHitTestMouse,
      b_bounds_in_c, transform_c_to_b, 0));  // b
  active_data_.push_back(AggregatedHitTestRegion(
      d_id,
      HitTestRegionFlags::kHitTestMine | HitTestRegionFlags::kHitTestMouse,
      d_bounds_in_e, transform_e_to_d, 0));  // d
  SendHitTestData();

  // All points are in e's coordinate system when we reach this case.
  gfx::PointF point1(1, 1);
  gfx::PointF point2(202, 102);
  gfx::PointF point3(450, 150);
  gfx::PointF point4(202, 202);

  std::vector<FrameSinkId> target_ancestors1{e_id};
  gfx::PointF transformed_point;
  EXPECT_TRUE(hit_test_query().TransformLocationForTarget(
      target_ancestors1, point1, &transformed_point));
  EXPECT_EQ(transformed_point, point1);
  std::vector<FrameSinkId> target_ancestors2{a_id, c_id, e_id};
  EXPECT_TRUE(hit_test_query().TransformLocationForTarget(
      target_ancestors2, point2, &transformed_point));
  EXPECT_EQ(transformed_point, gfx::PointF(2, 2));
  std::vector<FrameSinkId> target_ancestors3{d_id, e_id};
  EXPECT_TRUE(hit_test_query().TransformLocationForTarget(
      target_ancestors3, point3, &transformed_point));
  EXPECT_EQ(transformed_point, gfx::PointF(50, 100));
  std::vector<FrameSinkId> target_ancestors4{b_id, c_id, e_id};
  EXPECT_TRUE(hit_test_query().TransformLocationForTarget(
      target_ancestors4, point4, &transformed_point));
  EXPECT_EQ(transformed_point, gfx::PointF(2, 2));
}

// One embedder with two clipped children with a tab and transparent background.
//
//  +e-------------+
//  |   +c1--------|     Point   maps to
//  | 1 |+a--+     |     -----   -------
//  |   || 2 |  3  |       1        e
//  |   |+b--------|       2        a
//  |   ||         |       3        e ( transparent area in c1 )
//  |   ||   4     |       4        b
//  |   +----------|       5        g
//  |   +c2--------|       6        e ( transparent area in c2 )
//  |   |+g--+     |       7        h
//  |   || 5 |  6  |
//  |   |+h--------|
//  |   ||         |
//  |   ||   7     |
//  +--------------+
//
TEST_F(HitTestQueryTest, ClippedChildrenWithTabAndTransparentBackground) {
  FrameSinkId e_id = FrameSinkId(1, 1);
  FrameSinkId c1_id = FrameSinkId(2, 2);
  FrameSinkId a_id = FrameSinkId(3, 3);
  FrameSinkId b_id = FrameSinkId(4, 4);
  FrameSinkId c2_id = FrameSinkId(5, 5);
  FrameSinkId g_id = FrameSinkId(6, 6);
  FrameSinkId h_id = FrameSinkId(7, 7);
  gfx::Rect e_bounds_in_e = gfx::Rect(0, 0, 600, 1200);
  gfx::Rect c1_bounds_in_e = gfx::Rect(0, 0, 800, 500);
  gfx::Rect a_bounds_in_c1 = gfx::Rect(0, 0, 200, 100);
  gfx::Rect b_bounds_in_c1 = gfx::Rect(0, 0, 800, 400);
  gfx::Rect c2_bounds_in_e = gfx::Rect(0, 0, 800, 500);
  gfx::Rect g_bounds_in_c2 = gfx::Rect(0, 0, 200, 100);
  gfx::Rect h_bounds_in_c2 = gfx::Rect(0, 0, 800, 800);
  gfx::Transform transform_e_to_e, transform_e_to_c1, transform_c1_to_a,
      transform_c1_to_b, transform_e_to_c2, transform_c2_to_g,
      transform_c2_to_h;
  transform_e_to_c1.Translate(-200, -100);
  transform_c1_to_b.Translate(0, -100);
  transform_e_to_c2.Translate(-200, -700);
  transform_c2_to_h.Translate(0, -100);
  active_data_.push_back(AggregatedHitTestRegion(
      e_id,
      HitTestRegionFlags::kHitTestMine | HitTestRegionFlags::kHitTestMouse,
      e_bounds_in_e, transform_e_to_e, 6));  // e
  active_data_.push_back(
      AggregatedHitTestRegion(c1_id, HitTestRegionFlags::kHitTestChildSurface,
                              c1_bounds_in_e, transform_e_to_c1, 2));  // c1
  active_data_.push_back(AggregatedHitTestRegion(
      a_id,
      HitTestRegionFlags::kHitTestMine | HitTestRegionFlags::kHitTestMouse,
      a_bounds_in_c1, transform_c1_to_a, 0));  // a
  active_data_.push_back(AggregatedHitTestRegion(
      b_id,
      HitTestRegionFlags::kHitTestMine | HitTestRegionFlags::kHitTestMouse,
      b_bounds_in_c1, transform_c1_to_b, 0));  // b
  active_data_.push_back(
      AggregatedHitTestRegion(c2_id, HitTestRegionFlags::kHitTestChildSurface,
                              c2_bounds_in_e, transform_e_to_c2, 2));  // c2
  active_data_.push_back(AggregatedHitTestRegion(
      g_id,
      HitTestRegionFlags::kHitTestMine | HitTestRegionFlags::kHitTestMouse,
      g_bounds_in_c2, transform_c2_to_g, 0));  // g
  active_data_.push_back(AggregatedHitTestRegion(
      h_id,
      HitTestRegionFlags::kHitTestMine | HitTestRegionFlags::kHitTestMouse,
      h_bounds_in_c2, transform_c2_to_h, 0));  // h
  SendHitTestData();

  // All points are in e's coordinate system when we reach this case.
  gfx::PointF point1(1, 1);
  gfx::PointF point2(202, 102);
  gfx::PointF point3(403, 103);
  gfx::PointF point4(202, 202);
  gfx::PointF point5(250, 750);
  gfx::PointF point6(450, 750);
  gfx::PointF point7(350, 1100);

  Target target1 =
      hit_test_query().FindTargetForLocation(EventSource::MOUSE, point1);
  EXPECT_EQ(target1.frame_sink_id, e_id);
  EXPECT_EQ(target1.location_in_target, point1);
  EXPECT_EQ(target1.flags, HitTestRegionFlags::kHitTestMine |
                               HitTestRegionFlags::kHitTestMouse);

  Target target2 =
      hit_test_query().FindTargetForLocation(EventSource::MOUSE, point2);
  EXPECT_EQ(target2.frame_sink_id, a_id);
  EXPECT_EQ(target2.location_in_target, gfx::PointF(2, 2));
  EXPECT_EQ(target2.flags, HitTestRegionFlags::kHitTestMine |
                               HitTestRegionFlags::kHitTestMouse);

  Target target3 =
      hit_test_query().FindTargetForLocation(EventSource::MOUSE, point3);
  EXPECT_EQ(target3.frame_sink_id, e_id);
  EXPECT_EQ(target3.location_in_target, point3);
  EXPECT_EQ(target3.flags, HitTestRegionFlags::kHitTestMine |
                               HitTestRegionFlags::kHitTestMouse);

  Target target4 =
      hit_test_query().FindTargetForLocation(EventSource::MOUSE, point4);
  EXPECT_EQ(target4.frame_sink_id, b_id);
  EXPECT_EQ(target4.location_in_target, gfx::PointF(2, 2));
  EXPECT_EQ(target4.flags, HitTestRegionFlags::kHitTestMine |
                               HitTestRegionFlags::kHitTestMouse);

  Target target5 =
      hit_test_query().FindTargetForLocation(EventSource::MOUSE, point5);
  EXPECT_EQ(target5.frame_sink_id, g_id);
  EXPECT_EQ(target5.location_in_target, gfx::PointF(50, 50));
  EXPECT_EQ(target5.flags, HitTestRegionFlags::kHitTestMine |
                               HitTestRegionFlags::kHitTestMouse);

  Target target6 =
      hit_test_query().FindTargetForLocation(EventSource::MOUSE, point6);
  EXPECT_EQ(target6.frame_sink_id, e_id);
  EXPECT_EQ(target6.location_in_target, point6);
  EXPECT_EQ(target6.flags, HitTestRegionFlags::kHitTestMine |
                               HitTestRegionFlags::kHitTestMouse);

  Target target7 =
      hit_test_query().FindTargetForLocation(EventSource::MOUSE, point7);
  EXPECT_EQ(target7.frame_sink_id, h_id);
  EXPECT_EQ(target7.location_in_target, gfx::PointF(150, 300));
  EXPECT_EQ(target7.flags, HitTestRegionFlags::kHitTestMine |
                               HitTestRegionFlags::kHitTestMouse);
}

// Tests transforming location to be in target's coordinate system given the
// target's ancestor list, in the case of
// ClippedChildrenWithTabAndTransparentBackground test.
TEST_F(HitTestQueryTest,
       ClippedChildrenWithTabAndTransparentBackgroundTransform) {
  FrameSinkId e_id = FrameSinkId(1, 1);
  FrameSinkId c1_id = FrameSinkId(2, 2);
  FrameSinkId a_id = FrameSinkId(3, 3);
  FrameSinkId b_id = FrameSinkId(4, 4);
  FrameSinkId c2_id = FrameSinkId(5, 5);
  FrameSinkId g_id = FrameSinkId(6, 6);
  FrameSinkId h_id = FrameSinkId(7, 7);
  gfx::Rect e_bounds_in_e = gfx::Rect(0, 0, 600, 1200);
  gfx::Rect c1_bounds_in_e = gfx::Rect(0, 0, 800, 500);
  gfx::Rect a_bounds_in_c1 = gfx::Rect(0, 0, 200, 100);
  gfx::Rect b_bounds_in_c1 = gfx::Rect(0, 0, 800, 400);
  gfx::Rect c2_bounds_in_e = gfx::Rect(0, 0, 800, 500);
  gfx::Rect g_bounds_in_c2 = gfx::Rect(0, 0, 200, 100);
  gfx::Rect h_bounds_in_c2 = gfx::Rect(0, 0, 800, 800);
  gfx::Transform transform_e_to_e, transform_e_to_c1, transform_c1_to_a,
      transform_c1_to_b, transform_e_to_c2, transform_c2_to_g,
      transform_c2_to_h;
  transform_e_to_c1.Translate(-200, -100);
  transform_c1_to_b.Translate(0, -100);
  transform_e_to_c2.Translate(-200, -700);
  transform_c2_to_h.Translate(0, -100);
  active_data_.push_back(AggregatedHitTestRegion(
      e_id,
      HitTestRegionFlags::kHitTestMine | HitTestRegionFlags::kHitTestMouse,
      e_bounds_in_e, transform_e_to_e, 6));  // e
  active_data_.push_back(
      AggregatedHitTestRegion(c1_id, HitTestRegionFlags::kHitTestChildSurface,
                              c1_bounds_in_e, transform_e_to_c1, 2));  // c1
  active_data_.push_back(AggregatedHitTestRegion(
      a_id,
      HitTestRegionFlags::kHitTestMine | HitTestRegionFlags::kHitTestMouse,
      a_bounds_in_c1, transform_c1_to_a, 0));  // a
  active_data_.push_back(AggregatedHitTestRegion(
      b_id,
      HitTestRegionFlags::kHitTestMine | HitTestRegionFlags::kHitTestMouse,
      b_bounds_in_c1, transform_c1_to_b, 0));  // b
  active_data_.push_back(
      AggregatedHitTestRegion(c2_id, HitTestRegionFlags::kHitTestChildSurface,
                              c2_bounds_in_e, transform_e_to_c2, 2));  // c2
  active_data_.push_back(AggregatedHitTestRegion(
      g_id,
      HitTestRegionFlags::kHitTestMine | HitTestRegionFlags::kHitTestMouse,
      g_bounds_in_c2, transform_c2_to_g, 0));  // g
  active_data_.push_back(AggregatedHitTestRegion(
      h_id,
      HitTestRegionFlags::kHitTestMine | HitTestRegionFlags::kHitTestMouse,
      h_bounds_in_c2, transform_c2_to_h, 0));  // h
  SendHitTestData();

  // All points are in e's coordinate system when we reach this case.
  gfx::PointF point1(1, 1);
  gfx::PointF point2(202, 102);
  gfx::PointF point3(403, 103);
  gfx::PointF point4(202, 202);
  gfx::PointF point5(250, 750);
  gfx::PointF point6(450, 750);
  gfx::PointF point7(350, 1100);

  std::vector<FrameSinkId> target_ancestors1{e_id};
  gfx::PointF transformed_point;
  EXPECT_TRUE(hit_test_query().TransformLocationForTarget(
      target_ancestors1, point1, &transformed_point));
  EXPECT_EQ(transformed_point, point1);
  std::vector<FrameSinkId> target_ancestors2{a_id, c1_id, e_id};
  EXPECT_TRUE(hit_test_query().TransformLocationForTarget(
      target_ancestors2, point2, &transformed_point));
  EXPECT_EQ(transformed_point, gfx::PointF(2, 2));
  EXPECT_TRUE(hit_test_query().TransformLocationForTarget(
      target_ancestors1, point3, &transformed_point));
  EXPECT_EQ(transformed_point, point3);
  std::vector<FrameSinkId> target_ancestors3{b_id, c1_id, e_id};
  EXPECT_TRUE(hit_test_query().TransformLocationForTarget(
      target_ancestors3, point4, &transformed_point));
  EXPECT_EQ(transformed_point, gfx::PointF(2, 2));
  std::vector<FrameSinkId> target_ancestors4{g_id, c2_id, e_id};
  EXPECT_TRUE(hit_test_query().TransformLocationForTarget(
      target_ancestors4, point5, &transformed_point));
  EXPECT_EQ(transformed_point, gfx::PointF(50, 50));
  EXPECT_TRUE(hit_test_query().TransformLocationForTarget(
      target_ancestors1, point6, &transformed_point));
  EXPECT_EQ(transformed_point, point6);
  std::vector<FrameSinkId> target_ancestors5{h_id, c2_id, e_id};
  EXPECT_TRUE(hit_test_query().TransformLocationForTarget(
      target_ancestors5, point7, &transformed_point));
  EXPECT_EQ(transformed_point, gfx::PointF(150, 300));
}

// Children that are multiple layers deep.
//
//  +e--------------------+
//  |          +c2--------|     Point   maps to
//  | +c1------|----+     |     -----   -------
//  |1| +a-----|---+|     |       1        e
//  | | |+b----|--+||     |       2        g
//  | | ||+g--+|  |||     |       3        b
//  | | ||| 2 || 3|||  4  |       4        c2
//  | | ||+---+|  |||     |
//  | | |+-----|--+||     |
//  | | +------| --+|     |
//  | +--------|----+     |
//  +---------------------+
//
TEST_F(HitTestQueryTest, MultipleLayerChild) {
  FrameSinkId e_id = FrameSinkId(1, 1);
  FrameSinkId c1_id = FrameSinkId(2, 2);
  FrameSinkId a_id = FrameSinkId(3, 3);
  FrameSinkId b_id = FrameSinkId(4, 4);
  FrameSinkId g_id = FrameSinkId(5, 5);
  FrameSinkId c2_id = FrameSinkId(6, 6);
  gfx::Rect e_bounds_in_e = gfx::Rect(0, 0, 1000, 1000);
  gfx::Rect c1_bounds_in_e = gfx::Rect(0, 0, 800, 800);
  gfx::Rect a_bounds_in_c1 = gfx::Rect(0, 0, 700, 700);
  gfx::Rect b_bounds_in_a = gfx::Rect(0, 0, 600, 600);
  gfx::Rect g_bounds_in_b = gfx::Rect(0, 0, 200, 200);
  gfx::Rect c2_bounds_in_e = gfx::Rect(0, 0, 800, 800);
  gfx::Transform transform_e_to_e, transform_e_to_c1, transform_c1_to_a,
      transform_a_to_b, transform_b_to_g, transform_e_to_c2;
  transform_e_to_c1.Translate(-100, -100);
  transform_a_to_b.Translate(-50, -30);
  transform_b_to_g.Translate(-150, -200);
  transform_e_to_c2.Translate(-400, -50);
  active_data_.push_back(AggregatedHitTestRegion(
      e_id,
      HitTestRegionFlags::kHitTestMine | HitTestRegionFlags::kHitTestMouse,
      e_bounds_in_e, transform_e_to_e, 5));  // e
  active_data_.push_back(
      AggregatedHitTestRegion(c1_id, HitTestRegionFlags::kHitTestChildSurface,
                              c1_bounds_in_e, transform_e_to_c1, 3));  // c1
  active_data_.push_back(AggregatedHitTestRegion(
      a_id,
      HitTestRegionFlags::kHitTestMine | HitTestRegionFlags::kHitTestMouse,
      a_bounds_in_c1, transform_c1_to_a, 2));  // a
  active_data_.push_back(AggregatedHitTestRegion(
      b_id,
      HitTestRegionFlags::kHitTestMine | HitTestRegionFlags::kHitTestMouse,
      b_bounds_in_a, transform_a_to_b, 1));  // b
  active_data_.push_back(AggregatedHitTestRegion(
      g_id,
      HitTestRegionFlags::kHitTestMine | HitTestRegionFlags::kHitTestMouse,
      g_bounds_in_b, transform_b_to_g, 0));  // g
  active_data_.push_back(AggregatedHitTestRegion(
      c2_id,
      HitTestRegionFlags::kHitTestMine | HitTestRegionFlags::kHitTestMouse,
      c2_bounds_in_e, transform_e_to_c2, 0));  // c2
  SendHitTestData();

  // All points are in e's coordinate system when we reach this case.
  gfx::PointF point1(1, 1);
  gfx::PointF point2(300, 350);
  gfx::PointF point3(550, 350);
  gfx::PointF point4(900, 350);

  Target target1 =
      hit_test_query().FindTargetForLocation(EventSource::MOUSE, point1);
  EXPECT_EQ(target1.frame_sink_id, e_id);
  EXPECT_EQ(target1.location_in_target, point1);
  EXPECT_EQ(target1.flags, HitTestRegionFlags::kHitTestMine |
                               HitTestRegionFlags::kHitTestMouse);

  Target target2 =
      hit_test_query().FindTargetForLocation(EventSource::MOUSE, point2);
  EXPECT_EQ(target2.frame_sink_id, g_id);
  EXPECT_EQ(target2.location_in_target, gfx::PointF(0, 20));
  EXPECT_EQ(target2.flags, HitTestRegionFlags::kHitTestMine |
                               HitTestRegionFlags::kHitTestMouse);

  Target target3 =
      hit_test_query().FindTargetForLocation(EventSource::MOUSE, point3);
  EXPECT_EQ(target3.frame_sink_id, b_id);
  EXPECT_EQ(target3.location_in_target, gfx::PointF(400, 220));
  EXPECT_EQ(target3.flags, HitTestRegionFlags::kHitTestMine |
                               HitTestRegionFlags::kHitTestMouse);

  Target target4 =
      hit_test_query().FindTargetForLocation(EventSource::MOUSE, point4);
  EXPECT_EQ(target4.frame_sink_id, c2_id);
  EXPECT_EQ(target4.location_in_target, gfx::PointF(500, 300));
  EXPECT_EQ(target4.flags, HitTestRegionFlags::kHitTestMine |
                               HitTestRegionFlags::kHitTestMouse);
}

// Multiple layers deep of transparent children.
//
//  +e--------------------+
//  |          +c2--------|     Point   maps to
//  | +c1------|----+     |     -----   -------
//  |1| +a-----|---+|     |       1        e
//  | | |+b----|--+||     |       2        e
//  | | ||+g--+|  |||     |       3        c2
//  | | ||| 2 || 3|||  4  |       4        c2
//  | | ||+---+|  |||     |
//  | | |+-----|--+||     |
//  | | +------| --+|     |
//  | +--------|----+     |
//  +---------------------+
//
TEST_F(HitTestQueryTest, MultipleLayerTransparentChild) {
  FrameSinkId e_id = FrameSinkId(1, 1);
  FrameSinkId c1_id = FrameSinkId(2, 2);
  FrameSinkId a_id = FrameSinkId(3, 3);
  FrameSinkId b_id = FrameSinkId(4, 4);
  FrameSinkId g_id = FrameSinkId(5, 5);
  FrameSinkId c2_id = FrameSinkId(6, 6);
  gfx::Rect e_bounds_in_e = gfx::Rect(0, 0, 1000, 1000);
  gfx::Rect c1_bounds_in_e = gfx::Rect(0, 0, 800, 800);
  gfx::Rect a_bounds_in_c1 = gfx::Rect(0, 0, 700, 700);
  gfx::Rect b_bounds_in_a = gfx::Rect(0, 0, 600, 600);
  gfx::Rect g_bounds_in_b = gfx::Rect(0, 0, 200, 200);
  gfx::Rect c2_bounds_in_e = gfx::Rect(0, 0, 800, 800);
  gfx::Transform transform_e_to_e, transform_e_to_c1, transform_c1_to_a,
      transform_a_to_b, transform_b_to_g, transform_e_to_c2;
  transform_e_to_c1.Translate(-100, -100);
  transform_a_to_b.Translate(-50, -30);
  transform_b_to_g.Translate(-150, -200);
  transform_e_to_c2.Translate(-400, -50);
  active_data_.push_back(AggregatedHitTestRegion(
      e_id,
      HitTestRegionFlags::kHitTestMine | HitTestRegionFlags::kHitTestMouse,
      e_bounds_in_e, transform_e_to_e, 5));  // e
  active_data_.push_back(
      AggregatedHitTestRegion(c1_id, HitTestRegionFlags::kHitTestChildSurface,
                              c1_bounds_in_e, transform_e_to_c1, 3));  // c1
  active_data_.push_back(
      AggregatedHitTestRegion(a_id, HitTestRegionFlags::kHitTestChildSurface,
                              a_bounds_in_c1, transform_c1_to_a, 2));  // a
  active_data_.push_back(
      AggregatedHitTestRegion(b_id, HitTestRegionFlags::kHitTestChildSurface,
                              b_bounds_in_a, transform_a_to_b, 1));  // b
  active_data_.push_back(
      AggregatedHitTestRegion(g_id, HitTestRegionFlags::kHitTestChildSurface,
                              g_bounds_in_b, transform_b_to_g, 0));  // g
  active_data_.push_back(AggregatedHitTestRegion(
      c2_id,
      HitTestRegionFlags::kHitTestMine | HitTestRegionFlags::kHitTestMouse,
      c2_bounds_in_e, transform_e_to_c2, 0));  // c2
  SendHitTestData();

  // All points are in e's coordinate system when we reach this case.
  gfx::PointF point1(1, 1);
  gfx::PointF point2(300, 350);
  gfx::PointF point3(450, 350);
  gfx::PointF point4(900, 350);

  Target target1 =
      hit_test_query().FindTargetForLocation(EventSource::MOUSE, point1);
  EXPECT_EQ(target1.frame_sink_id, e_id);
  EXPECT_EQ(target1.location_in_target, point1);
  EXPECT_TRUE(target1.flags);

  Target target2 =
      hit_test_query().FindTargetForLocation(EventSource::MOUSE, point2);
  EXPECT_EQ(target2.frame_sink_id, e_id);
  EXPECT_EQ(target2.location_in_target, point2);
  EXPECT_TRUE(target2.flags);

  Target target3 =
      hit_test_query().FindTargetForLocation(EventSource::MOUSE, point3);
  EXPECT_EQ(target3.frame_sink_id, c2_id);
  EXPECT_EQ(target3.location_in_target, gfx::PointF(50, 300));
  EXPECT_TRUE(target3.flags);

  Target target4 =
      hit_test_query().FindTargetForLocation(EventSource::MOUSE, point4);
  EXPECT_EQ(target4.frame_sink_id, c2_id);
  EXPECT_EQ(target4.location_in_target, gfx::PointF(500, 300));
  EXPECT_TRUE(target4.flags);
}

TEST_F(HitTestQueryTest, InvalidAggregatedHitTestRegionData) {
  FrameSinkId e_id = FrameSinkId(1, 1);
  FrameSinkId c_id = FrameSinkId(2, 2);
  FrameSinkId a_id = FrameSinkId(3, 3);
  FrameSinkId b_id = FrameSinkId(4, 4);
  gfx::Rect e_bounds_in_e = gfx::Rect(0, 0, 600, 600);
  gfx::Rect c_bounds_in_e = gfx::Rect(0, 0, 800, 800);
  gfx::Rect a_bounds_in_c = gfx::Rect(0, 0, 200, 100);
  gfx::Rect b_bounds_in_c = gfx::Rect(0, 0, 800, 600);
  gfx::Transform transform_e_to_e, transform_e_to_c, transform_c_to_a,
      transform_c_to_b;
  transform_e_to_c.Translate(-200, -100);
  transform_c_to_b.Translate(0, -100);
  active_data_.push_back(AggregatedHitTestRegion(
      e_id,
      HitTestRegionFlags::kHitTestMine | HitTestRegionFlags::kHitTestMouse,
      e_bounds_in_e, transform_e_to_e, 3));  // e
  active_data_.push_back(AggregatedHitTestRegion(
      c_id, HitTestRegionFlags::kHitTestChildSurface, c_bounds_in_e,
      transform_e_to_c, INT32_MIN));  // c
  active_data_.push_back(AggregatedHitTestRegion(
      a_id,
      HitTestRegionFlags::kHitTestMine | HitTestRegionFlags::kHitTestMouse,
      a_bounds_in_c, transform_c_to_a, 0));  // a
  active_data_.push_back(AggregatedHitTestRegion(
      b_id,
      HitTestRegionFlags::kHitTestMine | HitTestRegionFlags::kHitTestMouse,
      b_bounds_in_c, transform_c_to_b, 0));  // b
  SendHitTestData();

  // All points are in e's coordinate system when we reach this case.
  gfx::PointF point1(1, 1);
  gfx::PointF point2(202, 102);

  // |child_count| is invalid, which is a security fault. For now, check to see
  // if the returned Target is invalid.
  Target target1 =
      hit_test_query().FindTargetForLocation(EventSource::MOUSE, point1);
  EXPECT_EQ(target1.frame_sink_id, FrameSinkId());
  EXPECT_EQ(target1.location_in_target, gfx::PointF());
  EXPECT_FALSE(target1.flags);

  Target target2 =
      hit_test_query().FindTargetForLocation(EventSource::MOUSE, point2);
  EXPECT_EQ(target2.frame_sink_id, FrameSinkId());
  EXPECT_EQ(target2.location_in_target, gfx::PointF());
  EXPECT_FALSE(target2.flags);

  active_data_.clear();
  active_data_.push_back(AggregatedHitTestRegion(
      e_id,
      HitTestRegionFlags::kHitTestMine | HitTestRegionFlags::kHitTestMouse,
      e_bounds_in_e, transform_e_to_e, INT32_MAX));  // e
  active_data_.push_back(
      AggregatedHitTestRegion(c_id, HitTestRegionFlags::kHitTestChildSurface,
                              c_bounds_in_e, transform_e_to_c, 2));  // c
  active_data_.push_back(AggregatedHitTestRegion(
      a_id,
      HitTestRegionFlags::kHitTestMine | HitTestRegionFlags::kHitTestMouse,
      a_bounds_in_c, transform_c_to_a, 0));  // a
  active_data_.push_back(AggregatedHitTestRegion(
      b_id,
      HitTestRegionFlags::kHitTestMine | HitTestRegionFlags::kHitTestMouse,
      b_bounds_in_c, transform_c_to_b, 0));  // b
  SendHitTestData();

  Target target3 =
      hit_test_query().FindTargetForLocation(EventSource::MOUSE, point1);
  EXPECT_EQ(target3.frame_sink_id, FrameSinkId());
  EXPECT_EQ(target3.location_in_target, gfx::PointF());
  EXPECT_FALSE(target3.flags);

  active_data_.clear();
  active_data_.push_back(AggregatedHitTestRegion(
      e_id,
      HitTestRegionFlags::kHitTestMine | HitTestRegionFlags::kHitTestMouse,
      e_bounds_in_e, transform_e_to_e, 3));  // e
  active_data_.push_back(
      AggregatedHitTestRegion(c_id, HitTestRegionFlags::kHitTestChildSurface,
                              c_bounds_in_e, transform_e_to_c, 3));  // c
  active_data_.push_back(AggregatedHitTestRegion(
      a_id,
      HitTestRegionFlags::kHitTestMine | HitTestRegionFlags::kHitTestMouse,
      a_bounds_in_c, transform_c_to_a, 0));  // a
  active_data_.push_back(AggregatedHitTestRegion(
      b_id,
      HitTestRegionFlags::kHitTestMine | HitTestRegionFlags::kHitTestMouse,
      b_bounds_in_c, transform_c_to_b, 0));  // b
  SendHitTestData();

  Target target4 =
      hit_test_query().FindTargetForLocation(EventSource::MOUSE, point1);
  EXPECT_EQ(target4.frame_sink_id, FrameSinkId());
  EXPECT_EQ(target4.location_in_target, gfx::PointF());
  EXPECT_FALSE(target4.flags);
}

// Tests flags kHitTestMouse and kHitTestTouch.
TEST_F(HitTestQueryTest, MouseTouchFlags) {
  FrameSinkId e_id = FrameSinkId(1, 1);
  FrameSinkId c1_id = FrameSinkId(2, 2);
  FrameSinkId c2_id = FrameSinkId(3, 3);
  gfx::Rect e_bounds_in_e = gfx::Rect(0, 0, 600, 600);
  gfx::Rect c1_bounds_in_e = gfx::Rect(0, 0, 300, 200);
  gfx::Rect c2_bounds_in_e = gfx::Rect(0, 0, 350, 250);
  gfx::Transform transform_e_to_e, transform_e_to_c1, transform_e_to_c2;
  transform_e_to_c1.Translate(-100, -100);
  transform_e_to_c2.Translate(-75, -75);
  active_data_.push_back(AggregatedHitTestRegion(
      e_id,
      HitTestRegionFlags::kHitTestMine | HitTestRegionFlags::kHitTestMouse |
          HitTestRegionFlags::kHitTestTouch,
      e_bounds_in_e, transform_e_to_e, 2));  // e
  active_data_.push_back(AggregatedHitTestRegion(
      c1_id,
      HitTestRegionFlags::kHitTestMine | HitTestRegionFlags::kHitTestMouse,
      c1_bounds_in_e, transform_e_to_c1, 0));  // c1
  active_data_.push_back(AggregatedHitTestRegion(
      c2_id,
      HitTestRegionFlags::kHitTestMine | HitTestRegionFlags::kHitTestTouch,
      c2_bounds_in_e, transform_e_to_c2, 0));  // c2
  SendHitTestData();

  // All points are in e's coordinate system when we reach this case.
  gfx::PointF point1(80, 80);
  gfx::PointF point2(150, 150);

  Target target1 =
      hit_test_query().FindTargetForLocation(EventSource::MOUSE, point1);
  EXPECT_EQ(target1.frame_sink_id, e_id);
  EXPECT_EQ(target1.location_in_target, point1);
  EXPECT_EQ(target1.flags, HitTestRegionFlags::kHitTestMine |
                               HitTestRegionFlags::kHitTestMouse |
                               HitTestRegionFlags::kHitTestTouch);

  Target target2 =
      hit_test_query().FindTargetForLocation(EventSource::TOUCH, point1);
  EXPECT_EQ(target2.frame_sink_id, c2_id);
  EXPECT_EQ(target2.location_in_target, gfx::PointF(5, 5));
  EXPECT_EQ(target2.flags, HitTestRegionFlags::kHitTestMine |
                               HitTestRegionFlags::kHitTestTouch);

  Target target3 =
      hit_test_query().FindTargetForLocation(EventSource::MOUSE, point2);
  EXPECT_EQ(target3.frame_sink_id, c1_id);
  EXPECT_EQ(target3.location_in_target, gfx::PointF(50, 50));
  EXPECT_EQ(target3.flags, HitTestRegionFlags::kHitTestMine |
                               HitTestRegionFlags::kHitTestMouse);

  Target target4 =
      hit_test_query().FindTargetForLocation(EventSource::TOUCH, point2);
  EXPECT_EQ(target4.frame_sink_id, c2_id);
  EXPECT_EQ(target4.location_in_target, gfx::PointF(75, 75));
  EXPECT_EQ(target4.flags, HitTestRegionFlags::kHitTestMine |
                               HitTestRegionFlags::kHitTestTouch);
}

TEST_F(HitTestQueryTest, RootHitTestAskFlag) {
  FrameSinkId e_id = FrameSinkId(1, 1);
  gfx::Rect e_bounds = gfx::Rect(0, 0, 600, 600);
  gfx::Transform transform_e_to_e;
  active_data_.push_back(AggregatedHitTestRegion(
      e_id, HitTestRegionFlags::kHitTestAsk | HitTestRegionFlags::kHitTestMouse,
      e_bounds, transform_e_to_e, 0,
      AsyncHitTestReasons::kUseDrawQuadData));  // e
  SendHitTestData();

  // All points are in e's coordinate system when we reach this case.
  gfx::PointF point1(1, 1);
  gfx::PointF point2(600, 600);

  // point1 is inside e but we have to ask clients for targeting.
  Target target1 =
      hit_test_query().FindTargetForLocation(EventSource::MOUSE, point1);
  EXPECT_EQ(target1.frame_sink_id, e_id);
  EXPECT_EQ(target1.location_in_target, point1);
  EXPECT_EQ(target1.flags, HitTestRegionFlags::kHitTestAsk |
                               HitTestRegionFlags::kHitTestMouse);

  // point2 is on the bounds of e so no target found.
  Target target2 =
      hit_test_query().FindTargetForLocation(EventSource::MOUSE, point2);
  EXPECT_EQ(target2.frame_sink_id, FrameSinkId());
  EXPECT_EQ(target2.location_in_target, gfx::PointF());
  EXPECT_FALSE(target2.flags);
}

// One embedder with two children.
//
//  +e------------+     Point   maps to
//  | +c1-+ +c2---|     -----   -------
//  |1|   | |     |      1        e
//  | | 2 | | 3   |      2        c1
//  | +---+ |     |      3        c2
//  +-------------+
//
TEST_F(HitTestQueryTest, ChildHitTestAskFlag) {
  FrameSinkId e_id = FrameSinkId(1, 1);
  FrameSinkId c1_id = FrameSinkId(2, 2);
  FrameSinkId c2_id = FrameSinkId(3, 3);
  gfx::Rect e_bounds_in_e = gfx::Rect(0, 0, 600, 600);
  gfx::Rect c1_bounds_in_e = gfx::Rect(0, 0, 200, 200);
  gfx::Rect c2_bounds_in_e = gfx::Rect(0, 0, 400, 400);
  gfx::Transform transform_e_to_e, transform_e_to_c1, transform_e_to_c2;
  transform_e_to_c1.Translate(-100, -100);
  transform_e_to_c2.Translate(-300, -300);
  active_data_.push_back(AggregatedHitTestRegion(
      e_id,
      HitTestRegionFlags::kHitTestMine | HitTestRegionFlags::kHitTestMouse,
      e_bounds_in_e, transform_e_to_e, 2));  // e
  active_data_.push_back(AggregatedHitTestRegion(
      c1_id,
      HitTestRegionFlags::kHitTestMine | HitTestRegionFlags::kHitTestMouse,
      c1_bounds_in_e, transform_e_to_c1, 0));  // c1
  active_data_.push_back(AggregatedHitTestRegion(
      c2_id,
      HitTestRegionFlags::kHitTestAsk | HitTestRegionFlags::kHitTestMouse,
      c2_bounds_in_e, transform_e_to_c2, 0,
      AsyncHitTestReasons::kOverlappedRegion));  // c2
  SendHitTestData();

  // All points are in e's coordinate system when we reach this case.
  gfx::PointF point1(99, 200);
  gfx::PointF point2(150, 150);
  gfx::PointF point3(400, 400);

  Target target1 =
      hit_test_query().FindTargetForLocation(EventSource::MOUSE, point1);
  EXPECT_EQ(target1.frame_sink_id, e_id);
  EXPECT_EQ(target1.location_in_target, point1);
  EXPECT_EQ(target1.flags, HitTestRegionFlags::kHitTestMine |
                               HitTestRegionFlags::kHitTestMouse);

  Target target2 =
      hit_test_query().FindTargetForLocation(EventSource::MOUSE, point2);
  EXPECT_EQ(target2.frame_sink_id, c1_id);
  EXPECT_EQ(target2.location_in_target, gfx::PointF(50, 50));
  EXPECT_EQ(target2.flags, HitTestRegionFlags::kHitTestMine |
                               HitTestRegionFlags::kHitTestMouse);

  // point3 is inside c2 but we have to ask clients for targeting. Event
  // shouldn't go back to e.
  Target target3 =
      hit_test_query().FindTargetForLocation(EventSource::MOUSE, point3);
  EXPECT_EQ(target3.frame_sink_id, c2_id);
  EXPECT_EQ(target3.location_in_target, gfx::PointF(100, 100));
  EXPECT_EQ(target3.flags, HitTestRegionFlags::kHitTestAsk |
                               HitTestRegionFlags::kHitTestMouse);
}

// One embedder with nested OOPIFs. When the mid-level iframe has kHitTestAsk
// flag we should do async hit test and skip checking its descendants.
//
//  +e-------------+
//  |   +c---------|     Point   maps to
//  | 1 |    2     |     -----   -------
//  |   |          |       1        e
//  |   |+b--------|       2        c
//  |   ||         |       3        c
//  |   ||   3     |
//  +--------------+
//
TEST_F(HitTestQueryTest, NestedOOPIFs) {
  FrameSinkId e_id = FrameSinkId(1, 1);
  FrameSinkId c_id = FrameSinkId(2, 2);
  FrameSinkId b_id = FrameSinkId(3, 3);
  gfx::Rect e_bounds_in_e = gfx::Rect(0, 0, 600, 600);
  gfx::Rect c_bounds_in_e = gfx::Rect(0, 0, 800, 800);
  gfx::Rect b_bounds_in_c = gfx::Rect(0, 0, 800, 600);
  gfx::Transform transform_e_to_e, transform_e_to_c, transform_c_to_b;
  transform_e_to_c.Translate(-200, -100);
  transform_c_to_b.Translate(0, -100);
  active_data_.push_back(AggregatedHitTestRegion(
      e_id,
      HitTestRegionFlags::kHitTestMine | HitTestRegionFlags::kHitTestMouse,
      e_bounds_in_e, transform_e_to_e, 2));  // e
  active_data_.push_back(AggregatedHitTestRegion(
      c_id,
      HitTestRegionFlags::kHitTestChildSurface |
          HitTestRegionFlags::kHitTestAsk | HitTestRegionFlags::kHitTestMouse,
      c_bounds_in_e, transform_e_to_c, 1,
      AsyncHitTestReasons::kOverlappedRegion));  // c
  active_data_.push_back(AggregatedHitTestRegion(
      b_id, HitTestRegionFlags::kHitTestAsk | HitTestRegionFlags::kHitTestMouse,
      b_bounds_in_c, transform_c_to_b, 0,
      AsyncHitTestReasons::kOverlappedRegion));  // b
  SendHitTestData();

  // All points are in e's coordinate system when we reach this case.
  gfx::PointF point1(1, 1);
  gfx::PointF point2(202, 102);
  gfx::PointF point3(202, 202);

  Target target1 =
      hit_test_query().FindTargetForLocation(EventSource::MOUSE, point1);
  EXPECT_EQ(target1.frame_sink_id, e_id);
  EXPECT_EQ(target1.location_in_target, point1);
  EXPECT_EQ(target1.flags, HitTestRegionFlags::kHitTestMine |
                               HitTestRegionFlags::kHitTestMouse);

  // c is the deepest OOPIF for point2, return c with ask flag.
  Target target2 =
      hit_test_query().FindTargetForLocation(EventSource::MOUSE, point2);
  EXPECT_EQ(target2.frame_sink_id, c_id);
  // point2 + transform_e_to_c  = (2, 2).
  EXPECT_EQ(target2.location_in_target, gfx::PointF(2, 2));
  EXPECT_EQ(target2.flags, HitTestRegionFlags::kHitTestChildSurface |
                               HitTestRegionFlags::kHitTestAsk |
                               HitTestRegionFlags::kHitTestMouse);

  // b is the deepest OOPIF for point3, but c has the ask flag. Return c
  // accordingly.
  Target target3 =
      hit_test_query().FindTargetForLocation(EventSource::MOUSE, point3);
  EXPECT_EQ(target3.frame_sink_id, c_id);
  // point3 + transform_e_to_c  = (2, 102).
  EXPECT_EQ(target3.location_in_target, gfx::PointF(2, 102));
  EXPECT_EQ(target3.flags, HitTestRegionFlags::kHitTestChildSurface |
                               HitTestRegionFlags::kHitTestAsk |
                               HitTestRegionFlags::kHitTestMouse);
}

// Tests getting the transform from root to a given target.
TEST_F(HitTestQueryTest, GetTransformToTarget) {
  FrameSinkId e_id = FrameSinkId(1, 1);
  FrameSinkId c_id = FrameSinkId(2, 2);
  FrameSinkId a_id = FrameSinkId(3, 3);
  FrameSinkId b_id = FrameSinkId(4, 4);
  FrameSinkId d_id = FrameSinkId(5, 5);
  gfx::Rect e_bounds_in_e = gfx::Rect(0, 0, 600, 600);
  gfx::Rect c_bounds_in_e = gfx::Rect(0, 0, 800, 800);
  gfx::Rect a_bounds_in_c = gfx::Rect(0, 0, 200, 100);
  gfx::Rect b_bounds_in_c = gfx::Rect(0, 0, 800, 600);
  gfx::Rect d_bounds_in_e = gfx::Rect(0, 0, 800, 800);
  gfx::Transform transform_e_to_e, transform_e_to_c, transform_c_to_a,
      transform_c_to_b, transform_e_to_d;
  transform_e_to_c.Translate(-200, -150);
  transform_e_to_d.Translate(-400, -50);
  transform_c_to_b.Translate(0, -100);
  transform_c_to_b.Skew(2, 3);
  transform_c_to_b.Scale(.5f, .7f);
  active_data_.push_back(AggregatedHitTestRegion(
      e_id,
      HitTestRegionFlags::kHitTestMine | HitTestRegionFlags::kHitTestMouse,
      e_bounds_in_e, transform_e_to_e, 4));  // e
  active_data_.push_back(
      AggregatedHitTestRegion(c_id, HitTestRegionFlags::kHitTestChildSurface,
                              c_bounds_in_e, transform_e_to_c, 2));  // c
  active_data_.push_back(AggregatedHitTestRegion(
      a_id,
      HitTestRegionFlags::kHitTestMine | HitTestRegionFlags::kHitTestMouse,
      a_bounds_in_c, transform_c_to_a, 0));  // a
  active_data_.push_back(AggregatedHitTestRegion(
      b_id,
      HitTestRegionFlags::kHitTestMine | HitTestRegionFlags::kHitTestMouse,
      b_bounds_in_c, transform_c_to_b, 0));  // b
  active_data_.push_back(AggregatedHitTestRegion(
      d_id,
      HitTestRegionFlags::kHitTestMine | HitTestRegionFlags::kHitTestMouse,
      d_bounds_in_e, transform_e_to_d, 0));  // d
  SendHitTestData();

  // Check that we can get the correct transform to all regions.
  gfx::Transform transform_to_e;
  EXPECT_TRUE(hit_test_query().GetTransformToTarget(e_id, &transform_to_e));
  EXPECT_EQ(transform_to_e, gfx::Transform());

  gfx::Transform transform_to_c;
  gfx::Transform expected_transform_to_c;
  expected_transform_to_c.Translate(-200, -150);
  EXPECT_TRUE(hit_test_query().GetTransformToTarget(c_id, &transform_to_c));
  EXPECT_EQ(transform_to_c, expected_transform_to_c);

  gfx::Transform transform_to_a;
  gfx::Transform expected_transform_to_a;
  expected_transform_to_a.Translate(-200, -150);
  EXPECT_TRUE(hit_test_query().GetTransformToTarget(a_id, &transform_to_a));
  EXPECT_EQ(transform_to_a, expected_transform_to_a);

  gfx::Transform transform_to_b;
  gfx::Transform expected_transform_to_b;
  expected_transform_to_b.Translate(-200, -150);
  expected_transform_to_b.ConcatTransform(transform_c_to_b);
  EXPECT_TRUE(hit_test_query().GetTransformToTarget(b_id, &transform_to_b));
  // Use ToString so that we can compare float.
  EXPECT_EQ(transform_to_b.ToString(), expected_transform_to_b.ToString());

  gfx::Transform transform_to_d;
  gfx::Transform expected_transform_to_d;
  expected_transform_to_d.Translate(-400, -50);
  EXPECT_TRUE(hit_test_query().GetTransformToTarget(d_id, &transform_to_d));
  EXPECT_EQ(transform_to_d, expected_transform_to_d);
}

// Region c1 is transparent and on top of e, c2 is a child of e, d1 is a
// child of c1. Any events outside of d1 should go to either c2 or e instead
// of being taken by the transparent c1 region.
//
//  +e/c1----------+
//  |              |     Point   maps to
//  |   +c2-+      |     -----   -------
//  |   | 1 |      |       1        c2
//  |   +d1--------|       2        d1
//  |   | 2        |
//  |   |          |
//  +--------------+
//
TEST_F(HitTestQueryTest, TransparentOverlayRegions) {
  FrameSinkId e_id = FrameSinkId(1, 1);
  FrameSinkId c1_id = FrameSinkId(2, 2);
  FrameSinkId c2_id = FrameSinkId(3, 3);
  FrameSinkId d1_id = FrameSinkId(4, 4);
  gfx::Rect e_bounds_in_e = gfx::Rect(0, 0, 600, 600);
  gfx::Rect c1_bounds_in_e = gfx::Rect(0, 0, 600, 600);
  gfx::Rect c2_bounds_in_e = gfx::Rect(0, 0, 200, 100);
  gfx::Rect d1_bounds_in_c1 = gfx::Rect(0, 0, 400, 300);
  gfx::Transform transform_e_to_e, transform_e_to_c1, transform_e_to_c2,
      transform_c1_to_d1;
  transform_e_to_c2.Translate(-200, -100);
  transform_c1_to_d1.Translate(-200, -200);
  active_data_.push_back(AggregatedHitTestRegion(
      e_id,
      HitTestRegionFlags::kHitTestMine | HitTestRegionFlags::kHitTestMouse,
      e_bounds_in_e, transform_e_to_e, 3));  // e
  active_data_.push_back(
      AggregatedHitTestRegion(c1_id,
                              HitTestRegionFlags::kHitTestChildSurface |
                                  HitTestRegionFlags::kHitTestMouse,
                              c1_bounds_in_e, transform_e_to_c1, 1));  // c1
  active_data_.push_back(AggregatedHitTestRegion(
      d1_id,
      HitTestRegionFlags::kHitTestMine | HitTestRegionFlags::kHitTestMouse,
      d1_bounds_in_c1, transform_c1_to_d1, 0));  // d1
  active_data_.push_back(AggregatedHitTestRegion(
      c2_id,
      HitTestRegionFlags::kHitTestMine | HitTestRegionFlags::kHitTestMouse,
      c2_bounds_in_e, transform_e_to_c2, 0));  // c2
  SendHitTestData();

  // All points are in e's coordinate system when we reach this case.
  gfx::PointF point1(202, 102);
  gfx::PointF point2(202, 202);

  Target target1 =
      hit_test_query().FindTargetForLocation(EventSource::MOUSE, point1);
  EXPECT_EQ(target1.frame_sink_id, c2_id);
  EXPECT_EQ(target1.location_in_target, gfx::PointF(2, 2));
  EXPECT_EQ(target1.flags, HitTestRegionFlags::kHitTestMine |
                               HitTestRegionFlags::kHitTestMouse);

  Target target2 =
      hit_test_query().FindTargetForLocation(EventSource::MOUSE, point2);
  EXPECT_EQ(target2.frame_sink_id, d1_id);
  EXPECT_EQ(target2.location_in_target, gfx::PointF(2, 2));
  EXPECT_EQ(target2.flags, HitTestRegionFlags::kHitTestMine |
                               HitTestRegionFlags::kHitTestMouse);
}

TEST_F(HitTestQueryTest, FindTargetForLocationStartingFrom) {
  FrameSinkId e_id = FrameSinkId(1, 1);
  FrameSinkId c_id = FrameSinkId(2, 2);
  gfx::Rect e_bounds_in_e = gfx::Rect(0, 0, 600, 600);
  gfx::Rect c_bounds_in_e = gfx::Rect(0, 0, 200, 200);
  gfx::Transform transform_e_to_e, transform_e_to_c;
  transform_e_to_c.Translate(-100, -100);
  active_data_.push_back(AggregatedHitTestRegion(
      e_id,
      HitTestRegionFlags::kHitTestMine | HitTestRegionFlags::kHitTestMouse,
      e_bounds_in_e, transform_e_to_e, 1));  // e
  active_data_.push_back(AggregatedHitTestRegion(
      c_id,
      HitTestRegionFlags::kHitTestMine | HitTestRegionFlags::kHitTestMouse,
      c_bounds_in_e, transform_e_to_c, 0));  // c
  SendHitTestData();

  // Test a point in the embedder with the embedder as the initial frame sink.
  gfx::PointF point1(99, 200);
  Target target1 = hit_test_query().FindTargetForLocationStartingFrom(
      EventSource::MOUSE, point1, e_id);
  EXPECT_EQ(target1.frame_sink_id, e_id);
  EXPECT_EQ(target1.location_in_target, point1);
  EXPECT_EQ(target1.flags, HitTestRegionFlags::kHitTestMine |
                               HitTestRegionFlags::kHitTestMouse);

  // Test a point in the child with the embedder as the initial frame sink. The
  // point is still specified in the coordinate space of the embedder.
  gfx::PointF point2(150, 150);
  Target target2 = hit_test_query().FindTargetForLocationStartingFrom(
      EventSource::MOUSE, point2, e_id);
  EXPECT_EQ(target2.frame_sink_id, c_id);
  EXPECT_EQ(target2.location_in_target, gfx::PointF(50, 50));
  EXPECT_EQ(target2.flags, HitTestRegionFlags::kHitTestMine |
                               HitTestRegionFlags::kHitTestMouse);

  // Test a point in the child with the child as the initial frame sink. The
  // point is specified in the coordinate space of the child.
  gfx::PointF point3(40, 40);
  Target target3 = hit_test_query().FindTargetForLocationStartingFrom(
      EventSource::MOUSE, point3, c_id);
  EXPECT_EQ(target3.frame_sink_id, c_id);
  EXPECT_EQ(target3.location_in_target, point3);
  EXPECT_EQ(target3.flags, HitTestRegionFlags::kHitTestMine |
                               HitTestRegionFlags::kHitTestMouse);
}

// One embedder with nested OOPIFs. When the root view is overlapped we should
// continue checking its descendants rather than doing async hit test.
//
//  +e-------------+
//  |   +c---------|     Point   maps to
//  | 1 |    2     |     -----   -------
//  |   |          |       1        e
//  |   |          |       2        c
//  |   |          |
//  |   |          |
//  +--------------+
//
TEST_F(HitTestQueryTest, OverlappedRootView) {
  FrameSinkId e_id = FrameSinkId(1, 1);
  FrameSinkId c_id = FrameSinkId(2, 2);
  gfx::Rect e_bounds_in_e = gfx::Rect(0, 0, 600, 600);
  gfx::Rect c_bounds_in_e = gfx::Rect(0, 0, 800, 800);
  gfx::Transform transform_e_to_e, transform_e_to_c;
  transform_e_to_c.Translate(-200, -100);
  active_data_.push_back(AggregatedHitTestRegion(
      e_id,
      HitTestRegionFlags::kHitTestMine | HitTestRegionFlags::kHitTestMouse |
          HitTestRegionFlags::kHitTestAsk,
      e_bounds_in_e, transform_e_to_e, 1,
      AsyncHitTestReasons::kOverlappedRegion));  // e
  active_data_.push_back(AggregatedHitTestRegion(
      c_id,
      HitTestRegionFlags::kHitTestChildSurface | kHitTestMine |
          HitTestRegionFlags::kHitTestMouse,
      c_bounds_in_e, transform_e_to_c, 0));  // c
  SendHitTestData();

  // All points are in e's coordinate system when we reach this case.
  gfx::PointF point1(1, 1);
  gfx::PointF point2(202, 102);

  Target target1 =
      hit_test_query().FindTargetForLocation(EventSource::MOUSE, point1);
  EXPECT_EQ(target1.frame_sink_id, e_id);
  EXPECT_EQ(target1.location_in_target, point1);
  // kHitTestAsk should be dropped for overlapped root view.
  EXPECT_EQ(target1.flags, HitTestRegionFlags::kHitTestMine |
                               HitTestRegionFlags::kHitTestMouse);

  Target target2 =
      hit_test_query().FindTargetForLocation(EventSource::MOUSE, point2);
  EXPECT_EQ(target2.frame_sink_id, c_id);
  // point2 + transform_e_to_c  = (2, 2).
  EXPECT_EQ(target2.location_in_target, gfx::PointF(2, 2));
  EXPECT_EQ(target2.flags, HitTestRegionFlags::kHitTestChildSurface |
                               HitTestRegionFlags::kHitTestMine |
                               HitTestRegionFlags::kHitTestMouse);
}

}  // namespace test
}  // namespace viz


text:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Controls;

namespace PokerLeagueManager.UI.Wpf.Infrastructure
{
    public interface IMainWindow
    {
        void ShowView(object view);

        void ShowWarning(string title, string message);

        void ShowError(string title, string message);

        void SetWidth(int width);

        void SetHeight(int height);

        void SetMinWidth(int minWidth);

        void SetMinHeight(int minHeight);

        void SetMaxWidth(int maxWidth);

        void SetMaxHeight(int maxHeight);

        void SetWindowTitle(string title);
    }
}

redpajama_set_name: RedPajamaGithub


text:
package org.tolweb.search;

import java.util.Iterator;

import junit.framework.TestCase;

public class GroupSearchOptionTest extends TestCase {

	public void test_constants_are_expected_values() {
		assertEquals("partially", GroupSearchOptions.PARTIAL_OPTION);
		assertEquals("exactly", GroupSearchOptions.EXACT_OPTION);
		assertEquals("beginning of name", GroupSearchOptions.START_OPTION);
		assertEquals("end of name", GroupSearchOptions.END_OPTION);
	}

	public void test_options_list_constructed() {
		assertNotNull(GroupSearchOptions.OPTIONS);
		assertTrue(!GroupSearchOptions.OPTIONS.isEmpty());
		assertEquals(GroupSearchOptions.OPTIONS, GroupSearchOptions.getOptionsList());
	}

	/**
	 * Order will matter when the list is used by a UI component
	 */
	public void test_options_list_ordering() {
		Iterator<String> itr = GroupSearchOptions.OPTIONS.iterator();

		String option = itr.next();
		assertEquals(GroupSearchOptions.PARTIAL_OPTION, option);

		option = itr.next();
		assertEquals(GroupSearchOptions.EXACT_OPTION, option);

		option = itr.next();
		assertEquals(GroupSearchOptions.START_OPTION, option);

		option = itr.next();
		assertEquals(GroupSearchOptions.END_OPTION, option);
	}
}


text:
// Copyright 2018 Google LLC
//
// 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
//
//     https://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.

#include "google/cloud/internal/default_completion_queue_impl.h"
#include "google/cloud/internal/throw_delegate.h"
#include "google/cloud/options.h"
#include "absl/memory/memory.h"
#include <grpcpp/alarm.h>
#include <sstream>

// There is no way to unblock the gRPC event loop, not even calling Shutdown(),
// so we periodically wake up from the loop to check if the application has
// shutdown the run.
std::chrono::milliseconds constexpr kLoopTimeout(50);

namespace google {
namespace cloud {
GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN
namespace internal {

namespace {

/**
 * Wrap a gRPC timer into an `AsyncOperation`.
 *
 * Applications (or more likely, other components in the client library) will
 * associate timers with a completion queue. gRPC timers require applications
 * to create a unique `grpc::Alarm` object for each timer, and then to
 * associate them with the completion queue using a `void*` tag.
 *
 * This class collaborates with our wrapper for `CompletionQueue` to associate
 * a `future<AsyncTimerResult>` for each timer. This class takes care of
 * allocating the `grpc::Alarm`, creating a unique `void*` associated with the
 * timer, and satisfying the future when the timer expires.
 *
 * Note that this class is an implementation detail, hidden from the
 * application developers.
 */
class AsyncTimerFuture : public internal::AsyncGrpcOperation {
 public:
  using ValueType = StatusOr<std::chrono::system_clock::time_point>;

  // We need to create the shared_ptr before completing the initialization, so
  // use a factory member function.
  static std::pair<std::shared_ptr<AsyncTimerFuture>, future<ValueType>>
  Create() {
    auto self = std::shared_ptr<AsyncTimerFuture>(new AsyncTimerFuture);
    auto weak = std::weak_ptr<AsyncTimerFuture>(self);
    self->promise_ = promise<AsyncTimerFuture::ValueType>([weak] {
      if (auto self = weak.lock()) self->Cancel();
    });
    return {self, self->promise_.get_future()};
  }

  void Set(grpc::CompletionQueue& cq,
           std::chrono::system_clock::time_point deadline, void* tag) {
    deadline_ = deadline;
    alarm_.Set(&cq, deadline, tag);
  }

  void Cancel() override {
    OptionsSpan span(options_);
    alarm_.Cancel();
  }

 private:
  explicit AsyncTimerFuture() : promise_(null_promise_t{}) {}

  bool Notify(bool ok) override {
    OptionsSpan span(options_);
    promise_.set_value(ok ? ValueType(deadline_) : Canceled());
    return true;
  }

  static ValueType Canceled() {
    return Status{StatusCode::kCancelled, "timer canceled"};
  }

  promise<ValueType> promise_;
  std::chrono::system_clock::time_point deadline_;
  grpc::Alarm alarm_;
  Options options_ = CurrentOptions();
};

}  // namespace

// A helper class to wake up the asynchronous thread and drain the RunAsync()
// queue in a loop.
class DefaultCompletionQueueImpl::WakeUpRunAsyncLoop
    : public internal::AsyncGrpcOperation {
 public:
  explicit WakeUpRunAsyncLoop(std::weak_ptr<DefaultCompletionQueueImpl> w)
      : weak_(std::move(w)) {}

  void Set(grpc::CompletionQueue& cq, void* tag) {
    alarm_.Set(&cq, std::chrono::system_clock::now(), tag);
  }

  void Cancel() override {}

 private:
  bool Notify(bool ok) override {
    OptionsSpan span(options_);
    if (!ok) return true;  // do not run async operations on shutdown CQs
    if (auto self = weak_.lock()) self->DrainRunAsyncLoop();
    return true;
  }

  std::weak_ptr<DefaultCompletionQueueImpl> weak_;
  grpc::Alarm alarm_;
  Options options_ = CurrentOptions();
};

// A helper class to wake up the asynchronous thread and drain the RunAsync()
// one element at a time.
class DefaultCompletionQueueImpl::WakeUpRunAsyncOnIdle
    : public internal::AsyncGrpcOperation {
 public:
  explicit WakeUpRunAsyncOnIdle(std::weak_ptr<DefaultCompletionQueueImpl> w)
      : weak_(std::move(w)) {}

  void Set(grpc::CompletionQueue& cq, void* tag) {
    alarm_.Set(&cq, std::chrono::system_clock::now(), tag);
  }

  void Cancel() override {}

 private:
  bool Notify(bool ok) override {
    OptionsSpan span(options_);
    if (!ok) return true;  // do not run async operations on shutdown CQs
    if (auto self = weak_.lock()) self->DrainRunAsyncOnIdle();
    return true;
  }

  std::weak_ptr<DefaultCompletionQueueImpl> weak_;
  grpc::Alarm alarm_;
  Options options_ = CurrentOptions();
};

DefaultCompletionQueueImpl::DefaultCompletionQueueImpl()
    : shutdown_guard_(
          // Capturing `this` here is safe because the lifetime of copies of
          // this member do not outlive `StartOperation`.
          std::shared_ptr<void>(reinterpret_cast<void*>(this),
                                [this](void*) { cq_.Shutdown(); })) {}

void DefaultCompletionQueueImpl::Run() {
  class ThreadPoolCount {
   public:
    explicit ThreadPoolCount(DefaultCompletionQueueImpl* self) : self_(self) {
      self_->RunStart();
    }
    ~ThreadPoolCount() { self_->RunStop(); }

   private:
    DefaultCompletionQueueImpl* self_;
  } count(this);

  auto deadline = [] {
    return std::chrono::system_clock::now() + kLoopTimeout;
  };

  void* tag;
  bool ok;
  for (auto status = cq_.AsyncNext(&tag, &ok, deadline());
       status != grpc::CompletionQueue::SHUTDOWN;
       status = cq_.AsyncNext(&tag, &ok, deadline())) {
    if (status == grpc::CompletionQueue::TIMEOUT) continue;
    if (status != grpc::CompletionQueue::GOT_EVENT) {
      google::cloud::internal::ThrowRuntimeError(
          "unexpected status from AsyncNext()");
    }
    auto op = FindOperation(tag);
    ++notify_counter_;
    if (op->Notify(ok)) {
      ForgetOperation(tag);
    }
  }
}

void DefaultCompletionQueueImpl::Shutdown() {
  {
    std::lock_guard<std::mutex> lk(mu_);
    shutdown_ = true;
    shutdown_guard_.reset();
  }
}

void DefaultCompletionQueueImpl::CancelAll() {
  // Cancel all operations. We need to make a copy of the operations because
  // canceling them may trigger a recursive call that needs the lock. And we
  // need the lock because canceling might trigger calls that invalidate the
  // iterators.
  auto pending = [this] {
    std::unique_lock<std::mutex> lk(mu_);
    return pending_ops_;
  }();
  for (auto& kv : pending) {
    kv.second->Cancel();
  }
}

future<StatusOr<std::chrono::system_clock::time_point>>
DefaultCompletionQueueImpl::MakeDeadlineTimer(
    std::chrono::system_clock::time_point deadline) {
  auto p = AsyncTimerFuture::Create();
  auto op = std::move(p.first);
  StartOperation(op, [&](void* tag) { op->Set(cq(), deadline, tag); });
  return std::move(p.second);
}

future<StatusOr<std::chrono::system_clock::time_point>>
DefaultCompletionQueueImpl::MakeRelativeTimer(
    std::chrono::nanoseconds duration) {
  using std::chrono::system_clock;
  auto const d = std::chrono::duration_cast<system_clock::duration>(duration);
  return MakeDeadlineTimer(system_clock::now() + d);
}

void DefaultCompletionQueueImpl::RunAsync(
    std::unique_ptr<internal::RunAsyncBase> function) {
  std::unique_lock<std::mutex> lk(mu_);
  run_async_queue_.push_back(std::move(function));
  WakeUpRunAsyncThread(std::move(lk));
}

void DefaultCompletionQueueImpl::StartOperation(
    std::shared_ptr<AsyncGrpcOperation> op,
    absl::FunctionRef<void(void*)> start) {
  StartOperation(std::unique_lock<std::mutex>(mu_), std::move(op),
                 std::move(start));
}

grpc::CompletionQueue& DefaultCompletionQueueImpl::cq() { return cq_; }

void DefaultCompletionQueueImpl::StartOperation(
    std::unique_lock<std::mutex> lk, std::shared_ptr<AsyncGrpcOperation> op,
    absl::FunctionRef<void(void*)> start) {
  void* tag = op.get();
  if (shutdown_) {
    lk.unlock();
    op->Notify(/*ok=*/false);
    return;
  }
  auto ins = pending_ops_.emplace(tag, std::move(op));
  if (ins.second) {
    // Do not start the operation with the `CompletionQueue`'s lock held. This
    // may trigger a deadlock if that operation schedules some more work on the
    // completion queue (e.g. a timer). We need to delay the underlying
    // grpc::CompletionQueue::Shutdown until `start` finishes because gRPC's
    // reaction to trying to schedule some operation on a shut down completion
    // queue is an assertion.
    auto shutdown_guard = shutdown_guard_;
    lk.unlock();

    start(tag);
    return;
  }
  std::ostringstream os;
  os << "assertion failure: duplicate operation tag (" << tag << "),"
     << " asynchronous operations should complete before they are rescheduled."
     << " This might be a bug in the library, please report it at"
     << " https://github.com/google-cloud-cpp/issues";
  google::cloud::internal::ThrowRuntimeError(std::move(os).str());
}

std::shared_ptr<AsyncGrpcOperation> DefaultCompletionQueueImpl::FindOperation(
    void* tag) {
  std::lock_guard<std::mutex> lk(mu_);
  auto loc = pending_ops_.find(tag);
  if (pending_ops_.end() == loc) {
    google::cloud::internal::ThrowRuntimeError(
        "assertion failure: searching for async op tag");
  }
  return loc->second;
}

void DefaultCompletionQueueImpl::ForgetOperation(void* tag) {
  std::lock_guard<std::mutex> lk(mu_);
  auto const num_erased = pending_ops_.erase(tag);
  if (num_erased != 1) {
    google::cloud::internal::ThrowRuntimeError(
        "assertion failure: searching for async op tag when trying to "
        "unregister");
  }
}

void DefaultCompletionQueueImpl::DrainRunAsyncLoop() {
  std::unique_lock<std::mutex> lk(mu_);
  while (!run_async_queue_.empty() && !shutdown_) {
    auto f = std::move(run_async_queue_.front());
    run_async_queue_.pop_front();
    lk.unlock();
    f->exec();
    lk.lock();
  }
  --run_async_pool_size_;
}

void DefaultCompletionQueueImpl::DrainRunAsyncOnIdle() {
  std::unique_lock<std::mutex> lk(mu_);
  if (run_async_queue_.empty()) return;
  auto f = std::move(run_async_queue_.front());
  run_async_queue_.pop_front();
  lk.unlock();
  f->exec();
  lk.lock();
  if (run_async_queue_.empty()) {
    --run_async_pool_size_;
    return;
  }
  auto op = std::make_shared<WakeUpRunAsyncOnIdle>(shared_from_this());
  StartOperation(std::move(lk), op, [&](void* tag) { op->Set(cq(), tag); });
}

void DefaultCompletionQueueImpl::WakeUpRunAsyncThread(
    std::unique_lock<std::mutex> lk) {
  if (run_async_queue_.empty() || shutdown_) return;
  if (thread_pool_size_ <= 1) {
    if (run_async_pool_size_ > 0) return;
    ++run_async_pool_size_;
    run_async_pool_hwm_ = (std::max)(run_async_pool_hwm_, run_async_pool_size_);
    auto op = std::make_shared<WakeUpRunAsyncOnIdle>(shared_from_this());
    StartOperation(std::move(lk), op, [&](void* tag) { op->Set(cq(), tag); });
    return;
  }
  // Always leave one thread for I/O
  if (run_async_pool_size_ >= thread_pool_size_ - 1) return;
  auto op = std::make_shared<WakeUpRunAsyncLoop>(shared_from_this());
  ++run_async_pool_size_;
  run_async_pool_hwm_ = (std::max)(run_async_pool_hwm_, run_async_pool_size_);
  StartOperation(std::move(lk), op, [&](void* tag) { op->Set(cq(), tag); });
}

}  // namespace internal
GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END
}  // namespace cloud
}  // namespace google


text:
package stomp

import (
	"context"

	"github.com/micro/go-micro/v2/broker"
)

// Context related keys and funcs
type authKey struct{}
type connectHeaderKey struct{}
type connectTimeoutKey struct{}
type durableQueueKey struct{}
type receiptKey struct{}
type subscribeHeaderKey struct{}
type suppressContentLengthKey struct{}
type vHostKey struct{}

type authRecord struct {
	username string
	password string
}

// setSubscribeOption returns a function to setup a context with given value
func setSubscribeOption(k, v interface{}) broker.SubscribeOption {
	return func(o *broker.SubscribeOptions) {
		if o.Context == nil {
			o.Context = context.Background()
		}
		o.Context = context.WithValue(o.Context, k, v)
	}
}

// setBrokerOption returns a function to setup a context with given value
func setBrokerOption(k, v interface{}) broker.Option {
	return func(o *broker.Options) {
		if o.Context == nil {
			o.Context = context.Background()
		}
		o.Context = context.WithValue(o.Context, k, v)
	}
}

// setPublishOption returns a function to setup a context with given value
func setPublishOption(k, v interface{}) broker.PublishOption {
	return func(o *broker.PublishOptions) {
		if o.Context == nil {
			o.Context = context.Background()
		}
		o.Context = context.WithValue(o.Context, k, v)
	}
}


text:
package binlog

import (
	"encoding/json"
	"flag"
	"fmt"
	"os"
	"os/exec"
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"

	"vitess.io/vitess/go/sqltypes"
	"vitess.io/vitess/go/test/endtoend/cluster"
	"vitess.io/vitess/go/test/endtoend/sharding"
	"vitess.io/vitess/go/vt/proto/topodata"
)

var (
	localCluster *cluster.LocalProcessCluster
	cell1        = "zone1"
	cell2        = "zone2"
	hostname     = "localhost"
	keyspaceName = "ks"
	tableName    = "test_table"
	sqlSchema    = `
					create table %s(
					id bigint(20) unsigned auto_increment,
					msg varchar(64),
					primary key (id),
					index by_msg (msg)
					) Engine=InnoDB
`
	commonTabletArg = []string{
		"-vreplication_healthcheck_topology_refresh", "1s",
		"-vreplication_healthcheck_retry_delay", "1s",
		"-vreplication_retry_delay", "1s",
		"-degraded_threshold", "5s",
		"-lock_tables_timeout", "5s",
		"-watch_replication_stream",
		"-enable_replication_reporter",
		"-serving_state_grace_period", "1s",
		"-binlog_player_protocol", "grpc",
		"-enable-autocommit",
	}
	vSchema = `
		{
		  "sharded": true,
		  "vindexes": {
			"hash_index": {
			  "type": "hash"
			}
		  },
		  "tables": {
			"%s": {
			   "column_vindexes": [
				{
				  "column": "id",
				  "name": "hash_index"
				}
			  ]
			}
		  }
		}
`
	shard1Master  *cluster.Vttablet
	shard1Replica *cluster.Vttablet
	shard1Rdonly  *cluster.Vttablet
	shard2Master  *cluster.Vttablet
	shard2Replica *cluster.Vttablet
	shard2Rdonly  *cluster.Vttablet
)

func TestMain(m *testing.M) {
	defer cluster.PanicHandler(nil)
	flag.Parse()

	exitcode, err := func() (int, error) {
		localCluster = cluster.NewCluster(cell1, hostname)
		defer localCluster.Teardown()
		localCluster.Keyspaces = append(localCluster.Keyspaces, cluster.Keyspace{
			Name: keyspaceName,
		})

		// Start topo server
		if err := localCluster.StartTopo(); err != nil {
			return 1, err
		}

		// Adding another cell in the same cluster
		err := localCluster.TopoProcess.ManageTopoDir("mkdir", "/vitess/"+cell2)
		if err != nil {
			return 1, err
		}
		err = localCluster.VtctlProcess.AddCellInfo(cell2)
		if err != nil {
			return 1, err
		}

		shard1Master = localCluster.NewVttabletInstance("master", 0, cell1)
		shard1Replica = localCluster.NewVttabletInstance("replica", 0, cell2)
		shard1Rdonly = localCluster.NewVttabletInstance("rdonly", 0, cell2)

		shard2Master = localCluster.NewVttabletInstance("master", 0, cell1)
		shard2Replica = localCluster.NewVttabletInstance("replica", 0, cell2)
		shard2Rdonly = localCluster.NewVttabletInstance("rdonly", 0, cell2)

		var mysqlProcs []*exec.Cmd
		for _, tablet := range []*cluster.Vttablet{shard1Master, shard1Replica, shard1Rdonly, shard2Master, shard2Replica, shard2Rdonly} {
			tablet.MysqlctlProcess = *cluster.MysqlCtlProcessInstance(tablet.TabletUID, tablet.MySQLPort, localCluster.TmpDirectory)
			tablet.VttabletProcess = cluster.VttabletProcessInstance(tablet.HTTPPort,
				tablet.GrpcPort,
				tablet.TabletUID,
				tablet.Cell,
				"",
				keyspaceName,
				localCluster.VtctldProcess.Port,
				tablet.Type,
				localCluster.TopoPort,
				hostname,
				localCluster.TmpDirectory,
				commonTabletArg,
				true,
			)
			tablet.VttabletProcess.SupportsBackup = true
			proc, err := tablet.MysqlctlProcess.StartProcess()
			if err != nil {
				return 1, err
			}
			mysqlProcs = append(mysqlProcs, proc)
		}
		for _, proc := range mysqlProcs {
			if err := proc.Wait(); err != nil {
				return 1, err
			}
		}

		if err := localCluster.VtctlProcess.CreateKeyspace(keyspaceName); err != nil {
			return 1, err
		}

		shard1 := cluster.Shard{
			Name:      "-80",
			Vttablets: []*cluster.Vttablet{shard1Master, shard1Replica, shard1Rdonly},
		}
		for idx := range shard1.Vttablets {
			shard1.Vttablets[idx].VttabletProcess.Shard = shard1.Name
		}
		localCluster.Keyspaces[0].Shards = append(localCluster.Keyspaces[0].Shards, shard1)

		shard2 := cluster.Shard{
			Name:      "80-",
			Vttablets: []*cluster.Vttablet{shard2Master, shard2Replica, shard2Rdonly},
		}
		for idx := range shard2.Vttablets {
			shard2.Vttablets[idx].VttabletProcess.Shard = shard2.Name
		}
		localCluster.Keyspaces[0].Shards = append(localCluster.Keyspaces[0].Shards, shard2)

		for _, tablet := range shard1.Vttablets {
			if err := tablet.VttabletProcess.Setup(); err != nil {
				return 1, err
			}
		}
		if err := localCluster.VtctlclientProcess.InitShardMaster(keyspaceName, shard1.Name, shard1Master.Cell, shard1Master.TabletUID); err != nil {
			return 1, err
		}

		// run a health check on source replica so it responds to discovery
		// (for binlog players) and on the source rdonlys (for workers)
		for _, tablet := range []string{shard1Replica.Alias, shard1Rdonly.Alias} {
			if err := localCluster.VtctlclientProcess.ExecuteCommand("RunHealthCheck", tablet); err != nil {
				return 1, err
			}
		}

		for _, tablet := range shard2.Vttablets {
			if err := tablet.VttabletProcess.Setup(); err != nil {
				return 1, err
			}
		}

		if err := localCluster.VtctlclientProcess.InitShardMaster(keyspaceName, shard2.Name, shard2Master.Cell, shard2Master.TabletUID); err != nil {
			return 1, err
		}

		if err := localCluster.VtctlclientProcess.ApplySchema(keyspaceName, fmt.Sprintf(sqlSchema, tableName)); err != nil {
			return 1, err
		}
		if err := localCluster.VtctlclientProcess.ApplyVSchema(keyspaceName, fmt.Sprintf(vSchema, tableName)); err != nil {
			return 1, err
		}

		_ = localCluster.VtctlclientProcess.ExecuteCommand("RebuildKeyspaceGraph", keyspaceName)

		return m.Run(), nil
	}()
	if err != nil {
		fmt.Printf("%v\n", err)
		os.Exit(1)
	} else {
		os.Exit(exitcode)
	}
}

func TestAlias(t *testing.T) {
	defer cluster.PanicHandler(t)

	insertInitialValues(t)
	defer deleteInitialValues(t)

	err := localCluster.VtctlclientProcess.ExecuteCommand("RebuildKeyspaceGraph", keyspaceName)
	require.NoError(t, err)
	shard1 := localCluster.Keyspaces[0].Shards[0]
	shard2 := localCluster.Keyspaces[0].Shards[1]
	allCells := fmt.Sprintf("%s,%s", cell1, cell2)

	expectedPartitions := map[topodata.TabletType][]string{}
	expectedPartitions[topodata.TabletType_MASTER] = []string{shard1.Name, shard2.Name}
	expectedPartitions[topodata.TabletType_REPLICA] = []string{shard1.Name, shard2.Name}
	expectedPartitions[topodata.TabletType_RDONLY] = []string{shard1.Name, shard2.Name}
	sharding.CheckSrvKeyspace(t, cell1, keyspaceName, "", 0, expectedPartitions, *localCluster)
	sharding.CheckSrvKeyspace(t, cell2, keyspaceName, "", 0, expectedPartitions, *localCluster)

	// Adds alias so vtgate can route to replica/rdonly tablets that are not in the same cell, but same alias
	err = localCluster.VtctlclientProcess.ExecuteCommand("AddCellsAlias",
		"-cells", allCells,
		"region_east_coast")
	require.NoError(t, err)
	err = localCluster.VtctlclientProcess.ExecuteCommand("UpdateCellsAlias",
		"-cells", allCells,
		"region_east_coast")
	require.NoError(t, err)

	vtgateInstance := localCluster.NewVtgateInstance()
	vtgateInstance.CellsToWatch = allCells
	vtgateInstance.TabletTypesToWait = "MASTER,REPLICA"
	// Use legacy gateway. There's a separate test for tabletgateway in go/test/endtoend/tabletgateway/cellalias/cell_alias_test.go
	vtgateInstance.GatewayImplementation = "discoverygateway"
	err = vtgateInstance.Setup()
	require.NoError(t, err)

	// Cluster teardown will not teardown vtgate because we are not
	// actually setting this on localCluster.VtgateInstance
	defer vtgateInstance.TearDown()

	waitTillAllTabletsAreHealthyInVtgate(t, *vtgateInstance, shard1.Name, shard2.Name)

	testQueriesOnTabletType(t, "master", vtgateInstance.GrpcPort, false)
	testQueriesOnTabletType(t, "replica", vtgateInstance.GrpcPort, false)
	testQueriesOnTabletType(t, "rdonly", vtgateInstance.GrpcPort, false)

	// now, delete the alias, so that if we run above assertions again, it will fail for replica,rdonly target type
	err = localCluster.VtctlclientProcess.ExecuteCommand("DeleteCellsAlias",
		"region_east_coast")
	require.NoError(t, err)

	// restarts the vtgate process
	vtgateInstance.TabletTypesToWait = "MASTER"
	err = vtgateInstance.TearDown()
	require.NoError(t, err)
	err = vtgateInstance.Setup()
	require.NoError(t, err)

	// since replica and rdonly tablets of all shards in cell2, the last 2 assertion is expected to fail
	testQueriesOnTabletType(t, "master", vtgateInstance.GrpcPort, false)
	testQueriesOnTabletType(t, "replica", vtgateInstance.GrpcPort, true)
	testQueriesOnTabletType(t, "rdonly", vtgateInstance.GrpcPort, true)

}

func TestAddAliasWhileVtgateUp(t *testing.T) {
	defer cluster.PanicHandler(t)

	insertInitialValues(t)
	defer deleteInitialValues(t)

	err := localCluster.VtctlclientProcess.ExecuteCommand("RebuildKeyspaceGraph", keyspaceName)
	require.NoError(t, err)
	shard1 := localCluster.Keyspaces[0].Shards[0]
	shard2 := localCluster.Keyspaces[0].Shards[1]
	allCells := fmt.Sprintf("%s,%s", cell1, cell2)

	expectedPartitions := map[topodata.TabletType][]string{}
	expectedPartitions[topodata.TabletType_MASTER] = []string{shard1.Name, shard2.Name}
	expectedPartitions[topodata.TabletType_REPLICA] = []string{shard1.Name, shard2.Name}
	expectedPartitions[topodata.TabletType_RDONLY] = []string{shard1.Name, shard2.Name}
	sharding.CheckSrvKeyspace(t, cell1, keyspaceName, "", 0, expectedPartitions, *localCluster)
	sharding.CheckSrvKeyspace(t, cell2, keyspaceName, "", 0, expectedPartitions, *localCluster)

	vtgateInstance := localCluster.NewVtgateInstance()
	vtgateInstance.CellsToWatch = allCells
	// only MASTER is in vtgate's "cell", other tablet types are not visible because they are in the other cell
	vtgateInstance.TabletTypesToWait = "MASTER"
	err = vtgateInstance.Setup()
	require.NoError(t, err)
	defer vtgateInstance.TearDown()

	// since replica and rdonly tablets of all shards in cell2, the last 2 assertion is expected to fail
	testQueriesOnTabletType(t, "master", vtgateInstance.GrpcPort, false)
	testQueriesOnTabletType(t, "replica", vtgateInstance.GrpcPort, true)
	testQueriesOnTabletType(t, "rdonly", vtgateInstance.GrpcPort, true)

	// Adds alias so vtgate can route to replica/rdonly tablets that are not in the same cell, but same alias
	err = localCluster.VtctlclientProcess.ExecuteCommand("AddCellsAlias",
		"-cells", allCells,
		"region_east_coast")
	require.NoError(t, err)

	testQueriesOnTabletType(t, "master", vtgateInstance.GrpcPort, false)
	// TODO(deepthi) change the following to shouldFail:false when fixing https://github.com/vitessio/vitess/issues/5911
	testQueriesOnTabletType(t, "replica", vtgateInstance.GrpcPort, true)
	testQueriesOnTabletType(t, "rdonly", vtgateInstance.GrpcPort, true)

}

func waitTillAllTabletsAreHealthyInVtgate(t *testing.T, vtgateInstance cluster.VtgateProcess, shards ...string) {
	for _, shard := range shards {
		err := vtgateInstance.WaitForStatusOfTabletInShard(fmt.Sprintf("%s.%s.master", keyspaceName, shard), 1)
		require.NoError(t, err)
		err = vtgateInstance.WaitForStatusOfTabletInShard(fmt.Sprintf("%s.%s.replica", keyspaceName, shard), 1)
		require.NoError(t, err)
		err = vtgateInstance.WaitForStatusOfTabletInShard(fmt.Sprintf("%s.%s.rdonly", keyspaceName, shard), 1)
		require.NoError(t, err)
	}
}

func testQueriesOnTabletType(t *testing.T, tabletType string, vtgateGrpcPort int, shouldFail bool) {
	output, err := localCluster.VtctlProcess.ExecuteCommandWithOutput("VtGateExecute", "-json",
		"-server", fmt.Sprintf("%s:%d", localCluster.Hostname, vtgateGrpcPort),
		"-target", "@"+tabletType,
		fmt.Sprintf(`select * from %s`, tableName))
	if shouldFail {
		require.Error(t, err)
		return
	}
	require.NoError(t, err)
	var result sqltypes.Result

	err = json.Unmarshal([]byte(output), &result)
	require.NoError(t, err)
	assert.Equal(t, len(result.Rows), 3)
}

func insertInitialValues(t *testing.T) {
	sharding.ExecuteOnTablet(t,
		fmt.Sprintf(sharding.InsertTabletTemplateKsID, tableName, 1, "msg1", 1),
		*shard1Master,
		keyspaceName,
		false)

	sharding.ExecuteOnTablet(t,
		fmt.Sprintf(sharding.InsertTabletTemplateKsID, tableName, 2, "msg2", 2),
		*shard1Master,
		keyspaceName,
		false)

	sharding.ExecuteOnTablet(t,
		fmt.Sprintf(sharding.InsertTabletTemplateKsID, tableName, 4, "msg4", 4),
		*shard2Master,
		keyspaceName,
		false)
}

func deleteInitialValues(t *testing.T) {
	sharding.ExecuteOnTablet(t,
		fmt.Sprintf("delete from %s where id = %v", tableName, 1),
		*shard1Master,
		keyspaceName,
		false)

	sharding.ExecuteOnTablet(t,
		fmt.Sprintf("delete from %s where id = %v", tableName, 2),
		*shard1Master,
		keyspaceName,
		false)

	sharding.ExecuteOnTablet(t,
		fmt.Sprintf("delete from %s where id = %v", tableName, 4),
		*shard2Master,
		keyspaceName,
		false)
}


text:
/**
 * @file Contain the description of the next classes:
 *

 *
[TabPane]{@link TabPane}

 *

 *
 * @author Pavel Dubenko
 *
 * @version 1.0.0
 */

/**
 * Abstract tab panel.
 *
 * @constructor
 * @param {Object} [options] Set of events. This class listens 'tabChange'-event.
 */
var TabPane = new Class(/** @lends TabPane# */{
    Implements: [Options, Events],

    Static: {
        count: 1,
        assignID: function() {
            return this.count++;
        }
    },

    // constructor
    initialize: function (element, options) {
        Asset.css('tabpane.css');
        this.setOptions(options);

        /**
         * The main holder element.
         * @type {Element}
         */
        this.element = $(element);

        /**
         * Array of tabs.
         * @type {Elements}
         */
        this.tabs = this.element.getElement('ul.e-tabs').addClass('clearfix').getElements('li');

        /**
         * Current viewed tab.
         * @type {Element}
         */
        this.currentTab = this.tabs[0];
        this.element.addClass('e-items-count-' + this.tabs.length )
        this.tabs.each(function (tab) {
            tab.setProperty('unselectable', 'on');
            var anchor = tab.getElement('a');
            var paneId = anchor.getProperty('href').slice(anchor.getProperty('href').lastIndexOf('#'));
            anchor.addEvent('click', function (event) {
                event.preventDefault();
                //tab.blur();
            });

            var tabData = tab.getElement('span.data');
            tab.data = (tabData ? JSON.decode(tabData.firstChild.nodeValue, false) : {});
            tab.pane = this.element.getElement('div' + paneId).addClass('e-pane-item').setStyle('display', 'none');
            tab.pane.tab = tab;

            var tabpane = this;
            tab.addEvents({
                'mouseover': function () {
                    if (this != tabpane.currentTab) {
                        this.addClass('highlighted');
                    }
                },
                'mouseout': function () {
                    this.removeClass('highlighted');
                },
                'click': function () {
                    if ((this != tabpane.currentTab) && !this.hasClass('disabled')) {
                        tabpane.show(this);
                    }
                }
            });
        }, this);

        this.selectTab(this.currentTab);
    },

    /**
     * Show the specific tab.
     *
     * @fires TabPane#tabChange
     *
     * @function
     * @public
     * @param {Element} tab Tab that will be viewed.
     */
    show: function (tab) {
        this.selectTab(tab);
        /**
         * Changing the tab panel.
         * @event TabPane#tabChange
         * @param {Object} Object with language ID.
         */
        this.fireEvent('tabChange', this.currentTab.data);
    },

    /**
     * Select the tab.
     *
     * @function
     * @public
     * @param {Element} tab Tab that will be selected.
     */
    selectTab: function (tab) {
        if(!tab) return;
        this.currentTab.removeClass('current').pane.setStyle('display', 'none');
        tab.addClass('current').pane.setStyle('display', '');
        this.currentTab = tab;

        var firstInput = this.currentTab.pane.getElement('div.field div.control input[type=text]')
            || this.currentTab.pane.getElement('div.field div.control textarea');
        if (firstInput) {
            // TODO Check if this is not DatePicker.
            firstInput.focus();
        }
    },

    /**
     * Get the all [tabs]{@link TabPane#tabs}.
     *
     * @function
     * @public
     * @returns {Elements}
     */
    getTabs: function () {
        return this.tabs;
    },

    /**
     * Get the [current viewed tab]{@link TabPane#currentTab}.
     *
     * @function
     * @public
     * @returns {Element}
     */
    getCurrentTab: function () {
        return this.currentTab;
    },

    /**
     * Set the title of tab.
     *
     * @function
     * @public
     * @param {string} title The title.
     * @param {Element} tab The tab from the [tabs]{@link TabPane#tabs}.
     */
    setTabTitle: function (title, tab) {
        tab = Array.pick([tab, this.getCurrentTab()]);
        tab.getElement('a').set('html', title);
    },

    /**
     * Create the new tab.
     *
     * @function
     * @public
     * @param {string} tabTitle The title.
     * @returns {Element} New tab.
     */
    createNewTab: function (tabTitle) {
        var tabID = 'id' + TabPane.assignID(),
            titleElement = new Element('a', {'href': '#' + tabID, 'html': tabTitle}),
            tabPane = new Element('div', {'id': tabID, 'class': 'e-pane-item', 'styles': {'display': 'none'}}).inject(this.element.getElement('.e-pane-content')),
            tabElement = new Element('li', {'unselectable': 'on'}).grab(titleElement);

        this.element.getElement('ul.e-tabs').grab(tabElement);
        this.tabs.push(tabElement);

        titleElement.addEvent('click', function (event) {
            event.preventDefault();
            tabElement.blur();
        });
        tabElement.pane = tabPane;
        tabElement.pane.tab = tabElement;

        var tabpane = this;
        tabElement.addEvents({
            'mouseover': function () {
                if (this != tabpane.currentTab) {
                    this.addClass('highlighted');
                }
            },
            'mouseout': function () {
                this.removeClass('highlighted');
            },
            'click': function () {
                if (this != tabpane.currentTab) {
                    tabpane.show(this);
                }
            }
        });
        return tabElement;
    },

    /**
     * Find the tab.
     *
     * @function
     * @public
     * @param {Element} element
     * @returns {Element}
     */
    whereIs: function (element) {
        var el = $(element),
            pane = null;
        while (el = el.getParent()) {
            if (el.hasClass('e-pane-item') && el.tab) {
                pane = el.tab;
                break;
            }
        }
        return pane;
    },

    /**
     * Enable the tab by his index.
     *
     * @function
     * @public
     * @param {number} tabIndex The index of the tab.
     */
    enableTab: function (tabIndex) {
        if (this.tabs[tabIndex]) {
            this.tabs[tabIndex].removeClass('disabled');
        }
    },

    /**
     * Disable the tab by his index.
     *
     * @function
     * @public
     * @param {number} tabIndex The index of the tab.
     */
    disableTab: function (tabIndex) {
        if (this.tabs[tabIndex]) {
            this.tabs[tabIndex].addClass('disabled');
        }
    }
});


text:
  {{message.content}}

      <button type="button" class="btn btn-danger" (click)="onDeleteStudent()">{{message.button}}</button>
      <button type="button" class="btn btn-secondary" (click)="display = false">ยกเลิก</button>

  <my-course-nav (webboard)="gotoWebboard()" (feedback)="giveAllFeedback()" (leaderboard)="viewLeaderboard()"></my-course-nav>

           ย้อนกลับ

               แก้ไขข้อมูล

แก้ไขข้อมูลชั้นเรียน

แก้ไข  XP เริ่มต้นและการเลื่อนขั้น

แก้ไขข้อมูลเหรียญรางวัล

แก้ไข XP ของนักเรียน

พิมพ์บัตรเข้าระบบของนักเรียน

 {{course.name}}

        <form class="form-inline">

              <input class="form-check-input" type="checkbox" value="checkAll"
                     [(ngModel)]="checkAll" name="checkAll"> เลือกทั้งหมด

            <input type="text" class="form-control form-control-sm" id="Search" placeholder="ค้นหา..." #term
                   (keyup)="search(term.value)">

            <select class="form-control form-control-sm" [(ngModel)]="filterKeys" name="filterKeys">
              <option value="name">ชื่อนักเรียน</option>
              <option value="student_id">รหัสนักเรียน</option>
            </select>

            เรียงลำดับข้อมูลตาม
            <select class="form-control form-control-sm" name="orderKeys" (change)="orderBy($event.target.value)">
              <option value="student_id">รหัสนักเรียน</option>
              <option value="name">ชื่อนักเรียน</option>
              <option value="xp">ค่า XP</option>
            </select>

        </form>

        <form class="form-inline">

            <button class="btn btn-danger" type="button" (click)="deletePopup()"> ลบนักเรียน</button>
            <button class="btn btn-green" type="button" (click)="updateStudent()"> เพิ่มนักเรียน</button>

        </form>

Level {{student.level}}

Name: {{student.name}}

ID: {{student.student_id}}

                      {{student.overall_xp}} / {{student.maxXP}} XP

                  <input class="form-check-input" type="checkbox" [(ngModel)]="student.selected"
                         name="student.name"> เลือก

                ข้อมูลเพิ่มเติม

        <button type="button" class="close" data-dismiss="modal" aria-label="Close">
          ×
        </button>

มอบ {{feedbackState}} ให้กับ {{student.name}} {{student.student_id}} 1 && !last">,

            <button type="button" class="btn {{ feedbackState == 'XP' ? 'btn-info': 'btn-secondary'}} link"
                    style="min-width: 100px" (click)="chageFeedbackState('XP')">XP
            </button>
            <button type="button" class="btn {{ feedbackState == 'Badge' ? 'btn-info': 'btn-secondary'}} link"
                    style="min-width: 100px" (click)="chageFeedbackState('Badge')">Badge
            </button>

{{badge.name}}

                +{{badge.xp}} XP

                  + {{xp}} XP

            <form class="form-inline text-center" style="margin-right: 5px; margin-left: 5px">

กำหนดค่า XP

                <input type="number" class="form-control form-control-sm" name="dynamicXp" [(ngModel)]="dynamicXp">

              <button type="submit" class="btn btn-info btn-sm" (click)="onUpdateStudentScore(dynamicXp)">ตกลง</button>
            </form>

              กรุณาใส่ข้อมูลตัวเลขที่มีค่ามากกว่า 0 ขึ้นไป

        <button type="button" id="closeUpdateModal" class="btn btn-secondary" data-dismiss="modal" (click)="resetXp()" >ปิด</button>

        <button type="button" class="close" data-dismiss="modal" aria-label="Close">
          ×
        </button>

          <form class="form-inline text-center" style="margin-right: 15px; margin-left: 15px; margin-bottom: 0">

แสดงข้อมูลเฉพาะ

              <select class="form-control form-control-sm" (change)="selectHighScore($event.target.value)">
                <option value="{{defaultHighScore}}">{{defaultHighScore}}</option>
                <option value="10">10</option>
                <option value="20">20</option>
                <option value="30">15</option>
                <option value="40">40</option>
                <option value="50">50</option>
              </select>
              {{showHighScore}}

            อันดับแรก
          </form>

                <div *ngIf="index < showHighScore">

{{index + 1}}

{{student.student_id}} {{student.name}}

{{student.overall_xp}} XP

Level {{student.level}}

        <button type="button" class="btn btn-secondary" data-dismiss="modal">ปิด</button>


text:
from __future__ import unicode_literals

from django.contrib import admin
from models import ImgPost
# Register your models here.
admin.site.register(ImgPost)