chromium/ash/system/channel_indicator/channel_indicator_unittest.cc

// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ash/system/channel_indicator/channel_indicator.h"

#include "ash/public/cpp/shelf_types.h"
#include "ash/resources/vector_icons/vector_icons.h"
#include "ash/session/session_controller_impl.h"
#include "ash/shelf/shelf.h"
#include "ash/shell.h"
#include "ash/system/channel_indicator/channel_indicator_utils.h"
#include "ash/system/model/system_tray_model.h"
#include "ash/system/status_area_widget_test_helper.h"
#include "ash/system/tray/tray_constants.h"
#include "ash/system/unified/unified_system_tray.h"
#include "ash/test/ash_test_base.h"
#include "ash/test_shell_delegate.h"
#include "components/session_manager/session_manager_types.h"
#include "components/version_info/channel.h"
#include "ui/views/accessibility/view_accessibility.h"
#include "ui/views/controls/image_view.h"

namespace ash {

namespace {

// Constants for determining whether a view is "squished" i.e. one of its
// dimensions is very small and one dimension is much larger than the other.
constexpr int kSquishedMinDimension = 2;
constexpr int kSquishedMaxDifferenceBetweenDimensions = 2;

}  // namespace

class ChannelIndicatorViewTest
    : public AshTestBase,
      public testing::WithParamInterface<version_info::Channel> {
 public:
  ChannelIndicatorViewTest() = default;
  ChannelIndicatorViewTest(const ChannelIndicatorViewTest&) = delete;
  ChannelIndicatorViewTest& operator=(const ChannelIndicatorViewTest&) = delete;
  ~ChannelIndicatorViewTest() override = default;

  // AshTestBase:
  void SetUp() override {
    // Instantiate a `TestShellDelegate` with the channel set to our param.
    std::unique_ptr<TestShellDelegate> shell_delegate =
        std::make_unique<TestShellDelegate>();
    shell_delegate->set_channel(static_cast<version_info::Channel>(GetParam()));
    AshTestBase::SetUp(std::move(shell_delegate));
  }

  void SetSessionState(session_manager::SessionState state) {
    SessionInfo info;
    info.state = state;
    Shell::Get()->session_controller()->SetSessionInfo(info);
  }

  bool IsViewSquished(const views::View* view) {
    DCHECK(view);

    // A view is considered "squished" if:
    // (1) Either dimension of its bounds is very small and
    // (2) One dimension is much larger than the other
    gfx::Rect bounds = view->GetLocalBounds();
    bool is_squished = (bounds.width() <= kSquishedMinDimension ||
                        bounds.height() <= kSquishedMinDimension) &&
                       std::abs(bounds.width() - bounds.height()) >=
                           kSquishedMaxDifferenceBetweenDimensions;
    if (is_squished) {
      LOG(ERROR) << __FUNCTION__ << " view (w: " << bounds.width()
                 << " h: " << bounds.height() << ") is squished ";
    }
    return is_squished;
  }
};

// Run the `Visible` test below for each value of version_info::Channel.
INSTANTIATE_TEST_SUITE_P(ChannelValues,
                         ChannelIndicatorViewTest,
                         ::testing::Values(version_info::Channel::UNKNOWN,
                                           version_info::Channel::STABLE,
                                           version_info::Channel::BETA,
                                           version_info::Channel::DEV,
                                           version_info::Channel::CANARY));

TEST_P(ChannelIndicatorViewTest, Visible) {
  // Local ref.
  ShellDelegate* shell_delegate = Shell::Get()->shell_delegate();
  DCHECK(shell_delegate);

  // Local ref.
  UnifiedSystemTray* tray =
      StatusAreaWidgetTestHelper::GetStatusAreaWidget()->unified_system_tray();
  ChannelIndicatorView* channel_indicator_view = tray->channel_indicator_view();

  // If the channel is not displayable, there should be no view and the test is
  // complete.
  if (!channel_indicator_utils::IsDisplayableChannel(
          shell_delegate->GetChannel())) {
    EXPECT_FALSE(channel_indicator_view);
    EXPECT_FALSE(tray->ShouldChannelIndicatorBeShown());
    return;
  }

  // Otherwise the view exists, should be shown, and is visible.
  EXPECT_TRUE(channel_indicator_view);
  EXPECT_TRUE(tray->ShouldChannelIndicatorBeShown());
  EXPECT_TRUE(channel_indicator_view->GetVisible());

  // User is not logged in, view should display text, no image.
  SetSessionState(session_manager::SessionState::LOGIN_PRIMARY);
  EXPECT_TRUE(channel_indicator_view->IsLabelVisibleForTesting());
  EXPECT_FALSE(channel_indicator_view->IsImageViewVisibleForTesting());

  // User is logged in, view should display image, no text.
  SetSessionState(session_manager::SessionState::ACTIVE);
  EXPECT_FALSE(channel_indicator_view->IsLabelVisibleForTesting());
  EXPECT_TRUE(channel_indicator_view->IsImageViewVisibleForTesting());

  // Two shelf alignments to test.
  auto shelf_alignments = {ShelfAlignment::kBottom, ShelfAlignment::kRight};

  // Image is the right size in both alignments.
  for (const auto& alignment : shelf_alignments) {
    // Initiates the shelf alignment change.
    GetPrimaryShelf()->SetAlignment(alignment);

    // Perform a synchronous `Layout` of `channel_indicator_view` and its child
    // views.
    channel_indicator_view->GetWidget()->LayoutRootViewIfNecessary();

    // Now test the bounds of the image view.
    EXPECT_FALSE(IsViewSquished(
        GetPrimaryUnifiedSystemTray()->channel_indicator_view()->image_view()));
  }

  // User locks the session, view should display text, no image.
  SetSessionState(session_manager::SessionState::LOCKED);
  EXPECT_TRUE(channel_indicator_view->IsLabelVisibleForTesting());
  EXPECT_FALSE(channel_indicator_view->IsImageViewVisibleForTesting());

  // User is logged in again, view should display image, no text.
  SetSessionState(session_manager::SessionState::ACTIVE);
  EXPECT_FALSE(channel_indicator_view->IsLabelVisibleForTesting());
  EXPECT_TRUE(channel_indicator_view->IsImageViewVisibleForTesting());

  // Image is the right size in both alignments.
  for (const auto& alignment : shelf_alignments) {
    // Initiates the shelf alignment change.
    GetPrimaryShelf()->SetAlignment(alignment);

    // Perform a synchronous `Layout` of `channel_indicator_view` and its child
    // views.
    channel_indicator_view->GetWidget()->LayoutRootViewIfNecessary();

    // Now test the bounds of the image view.
    EXPECT_FALSE(IsViewSquished(
        GetPrimaryUnifiedSystemTray()->channel_indicator_view()->image_view()));
  }
}

TEST_P(ChannelIndicatorViewTest, AccessibleProperties) {
  ShellDelegate* shell_delegate = Shell::Get()->shell_delegate();
  UnifiedSystemTray* tray =
      StatusAreaWidgetTestHelper::GetStatusAreaWidget()->unified_system_tray();
  ChannelIndicatorView* channel_indicator_view = tray->channel_indicator_view();
  if (!channel_indicator_utils::IsDisplayableChannel(
          shell_delegate->GetChannel())) {
    EXPECT_FALSE(channel_indicator_view);
    GTEST_SKIP()
        << "Test is only valid when channel indicator view is not null.";
  }

  ui::AXNodeData data;
  channel_indicator_view->GetViewAccessibility().GetAccessibleNodeData(&data);
  EXPECT_EQ(data.role, ax::mojom::Role::kLabelText);
}

}  // namespace ash