// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ui/base/test/view_tree_validator.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/test/cocoa_helper.h"
using ViewTreeValidatorTest = ui::CocoaTest;
namespace {
NSView* ButtonWithFrame(int x, int y, int w, int h) {
return [[NSButton alloc] initWithFrame:NSMakeRect(x, y, w, h)];
}
NSView* ViewWithFrame(int x, int y, int w, int h) {
return [[NSView alloc] initWithFrame:NSMakeRect(x, y, w, h)];
}
void AdjustWidth(NSView* view, int delta) {
NSRect r = view.frame;
r.size.width += delta;
view.frame = r;
}
void AdjustHeight(NSView* view, int delta) {
NSRect r = view.frame;
r.size.height += delta;
view.frame = r;
}
} // namespace
TEST_F(ViewTreeValidatorTest, CorrectnessTest) {
NSWindow* window = test_window();
int width = NSWidth(window.contentView.frame);
int height = NSHeight(window.contentView.frame);
// A diagram. Views 3–5 share edges with their parents, and views 4 and 5
// share an overlapping edge, but in this diagram, the edges are drawn with
// insets to make it clear which views are subviews of other views.
// ┌────────────┬────────────┐
// │ │ ┌────────┐ │
// │ │ │ view_5 │ │
// │ │ └────────┘ │
// │ view_1 | view_2 │
// │ ┌────────┐ │ ┌────────┐ │
// │ │ view_3 │ │ │ view_4 │ │
// │ └────────┘ │ └────────┘ │
// └────────────┴────────────┘
NSView* view_1 = ViewWithFrame(0, 0, width / 2, height);
NSView* view_2 = ButtonWithFrame(width / 2, 0, width / 2, height);
NSView* view_3 = ButtonWithFrame(0, 0, width / 2, height / 2);
NSView* view_4 = ViewWithFrame(0, 0, width / 2, height / 2);
NSView* view_5 = ViewWithFrame(0, height / 2, width / 2, height / 2);
[view_2 addSubview:view_4];
[view_2 addSubview:view_5];
[view_1 addSubview:view_3];
[window.contentView addSubview:view_1];
[window.contentView addSubview:view_2];
{
// The original layout is well-formed.
std::optional<ui::ViewTreeProblemDetails> details =
ui::ValidateViewTree(window.contentView);
EXPECT_FALSE(details.has_value());
}
{
// Make view_3 no longer contained within view_1.
AdjustWidth(view_3, 1);
std::optional<ui::ViewTreeProblemDetails> details =
ui::ValidateViewTree(window.contentView);
ASSERT_TRUE(details.has_value());
EXPECT_EQ(details->type,
ui::ViewTreeProblemDetails::ProblemType::kViewOutsideParent);
EXPECT_EQ(details->view_a, view_3);
EXPECT_EQ(details->view_b, view_1);
AdjustWidth(view_3, -1);
}
{
// Make view_1 overlap view_2.
AdjustWidth(view_1, 1);
std::optional<ui::ViewTreeProblemDetails> details =
ui::ValidateViewTree(window.contentView);
ASSERT_TRUE(details.has_value());
EXPECT_EQ(details->type,
ui::ViewTreeProblemDetails::ProblemType::kViewsOverlap);
// Since there's no specified order for |view_a| and |view_b| for
// kViewsOverlap, check that |view_1| and |view_2| both appear exactly
// once in |view_a| and |view_b|.
EXPECT_TRUE(details->view_a == view_1 || details->view_a == view_2);
EXPECT_TRUE(details->view_b == view_1 || details->view_b == view_2);
EXPECT_NE(details->view_a, details->view_b);
AdjustWidth(view_1, -1);
}
{
// Make view_4 overlap view_5. Since they're both not localizable, this
// isn't an error.
AdjustHeight(view_4, 1);
std::optional<ui::ViewTreeProblemDetails> details =
ui::ValidateViewTree(window.contentView);
EXPECT_FALSE(details.has_value());
AdjustHeight(view_4, -1);
}
}