root/chrome/browser/ui/gtk/gtk_chrome_shrinkable_hbox_unittest.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. box_
  2. AddChildren
  3. Validate
  4. Test
  5. CollectChildData
  6. TEST_F
  7. TEST_F
  8. TEST_F

// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/ui/gtk/gtk_chrome_shrinkable_hbox.h"

#include <vector>

#include "testing/gtest/include/gtest/gtest.h"

namespace {

const int kSpacing = 3;
const int kBorderWidth = 5;

}  // namespace

class GtkChromeShrinkableHBoxTest : public testing::Test {
 protected:
  GtkChromeShrinkableHBoxTest()
      : window_(gtk_window_new(GTK_WINDOW_TOPLEVEL)),
        box_(gtk_chrome_shrinkable_hbox_new(FALSE, FALSE, kSpacing)) {
    gtk_widget_set_direction(box_, GTK_TEXT_DIR_LTR);
    gtk_window_set_default_size(GTK_WINDOW(window_), 200, 200);
    gtk_container_add(GTK_CONTAINER(window_), box_);
    gtk_container_set_border_width(GTK_CONTAINER(box_), kBorderWidth);
  }

  virtual ~GtkChromeShrinkableHBoxTest() {
    gtk_widget_destroy(window_);
  }

  // Add some children widgets with arbitrary width and padding.
  void AddChildren(bool pack_start) {
    static struct {
      int width;
      int padding;
    } kChildrenData[] = {
      { 60, 2 },
      { 70, 3 },
      { 80, 5 },
      { 50, 7 },
      { 40, 11 },
      { 60, 0 },
      { 0, 0 }
    };

    for (size_t i = 0; kChildrenData[i].width; ++i) {
      GtkWidget* child = gtk_fixed_new();
      gtk_widget_set_size_request(child, kChildrenData[i].width, -1);
      if (pack_start) {
        gtk_chrome_shrinkable_hbox_pack_start(
            GTK_CHROME_SHRINKABLE_HBOX(box_), child, kChildrenData[i].padding);
      } else {
        gtk_chrome_shrinkable_hbox_pack_end(
            GTK_CHROME_SHRINKABLE_HBOX(box_), child, kChildrenData[i].padding);
      }
    }
  }

  // Check if all children's size allocation are inside the |box_|'s boundary.
  void Validate(bool pack_start) {
    std::vector<ChildData> children_data;
    gtk_container_foreach(GTK_CONTAINER(box_), CollectChildData,
                          &children_data);

    size_t children_count = children_data.size();
    size_t visible_children_count = 0;
    for (size_t i = 0; i < children_count; ++i) {
      if (children_data[i].visible)
        ++visible_children_count;
    }

    if (visible_children_count == 0)
      return;

    int border_width = gtk_container_get_border_width(GTK_CONTAINER(box_));
    int x = box_->allocation.x + border_width;
    int width = box_->allocation.width - border_width * 2;
    int spacing = gtk_box_get_spacing(GTK_BOX(box_));
    bool homogeneous = gtk_box_get_homogeneous(GTK_BOX(box_));

    if (homogeneous) {
      // If the |box_| is in homogeneous mode, then check if the visible
      // children are not overlapped with each other.
      int homogeneous_child_width =
          (width - (visible_children_count - 1) * spacing) /
          visible_children_count;

      for (size_t i = 0; i < children_count; ++i) {
        SCOPED_TRACE(testing::Message() << "Validate homogeneous child " << i
                     << " visible: " << children_data[i].visible
                     << " padding: " << children_data[i].padding
                     << " x: " << children_data[i].x
                     << " width: " << children_data[i].width);

        if (children_data[i].visible)
          ASSERT_LE(children_data[i].width, homogeneous_child_width);
      }
    } else {
      // If the |box_| is not in homogeneous mode, then just check if all
      // visible children are inside the |box_|'s boundary. And for those
      // hidden children which are out of the boundary, they should only
      // be hidden one by one from the end of the |box_|.
      bool last_visible = pack_start;
      bool visibility_changed = false;
      for (size_t i = 0; i < children_count; ++i) {
        SCOPED_TRACE(testing::Message() << "Validate child " << i
                     << " visible: " << children_data[i].visible
                     << " padding: " << children_data[i].padding
                     << " x: " << children_data[i].x
                     << " width: " << children_data[i].width);

        if (last_visible != children_data[i].visible) {
          ASSERT_FALSE(visibility_changed);
          visibility_changed = true;
          last_visible = children_data[i].visible;
        }
        if (children_data[i].visible) {
          ASSERT_GE(children_data[i].x,
                    x + children_data[i].padding);
          ASSERT_LE(children_data[i].x + children_data[i].width,
                    x + width - children_data[i].padding);
        }
      }
    }
  }

  void Test(bool pack_start) {
    gtk_widget_show_all(window_);
    GtkAllocation allocation = { 0, 0, 0, 200 };
    gtk_chrome_shrinkable_hbox_set_hide_child_directly(
        GTK_CHROME_SHRINKABLE_HBOX(box_), FALSE);
    for (int width = 500; width > kBorderWidth * 2; --width) {
      SCOPED_TRACE(testing::Message() << "Shrink hide_child_directly = FALSE,"
                   << " width = " << width);

      allocation.width = width;
      // Reducing the width may cause some children to be hidden, which will
      // cause queue resize, so it's necessary to do another size allocation to
      // emulate the queue resize.
      gtk_widget_size_allocate(box_, &allocation);
      gtk_widget_size_allocate(box_, &allocation);
      ASSERT_NO_FATAL_FAILURE(Validate(pack_start)) << "width = " << width;
    }

    for (int width = kBorderWidth * 2; width <= 500; ++width) {
      SCOPED_TRACE(testing::Message() << "Expand hide_child_directly = FALSE,"
                   << " width = " << width);

      allocation.width = width;
      // Expanding the width may cause some invisible children to be shown,
      // which will cause queue resize, so it's necessary to do another size
      // allocation to emulate the queue resize.
      gtk_widget_size_allocate(box_, &allocation);
      gtk_widget_size_allocate(box_, &allocation);
      ASSERT_NO_FATAL_FAILURE(Validate(pack_start));
    }

    gtk_chrome_shrinkable_hbox_set_hide_child_directly(
        GTK_CHROME_SHRINKABLE_HBOX(box_), TRUE);
    for (int width = 500; width > kBorderWidth * 2; --width) {
      SCOPED_TRACE(testing::Message() << "Shrink hide_child_directly = TRUE,"
                   << " width = " << width);

      allocation.width = width;
      gtk_widget_size_allocate(box_, &allocation);
      gtk_widget_size_allocate(box_, &allocation);
      ASSERT_NO_FATAL_FAILURE(Validate(pack_start));
    }

    for (int width = kBorderWidth * 2; width <= 500; ++width) {
      SCOPED_TRACE(testing::Message() << "Expand hide_child_directly = TRUE,"
                   << " width = " << width);

      allocation.width = width;
      gtk_widget_size_allocate(box_, &allocation);
      gtk_widget_size_allocate(box_, &allocation);
      ASSERT_NO_FATAL_FAILURE(Validate(pack_start));
    }
  }

 protected:
  GtkWidget* window_;
  GtkWidget* box_;

 private:
  struct ChildData {
    bool visible;
    int padding;
    int x;
    int width;
  };

  static void CollectChildData(GtkWidget* child, gpointer userdata) {
    guint padding;
    gtk_box_query_child_packing(GTK_BOX(gtk_widget_get_parent(child)), child,
                                NULL, NULL, &padding, NULL);

    ChildData data;
    data.visible = gtk_widget_get_visible(child);
    data.padding = padding;
    data.x = child->allocation.x;
    data.width = child->allocation.width;

    reinterpret_cast<std::vector<ChildData>*>(userdata)->push_back(data);
  }
};

TEST_F(GtkChromeShrinkableHBoxTest, PackStart) {
  AddChildren(true);

  {
    SCOPED_TRACE("Test LTR");
    gtk_widget_set_direction(box_, GTK_TEXT_DIR_LTR);
    EXPECT_NO_FATAL_FAILURE(Test(true));
  }
  {
    SCOPED_TRACE("Test RTL");
    gtk_widget_set_direction(box_, GTK_TEXT_DIR_RTL);
    EXPECT_NO_FATAL_FAILURE(Test(true));
  }
}

TEST_F(GtkChromeShrinkableHBoxTest, PackEnd) {
  AddChildren(false);

  {
    SCOPED_TRACE("Test LTR");
    gtk_widget_set_direction(box_, GTK_TEXT_DIR_LTR);
    EXPECT_NO_FATAL_FAILURE(Test(false));
  }
  {
    SCOPED_TRACE("Test RTL");
    gtk_widget_set_direction(box_, GTK_TEXT_DIR_RTL);
    EXPECT_NO_FATAL_FAILURE(Test(false));
  }
}

TEST_F(GtkChromeShrinkableHBoxTest, Homogeneous) {
  AddChildren(true);
  gtk_box_set_homogeneous(GTK_BOX(box_), true);

  {
    SCOPED_TRACE("Test LTR");
    gtk_widget_set_direction(box_, GTK_TEXT_DIR_LTR);
    EXPECT_NO_FATAL_FAILURE(Test(true));
  }
  {
    SCOPED_TRACE("Test RTL");
    gtk_widget_set_direction(box_, GTK_TEXT_DIR_RTL);
    EXPECT_NO_FATAL_FAILURE(Test(true));
  }
}

/* [<][>][^][v][top][bottom][index][help] */