blob: 291e4ec1173ed10f29c4c9bb53430f5c240c693e [file] [log] [blame]
// Copyright 2019 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/ash/crostini/crostini_unsupported_action_notifier.h"
#include <memory>
#include <string>
#include <tuple>
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace crostini {
using ::testing::_;
using ::testing::Bool;
using ::testing::Combine;
using ::testing::NiceMock;
using ::testing::Return;
using ::testing::Truly;
using chromeos::input_method::InputMethodDescriptor;
class MockDelegate : public CrostiniUnsupportedActionNotifier::Delegate {
public:
MOCK_METHOD(bool, IsInTabletMode, (), (override));
MOCK_METHOD(bool, IsFocusedWindowCrostini, (), (override));
MOCK_METHOD(bool, IsVirtualKeyboardVisible, (), (override));
MOCK_METHOD(void, ShowToast, (const ash::ToastData& toast_data), (override));
MOCK_METHOD(std::string,
GetLocalizedDisplayName,
(const InputMethodDescriptor& descriptor),
(override));
MOCK_METHOD(InputMethodDescriptor, GetCurrentInputMethod, (), (override));
MOCK_METHOD(int, ToastTimeoutMs, (), (override));
MOCK_METHOD(void,
AddFocusObserver,
(aura::client::FocusChangeObserver * observer),
(override));
MOCK_METHOD(void,
RemoveFocusObserver,
(aura::client::FocusChangeObserver * observer),
(override));
MOCK_METHOD(void,
AddTabletModeObserver,
(ash::TabletModeObserver * observer),
(override));
MOCK_METHOD(void,
RemoveTabletModeObserver,
(ash::TabletModeObserver * observer),
(override));
MOCK_METHOD(void,
AddInputMethodObserver,
(chromeos::input_method::InputMethodManager::Observer * observer),
(override));
MOCK_METHOD(void,
RemoveInputMethodObserver,
(chromeos::input_method::InputMethodManager::Observer * observer),
(override));
MOCK_METHOD(void,
AddKeyboardControllerObserver,
(ash::KeyboardControllerObserver * observer),
(override));
MOCK_METHOD(void,
RemoveKeyboardControllerObserver,
(ash::KeyboardControllerObserver * observer),
(override));
};
namespace {
constexpr char supported_ime_id[] =
"_comp_ime_jkghodnilhceideoidjikpgommlajknkxkb:am:phonetic:arm";
constexpr char unsupported_ime_id[] =
"_comp_ime_jkghodnilhceideoidjikpgommlajknkvkd_ethi";
const InputMethodDescriptor
supported(supported_ime_id, {}, {}, {}, {}, {}, {}, {});
const InputMethodDescriptor
unsupported(unsupported_ime_id, {}, {}, {}, {}, {}, {}, {});
} // namespace
class CrostiniUnsupportedActionNotifierTest
: public testing::TestWithParam<std::tuple<bool, bool, bool, bool>> {
public:
CrostiniUnsupportedActionNotifierTest()
: notifier(std::make_unique<NiceMock<MockDelegate>>()) {}
virtual ~CrostiniUnsupportedActionNotifierTest() = default;
MockDelegate& get_delegate() {
auto* ptr = notifier.get_delegate_for_testing();
DCHECK(ptr);
// Our delegate is always a mock delegate in these tests, but we don't have
// RTTI so have to use a static cast.
return static_cast<NiceMock<MockDelegate>&>(*ptr);
}
bool is_tablet_mode() const { return std::get<0>(GetParam()); }
bool is_crostini_focused() const { return std::get<1>(GetParam()); }
bool is_vk_visible() const { return std::get<2>(GetParam()); }
bool is_ime_unsupported() const { return std::get<3>(GetParam()); }
InputMethodDescriptor ime_descriptor() const {
return is_ime_unsupported() ? unsupported : supported;
}
static bool IsIMEToast(const ash::ToastData& data) {
return data.id.compare(0, 3, "IME") == 0;
}
static bool IsVKToast(const ash::ToastData& data) {
return data.id.compare(0, 2, "VK") == 0;
}
void SetExpectations(bool show_tablet_toast,
bool show_vk_toast,
bool show_ime_toast) {
// We show the same toast for tablet mode and manually triggered virtual
// keyboard, so showing either counts as showing both.
int num_tablet_toasts = (show_tablet_toast || show_vk_toast) ? 1 : 0;
int num_ime_toasts = show_ime_toast ? 1 : 0;
EXPECT_CALL(get_delegate(), IsInTabletMode)
.WillRepeatedly(Return(is_tablet_mode()));
EXPECT_CALL(get_delegate(), IsVirtualKeyboardVisible)
.WillRepeatedly(Return(is_vk_visible()));
EXPECT_CALL(get_delegate(), IsFocusedWindowCrostini)
.WillRepeatedly(Return(is_crostini_focused()));
EXPECT_CALL(get_delegate(), GetCurrentInputMethod)
.WillRepeatedly(Return(ime_descriptor()));
ON_CALL(get_delegate(), GetLocalizedDisplayName).WillByDefault(Return(""));
EXPECT_CALL(get_delegate(), ShowToast(Truly(IsVKToast)))
.Times(num_tablet_toasts);
EXPECT_CALL(get_delegate(), ShowToast(Truly(IsIMEToast)))
.Times(num_ime_toasts);
}
CrostiniUnsupportedActionNotifier notifier;
};
TEST_P(CrostiniUnsupportedActionNotifierTest,
ToastShownOnceOnlyWhenEnteringTabletMode) {
bool show_tablet_toast = is_tablet_mode() && is_crostini_focused();
// Since tablet and vk toasts are the same we can trigger the toast
// OnTabletModeStarted even if not in tablet mode.
bool show_vk_toast = is_vk_visible() && is_crostini_focused();
bool show_ime_toast = false;
SetExpectations(show_tablet_toast, show_vk_toast, show_ime_toast);
notifier.OnTabletModeStarted();
notifier.OnTabletModeStarted();
}
TEST_P(CrostiniUnsupportedActionNotifierTest,
ToastShownOnceOnlyWhenShowingVirtualKeyboard) {
// Since tablet and vk toasts are the same we can trigger the toast
// OnKeyboardVisibilityChanged even if the virtual keyboard isn't visible.
bool show_tablet_toast = is_tablet_mode() && is_crostini_focused();
bool show_vk_toast = is_vk_visible() && is_crostini_focused();
bool show_ime_toast = false;
SetExpectations(show_tablet_toast, show_vk_toast, show_ime_toast);
notifier.OnKeyboardVisibilityChanged(true);
notifier.OnKeyboardVisibilityChanged(false);
notifier.OnKeyboardVisibilityChanged(true);
}
TEST_P(CrostiniUnsupportedActionNotifierTest,
ToastShownOnceOnlyWhenChangingIME) {
bool show_tablet_toast = false;
bool show_vk_toast = false;
bool show_ime_toast = is_ime_unsupported() && is_crostini_focused();
SetExpectations(show_tablet_toast, show_vk_toast, show_ime_toast);
notifier.InputMethodChanged({}, {}, {});
notifier.InputMethodChanged({}, {}, {});
}
TEST_P(CrostiniUnsupportedActionNotifierTest,
ToastsShownOnceOnlyWhenFocusingCrostiniApp) {
bool show_tablet_toast = is_tablet_mode() && is_crostini_focused();
bool show_vk_toast = is_vk_visible() && is_crostini_focused();
bool show_ime_toast = is_ime_unsupported() && is_crostini_focused();
SetExpectations(show_tablet_toast, show_vk_toast, show_ime_toast);
notifier.OnWindowFocused({}, {});
notifier.OnWindowFocused({}, {});
}
INSTANTIATE_TEST_CASE_P(CrostiniUnsupportedActionNotifierTestCombination,
CrostiniUnsupportedActionNotifierTest,
Combine(Bool(), Bool(), Bool(), Bool()));
} // namespace crostini