| // Copyright 2010-2015, Google Inc. |
| // All rights reserved. |
| // |
| // Redistribution and use in source and binary forms, with or without |
| // modification, are permitted provided that the following conditions are |
| // met: |
| // |
| // * Redistributions of source code must retain the above copyright |
| // notice, this list of conditions and the following disclaimer. |
| // * Redistributions in binary form must reproduce the above |
| // copyright notice, this list of conditions and the following disclaimer |
| // in the documentation and/or other materials provided with the |
| // distribution. |
| // * Neither the name of Google Inc. nor the names of its |
| // contributors may be used to endorse or promote products derived from |
| // this software without specific prior written permission. |
| // |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| |
| #include "win32/tip/tip_input_mode_manager.h" |
| |
| #include "testing/base/public/googletest.h" |
| #include "testing/base/public/gunit.h" |
| |
| namespace mozc { |
| namespace win32 { |
| namespace tsf { |
| namespace { |
| |
| typedef TipInputModeManagerImpl::StatePair StatePair; |
| class TestableTipInputModeManagerImpl : public TipInputModeManagerImpl { |
| public: |
| using TipInputModeManagerImpl::GetOverriddenState; |
| }; |
| |
| TipInputModeManager::Config GetGlobalMode() { |
| TipInputModeManager::Config config; |
| config.use_global_mode = true; |
| return config; |
| } |
| |
| TipInputModeManager::Config GetThreadLocalMode() { |
| TipInputModeManager::Config config; |
| config.use_global_mode = false; |
| return config; |
| } |
| |
| const DWORD kNativeHiragana = |
| IME_CMODE_NATIVE | IME_CMODE_FULLSHAPE | IME_CMODE_ROMAN; |
| const DWORD kNativeHalfAlpha = IME_CMODE_ROMAN; |
| |
| TEST(TipInputModeManagerImplTest, GetOverriddenState) { |
| // Check if input scopes for turning off IME temporarily. |
| { |
| vector<InputScope> input_scope_off; |
| input_scope_off.push_back(IS_NUMBER); |
| input_scope_off.push_back(IS_EMAIL_SMTPEMAILADDRESS); |
| { |
| const StatePair state = |
| TestableTipInputModeManagerImpl::GetOverriddenState( |
| StatePair(false, TipInputModeManagerImpl::kHiragana), |
| input_scope_off); |
| EXPECT_FALSE(state.open_close); |
| EXPECT_EQ(TipInputModeManagerImpl::kHiragana, state.conversion_mode); |
| } |
| { |
| const StatePair state = |
| TestableTipInputModeManagerImpl::GetOverriddenState( |
| StatePair(true, TipInputModeManagerImpl::kHiragana), |
| input_scope_off); |
| EXPECT_FALSE(state.open_close) << "Should be temporarily off."; |
| EXPECT_EQ(TipInputModeManagerImpl::kHiragana, state.conversion_mode); |
| } |
| } |
| |
| // Check if input scopes for turning on IME temporarily. |
| { |
| vector<InputScope> input_scope_full_hiragana; |
| input_scope_full_hiragana.push_back(IS_HIRAGANA); |
| { |
| const StatePair state = |
| TestableTipInputModeManagerImpl::GetOverriddenState( |
| StatePair(false, TipInputModeManagerImpl::kHiragana), |
| input_scope_full_hiragana); |
| EXPECT_TRUE(state.open_close) << "Should be temporarily on."; |
| EXPECT_EQ(TipInputModeManagerImpl::kHiragana, state.conversion_mode); |
| } |
| { |
| const StatePair state = |
| TestableTipInputModeManagerImpl::GetOverriddenState( |
| StatePair(true, TipInputModeManagerImpl::kHiragana), |
| input_scope_full_hiragana); |
| EXPECT_TRUE(state.open_close) << "Should be temporarily on."; |
| EXPECT_EQ(TipInputModeManagerImpl::kHiragana, state.conversion_mode); |
| } |
| { |
| const StatePair state = |
| TestableTipInputModeManagerImpl::GetOverriddenState( |
| StatePair(true, TipInputModeManagerImpl::kFullAscii), |
| input_scope_full_hiragana); |
| EXPECT_TRUE(state.open_close) << "Should be temporarily on."; |
| EXPECT_EQ(TipInputModeManagerImpl::kHiragana, state.conversion_mode) |
| << "Should be temporarily Hiragana"; |
| } |
| } |
| |
| // If there are multiple input scopes and they are not aggregatable, use the |
| // original state as is. |
| { |
| vector<InputScope> input_scope_invalid; |
| input_scope_invalid.push_back(IS_HIRAGANA); |
| input_scope_invalid.push_back(IS_KATAKANA_FULLWIDTH); |
| { |
| const StatePair state = |
| TestableTipInputModeManagerImpl::GetOverriddenState( |
| StatePair(false, TipInputModeManagerImpl::kHiragana), |
| input_scope_invalid); |
| EXPECT_FALSE(state.open_close); |
| EXPECT_EQ(TipInputModeManagerImpl::kHiragana, state.conversion_mode); |
| } |
| { |
| const StatePair state = |
| TestableTipInputModeManagerImpl::GetOverriddenState( |
| StatePair(true, TipInputModeManagerImpl::kHiragana), |
| input_scope_invalid); |
| EXPECT_TRUE(state.open_close); |
| EXPECT_EQ(TipInputModeManagerImpl::kHiragana, state.conversion_mode); |
| } |
| { |
| const StatePair state = |
| TestableTipInputModeManagerImpl::GetOverriddenState( |
| StatePair(true, TipInputModeManagerImpl::kFullAscii), |
| input_scope_invalid); |
| EXPECT_TRUE(state.open_close); |
| EXPECT_EQ(TipInputModeManagerImpl::kFullAscii, state.conversion_mode); |
| } |
| } |
| } |
| |
| TEST(TipInputModeManagerTest, IgnoreConversionModeByGlobalConfig_Issue8583505) { |
| // When per-session input mode is enabled, mode change notification from the |
| // application should be ignored. |
| TipInputModeManager input_mode_manager(GetGlobalMode()); |
| |
| // Initialize (Off + Hiragana) |
| input_mode_manager.OnInitialize(false, kNativeHiragana); |
| |
| // SetFocus (Off + Hiragana) |
| vector<InputScope> input_scope_empty; |
| auto action = input_mode_manager.OnSetFocus( |
| false, kNativeHiragana, input_scope_empty); |
| EXPECT_EQ(TipInputModeManager::kDoNothing, action); |
| EXPECT_FALSE(input_mode_manager.IsIndicatorVisible()); |
| EXPECT_FALSE(input_mode_manager.GetEffectiveOpenClose()); |
| EXPECT_EQ(TipInputModeManager::kHiragana, |
| input_mode_manager.GetEffectiveConversionMode()); |
| |
| // On |
| input_mode_manager.OnReceiveCommand( |
| true, TipInputModeManager::kHiragana, TipInputModeManager::kHiragana); |
| EXPECT_TRUE(input_mode_manager.GetEffectiveOpenClose()); |
| EXPECT_TRUE(input_mode_manager.IsIndicatorVisible()); |
| |
| // Key |
| action = input_mode_manager.OnKey(VirtualKey(), true, true); |
| EXPECT_TRUE(input_mode_manager.GetEffectiveOpenClose()); |
| EXPECT_FALSE(input_mode_manager.IsIndicatorVisible()); |
| |
| // Hiragana -> HalfAlpha (ignored) |
| action = input_mode_manager.OnChangeConversionMode(kNativeHalfAlpha); |
| EXPECT_EQ(TipInputModeManager::kDoNothing, action); |
| EXPECT_FALSE(input_mode_manager.IsIndicatorVisible()); |
| EXPECT_EQ(TipInputModeManager::kHiragana, |
| input_mode_manager.GetEffectiveConversionMode()); |
| } |
| |
| TEST(TipInputModeManagerTest, HonorConversionMode_Issue8583505) { |
| // When thread local input mode is enabled, mode change notification from the |
| // application should be honored. |
| TipInputModeManager input_mode_manager(GetThreadLocalMode()); |
| |
| // Initialize (Off + Hiragana) |
| input_mode_manager.OnInitialize(false, kNativeHiragana); |
| |
| // SetFocus (Off + Hiragana) |
| vector<InputScope> input_scope_empty; |
| auto action = input_mode_manager.OnSetFocus( |
| false, kNativeHiragana, input_scope_empty); |
| EXPECT_EQ(TipInputModeManager::kDoNothing, action); |
| EXPECT_FALSE(input_mode_manager.IsIndicatorVisible()); |
| EXPECT_FALSE(input_mode_manager.GetEffectiveOpenClose()); |
| EXPECT_EQ(TipInputModeManager::kHiragana, |
| input_mode_manager.GetEffectiveConversionMode()); |
| |
| // On |
| input_mode_manager.OnReceiveCommand( |
| true, TipInputModeManager::kHiragana, TipInputModeManager::kHiragana); |
| EXPECT_TRUE(input_mode_manager.GetEffectiveOpenClose()); |
| EXPECT_TRUE(input_mode_manager.IsIndicatorVisible()); |
| |
| // Key |
| action = input_mode_manager.OnKey(VirtualKey(), true, true); |
| EXPECT_TRUE(input_mode_manager.GetEffectiveOpenClose()); |
| EXPECT_FALSE(input_mode_manager.IsIndicatorVisible()); |
| |
| // Hiragana -> HalfAlpha (honored) |
| action = input_mode_manager.OnChangeConversionMode(kNativeHalfAlpha); |
| EXPECT_EQ(TipInputModeManager::kUpdateUI, action); |
| EXPECT_TRUE(input_mode_manager.IsIndicatorVisible()); |
| EXPECT_EQ(TipInputModeManager::kHalfAscii, |
| input_mode_manager.GetEffectiveConversionMode()); |
| } |
| |
| TEST(TipInputModeManagerTest, ChangeInputScope) { |
| TipInputModeManager input_mode_manager(GetThreadLocalMode()); |
| |
| // Initialize (Off + Hiragana) |
| input_mode_manager.OnInitialize(false, kNativeHiragana); |
| |
| // SetFocus (Off + Hiragana) |
| vector<InputScope> input_scope_empty; |
| auto action = input_mode_manager.OnSetFocus( |
| false, kNativeHiragana, input_scope_empty); |
| EXPECT_EQ(TipInputModeManager::kDoNothing, action); |
| EXPECT_FALSE(input_mode_manager.IsIndicatorVisible()); |
| EXPECT_FALSE(input_mode_manager.GetEffectiveOpenClose()); |
| EXPECT_EQ(TipInputModeManager::kHiragana, |
| input_mode_manager.GetEffectiveConversionMode()); |
| |
| vector<InputScope> input_scope_full_katakana; |
| input_scope_full_katakana.push_back(IS_KATAKANA_FULLWIDTH); |
| |
| // InputScope: IS_KATAKANA_FULLWIDTH |
| // This should change the mode and make indicator visible. |
| action = input_mode_manager.OnChangeInputScope(input_scope_full_katakana); |
| EXPECT_EQ(TipInputModeManager::kUpdateUI, action); |
| EXPECT_TRUE(input_mode_manager.GetEffectiveOpenClose()); |
| EXPECT_EQ(TipInputModeManager::kFullKatakana, |
| input_mode_manager.GetEffectiveConversionMode()); |
| EXPECT_TRUE(input_mode_manager.IsIndicatorVisible()); |
| |
| action = input_mode_manager.OnKey(VirtualKey(), true, true); |
| EXPECT_FALSE(input_mode_manager.IsIndicatorVisible()); |
| |
| // InputScope: IS_EMAIL_SMTPEMAILADDRESS |
| // This should change the mode and make indicator visible. |
| vector<InputScope> input_scope_email; |
| input_scope_email.push_back(IS_EMAIL_SMTPEMAILADDRESS); |
| action = input_mode_manager.OnChangeInputScope(input_scope_email); |
| EXPECT_EQ(TipInputModeManager::kUpdateUI, action); |
| EXPECT_FALSE(input_mode_manager.GetEffectiveOpenClose()); |
| EXPECT_EQ(TipInputModeManager::kHiragana, |
| input_mode_manager.GetEffectiveConversionMode()); |
| EXPECT_TRUE(input_mode_manager.IsIndicatorVisible()); |
| |
| // OnKey |
| // This should make indicator invisible. |
| action = input_mode_manager.OnKey(VirtualKey(), true, true); |
| EXPECT_FALSE(input_mode_manager.IsIndicatorVisible()); |
| |
| // InputScope: IS_NUMBER |
| // This should not change the mode and keep indicator invisible. |
| vector<InputScope> input_scope_number; |
| input_scope_number.push_back(IS_NUMBER); |
| action = input_mode_manager.OnChangeInputScope(input_scope_number); |
| EXPECT_EQ(TipInputModeManager::kDoNothing, action); |
| EXPECT_FALSE(input_mode_manager.GetEffectiveOpenClose()); |
| EXPECT_EQ(TipInputModeManager::kHiragana, |
| input_mode_manager.GetEffectiveConversionMode()); |
| EXPECT_FALSE(input_mode_manager.IsIndicatorVisible()); |
| } |
| |
| TEST(TipInputModeManagerTest, HonorOpenCloseModeFromApps_Issue8661096) { |
| TipInputModeManager input_mode_manager(GetGlobalMode()); |
| |
| // Initialize (Off + Hiragana) |
| input_mode_manager.OnInitialize(false, kNativeHiragana); |
| |
| // An application calls ImmSetOpenStatus(himc, TRUE) |
| auto action = input_mode_manager.OnChangeOpenClose(true); |
| EXPECT_EQ(TipInputModeManager::kUpdateUI, action); |
| EXPECT_TRUE(input_mode_manager.IsIndicatorVisible()); |
| EXPECT_TRUE(input_mode_manager.GetEffectiveOpenClose()); |
| EXPECT_TRUE(input_mode_manager.GetTsfOpenClose()); |
| } |
| |
| } // namespace |
| } // namespace tsf |
| } // namespace win32 |
| } // namespace mozc |