Commit c0430280 authored by Xiaohui Chen's avatar Xiaohui Chen Committed by Chromium LUCI CQ

assistant: delay active conversation closing

This avoids potential race when conversation involves opening
a new window which immediately closes the conversation.

Bug: b/173833583
Change-Id: I56654232099c5bb7318fcd17ed26e0d3bbb0d66d
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2623116
Commit-Queue: Xiaohui Chen <xiaohuic@chromium.org>
Reviewed-by: default avatarJeroen Dhollander <jeroendh@chromium.org>
Cr-Commit-Position: refs/heads/master@{#842812}
parent 248d70fe
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include "base/time/time.h" #include "base/time/time.h"
#include "chrome/browser/ui/ash/assistant/assistant_test_mixin.h" #include "chrome/browser/ui/ash/assistant/assistant_test_mixin.h"
#include "chrome/test/base/mixin_based_in_process_browser_test.h" #include "chrome/test/base/mixin_based_in_process_browser_test.h"
#include "chromeos/assistant/test_support/expect_utils.h"
#include "chromeos/audio/cras_audio_handler.h" #include "chromeos/audio/cras_audio_handler.h"
#include "chromeos/dbus/power_manager/backlight.pb.h" #include "chromeos/dbus/power_manager/backlight.pb.h"
#include "chromeos/services/assistant/public/cpp/features.h" #include "chromeos/services/assistant/public/cpp/features.h"
...@@ -36,6 +37,8 @@ constexpr int kStartBrightnessPercent = 50; ...@@ -36,6 +37,8 @@ constexpr int kStartBrightnessPercent = 50;
} // namespace } // namespace
using chromeos::assistant::test::ExpectResult;
class AssistantBrowserTest : public MixinBasedInProcessBrowserTest { class AssistantBrowserTest : public MixinBasedInProcessBrowserTest {
public: public:
AssistantBrowserTest() = default; AssistantBrowserTest() = default;
...@@ -64,7 +67,7 @@ class AssistantBrowserTest : public MixinBasedInProcessBrowserTest { ...@@ -64,7 +67,7 @@ class AssistantBrowserTest : public MixinBasedInProcessBrowserTest {
chromeos::PowerManagerClient::Get()->SetScreenBrightness(request); chromeos::PowerManagerClient::Get()->SetScreenBrightness(request);
// Wait for the initial value to settle. // Wait for the initial value to settle.
tester()->ExpectResult( ExpectResult(
true, base::BindLambdaForTesting([&]() { true, base::BindLambdaForTesting([&]() {
constexpr double kEpsilon = 0.1; constexpr double kEpsilon = 0.1;
auto current_brightness = tester()->SyncCall(base::BindOnce( auto current_brightness = tester()->SyncCall(base::BindOnce(
...@@ -79,7 +82,7 @@ class AssistantBrowserTest : public MixinBasedInProcessBrowserTest { ...@@ -79,7 +82,7 @@ class AssistantBrowserTest : public MixinBasedInProcessBrowserTest {
void ExpectBrightnessUp() { void ExpectBrightnessUp() {
auto* power_manager = chromeos::PowerManagerClient::Get(); auto* power_manager = chromeos::PowerManagerClient::Get();
// Check the brightness changes // Check the brightness changes
tester()->ExpectResult( ExpectResult(
true, base::BindLambdaForTesting([&]() { true, base::BindLambdaForTesting([&]() {
constexpr double kEpsilon = 1; constexpr double kEpsilon = 1;
auto current_brightness = tester()->SyncCall(base::BindOnce( auto current_brightness = tester()->SyncCall(base::BindOnce(
...@@ -94,7 +97,7 @@ class AssistantBrowserTest : public MixinBasedInProcessBrowserTest { ...@@ -94,7 +97,7 @@ class AssistantBrowserTest : public MixinBasedInProcessBrowserTest {
void ExpectBrightnessDown() { void ExpectBrightnessDown() {
auto* power_manager = chromeos::PowerManagerClient::Get(); auto* power_manager = chromeos::PowerManagerClient::Get();
// Check the brightness changes // Check the brightness changes
tester()->ExpectResult( ExpectResult(
true, base::BindLambdaForTesting([&]() { true, base::BindLambdaForTesting([&]() {
constexpr double kEpsilon = 1; constexpr double kEpsilon = 1;
auto current_brightness = tester()->SyncCall(base::BindOnce( auto current_brightness = tester()->SyncCall(base::BindOnce(
...@@ -163,12 +166,12 @@ IN_PROC_BROWSER_TEST_F(AssistantBrowserTest, ShouldTurnUpVolume) { ...@@ -163,12 +166,12 @@ IN_PROC_BROWSER_TEST_F(AssistantBrowserTest, ShouldTurnUpVolume) {
tester()->SendTextQuery("turn up volume"); tester()->SendTextQuery("turn up volume");
tester()->ExpectResult(true, base::BindRepeating( ExpectResult(true, base::BindRepeating(
[](chromeos::CrasAudioHandler* cras) { [](chromeos::CrasAudioHandler* cras) {
return cras->GetOutputVolumePercent() > return cras->GetOutputVolumePercent() >
kStartVolumePercent; kStartVolumePercent;
}, },
cras)); cras));
} }
IN_PROC_BROWSER_TEST_F(AssistantBrowserTest, ShouldTurnDownVolume) { IN_PROC_BROWSER_TEST_F(AssistantBrowserTest, ShouldTurnDownVolume) {
...@@ -185,12 +188,12 @@ IN_PROC_BROWSER_TEST_F(AssistantBrowserTest, ShouldTurnDownVolume) { ...@@ -185,12 +188,12 @@ IN_PROC_BROWSER_TEST_F(AssistantBrowserTest, ShouldTurnDownVolume) {
tester()->SendTextQuery("turn down volume"); tester()->SendTextQuery("turn down volume");
tester()->ExpectResult(true, base::BindRepeating( ExpectResult(true, base::BindRepeating(
[](chromeos::CrasAudioHandler* cras) { [](chromeos::CrasAudioHandler* cras) {
return cras->GetOutputVolumePercent() < return cras->GetOutputVolumePercent() <
kStartVolumePercent; kStartVolumePercent;
}, },
cras)); cras));
} }
IN_PROC_BROWSER_TEST_F(AssistantBrowserTest, ShouldTurnUpBrightness) { IN_PROC_BROWSER_TEST_F(AssistantBrowserTest, ShouldTurnUpBrightness) {
......
...@@ -260,23 +260,6 @@ class TypedExpectedResponseWaiter : public ExpectedResponseWaiter { ...@@ -260,23 +260,6 @@ class TypedExpectedResponseWaiter : public ExpectedResponseWaiter {
const std::string class_name_; const std::string class_name_;
}; };
template <typename T>
void CheckResult(base::OnceClosure quit,
T expected_value,
base::RepeatingCallback<T()> value_callback) {
if (expected_value == value_callback.Run()) {
std::move(quit).Run();
return;
}
// Check again in the future
base::SequencedTaskRunnerHandle::Get()->PostDelayedTask(
FROM_HERE,
base::BindOnce(CheckResult<T>, std::move(quit), expected_value,
value_callback),
base::TimeDelta::FromMilliseconds(10));
}
// Calls a callback when the view hierarchy changes. // Calls a callback when the view hierarchy changes.
class CallbackViewHierarchyChangedObserver : views::ViewObserver { class CallbackViewHierarchyChangedObserver : views::ViewObserver {
public: public:
...@@ -454,27 +437,6 @@ void AssistantTestMixin::SendTextQuery(const std::string& query) { ...@@ -454,27 +437,6 @@ void AssistantTestMixin::SendTextQuery(const std::string& query) {
test_api_->SendTextQuery(query); test_api_->SendTextQuery(query);
} }
template <typename T>
void AssistantTestMixin::ExpectResult(
T expected_value,
base::RepeatingCallback<T()> value_callback) {
const base::test::ScopedRunLoopTimeout run_timeout(FROM_HERE,
kDefaultWaitTimeout);
// Wait until we're ready or we hit the timeout.
base::RunLoop run_loop;
CheckResult(run_loop.QuitClosure(), expected_value, value_callback);
EXPECT_NO_FATAL_FAILURE(run_loop.Run())
<< "Failed waiting for expected result.\n"
<< "Expected \"" << expected_value << "\"\n"
<< "Got \"" << value_callback.Run() << "\"";
}
template void AssistantTestMixin::ExpectResult<bool>(
bool expected_value,
base::RepeatingCallback<bool()> value_callback);
template <typename T> template <typename T>
T AssistantTestMixin::SyncCall( T AssistantTestMixin::SyncCall(
base::OnceCallback<void(base::OnceCallback<void(T)>)> func) { base::OnceCallback<void(base::OnceCallback<void(T)>)> func) {
......
...@@ -74,17 +74,6 @@ class AssistantTestMixin : public InProcessBrowserTestMixin { ...@@ -74,17 +74,6 @@ class AssistantTestMixin : public InProcessBrowserTestMixin {
// displaying the query input text field. // displaying the query input text field.
void SendTextQuery(const std::string& query); void SendTextQuery(const std::string& query);
// Check if the |expected_value| is equal to the result of running
// |value_callback|. This method will block and continuously try the
// comparison above until it succeeds, or timeout.
//
// NOTE: This is a template method. If you need to use it with a new type,
// you may see a link error. You will need to manually instantiate for the
// new type. Please see .cc file for examples.
template <typename T>
void ExpectResult(T expected_value,
base::RepeatingCallback<T()> value_callback);
// Synchronize an async method call to make testing simpler. |func| is the // Synchronize an async method call to make testing simpler. |func| is the
// async method to be invoked, the inner callback is the result callback. The // async method to be invoked, the inner callback is the result callback. The
// result with type |T| will be the return value. // result with type |T| will be the return value.
......
...@@ -2888,6 +2888,7 @@ if (!is_android) { ...@@ -2888,6 +2888,7 @@ if (!is_android) {
deps += [ deps += [
"//chrome/browser/ui/ash/assistant/test_support", "//chrome/browser/ui/ash/assistant/test_support",
"//chromeos/assistant/internal:internal", "//chromeos/assistant/internal:internal",
"//chromeos/assistant/test_support",
] ]
data += [ "//chromeos/assistant/internal/test_data/" ] data += [ "//chromeos/assistant/internal/test_data/" ]
......
static_library("test_support") {
testonly = true
sources = [ "expect_utils.h" ]
deps = [
"//base",
"//base/test:test_support",
"//testing/gmock",
"//testing/gtest",
]
}
// Copyright 2021 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.
#ifndef CHROMEOS_ASSISTANT_TEST_SUPPORT_EXPECT_UTILS_H_
#define CHROMEOS_ASSISTANT_TEST_SUPPORT_EXPECT_UTILS_H_
#include "base/callback_forward.h"
#include "base/run_loop.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace chromeos {
namespace assistant {
namespace test {
namespace {
template <typename T>
void CheckResult(base::OnceClosure quit,
T expected_value,
base::RepeatingCallback<T()> value_callback) {
if (expected_value == value_callback.Run()) {
std::move(quit).Run();
return;
}
// Check again in the future
base::SequencedTaskRunnerHandle::Get()->PostDelayedTask(
FROM_HERE,
base::BindOnce(CheckResult<T>, std::move(quit), expected_value,
value_callback),
base::TimeDelta::FromMilliseconds(10));
}
} // namespace
// Check if the |expected_value| is equal to the result of running
// |value_callback|. This method will block and continuously try the
// comparison above until it succeeds, or timeout.
template <typename T>
void ExpectResult(T expected_value,
base::RepeatingCallback<T()> value_callback,
const std::string& tag = std::string()) {
// Wait until we're ready or we hit the default task environment timeout.
base::RunLoop run_loop;
CheckResult(run_loop.QuitClosure(), expected_value, value_callback);
EXPECT_NO_FATAL_FAILURE(run_loop.Run())
<< (tag.empty() ? tag : tag + ": ")
<< "Failed waiting for expected result.\n"
<< "Expected \"" << expected_value << "\"\n"
<< "Got \"" << value_callback.Run() << "\"";
}
#define WAIT_FOR_CALL(mock_obj, call) \
{ \
base::RunLoop run_loop; \
EXPECT_CALL(mock_obj, call).WillOnce([quit = run_loop.QuitClosure()]() { \
std::move(quit).Run(); \
}); \
EXPECT_NO_FATAL_FAILURE(run_loop.Run()) \
<< #mock_obj << "::" << #call << " is NOT called."; \
}
} // namespace test
} // namespace assistant
} // namespace chromeos
#endif // CHROMEOS_ASSISTANT_TEST_SUPPORT_EXPECT_UTILS_H_
...@@ -207,6 +207,7 @@ source_set("tests") { ...@@ -207,6 +207,7 @@ source_set("tests") {
"//chromeos/assistant/internal:test_support", "//chromeos/assistant/internal:test_support",
"//chromeos/assistant/internal:tests", "//chromeos/assistant/internal:tests",
"//chromeos/assistant/internal/proto/google3", "//chromeos/assistant/internal/proto/google3",
"//chromeos/assistant/test_support",
"//chromeos/dbus", "//chromeos/dbus",
"//chromeos/services/assistant/proxy", "//chromeos/services/assistant/proxy",
"//chromeos/services/assistant/public/cpp/migration", "//chromeos/services/assistant/public/cpp/migration",
......
...@@ -451,6 +451,7 @@ void AssistantManagerServiceImpl::UpdateInternalMediaPlayerStatus( ...@@ -451,6 +451,7 @@ void AssistantManagerServiceImpl::UpdateInternalMediaPlayerStatus(
void AssistantManagerServiceImpl::StartVoiceInteraction() { void AssistantManagerServiceImpl::StartVoiceInteraction() {
DCHECK(assistant_manager()); DCHECK(assistant_manager());
DVLOG(1) << __func__; DVLOG(1) << __func__;
MaybeStopPreviousInteraction();
audio_input_host_->SetMicState(true); audio_input_host_->SetMicState(true);
assistant_manager()->StartAssistantInteraction(); assistant_manager()->StartAssistantInteraction();
...@@ -465,8 +466,27 @@ void AssistantManagerServiceImpl::StopActiveInteraction( ...@@ -465,8 +466,27 @@ void AssistantManagerServiceImpl::StopActiveInteraction(
VLOG(1) << "Stopping interaction without assistant manager."; VLOG(1) << "Stopping interaction without assistant manager.";
return; return;
} }
assistant_manager_internal()->StopAssistantInteractionInternal(
cancel_conversation); // We do not stop the interaction immediately, but instead we give
// Libassistant a bit of time to stop on its own accord. This improves
// stability as Libassistant might misbehave when it's forcefully stopped.
auto stop_callback = [](base::WeakPtr<AssistantManagerServiceImpl> weak_this,
bool cancel_conversation) {
if (!weak_this || !weak_this->assistant_manager_internal()) {
return;
}
VLOG(1) << "Stopping interaction.";
weak_this->assistant_manager_internal()->StopAssistantInteractionInternal(
cancel_conversation);
};
stop_interaction_closure_ =
std::make_unique<base::CancelableOnceClosure>(base::BindOnce(
stop_callback, weak_factory_.GetWeakPtr(), cancel_conversation));
main_task_runner()->PostDelayedTask(FROM_HERE,
stop_interaction_closure_->callback(),
stop_interactioin_delay_);
} }
void AssistantManagerServiceImpl::StartEditReminderInteraction( void AssistantManagerServiceImpl::StartEditReminderInteraction(
...@@ -508,6 +528,8 @@ void AssistantManagerServiceImpl::StartTextInteraction( ...@@ -508,6 +528,8 @@ void AssistantManagerServiceImpl::StartTextInteraction(
bool allow_tts) { bool allow_tts) {
DVLOG(1) << __func__; DVLOG(1) << __func__;
MaybeStopPreviousInteraction();
conversation_controller_proxy().SendTextQuery( conversation_controller_proxy().SendTextQuery(
query, allow_tts, query, allow_tts,
NewPendingInteraction(AssistantInteractionType::kText, source, query)); NewPendingInteraction(AssistantInteractionType::kText, source, query));
...@@ -569,6 +591,8 @@ void AssistantManagerServiceImpl::OnConversationTurnStartedInternal( ...@@ -569,6 +591,8 @@ void AssistantManagerServiceImpl::OnConversationTurnStartedInternal(
&AssistantManagerServiceImpl::OnConversationTurnStartedInternal, &AssistantManagerServiceImpl::OnConversationTurnStartedInternal,
metadata); metadata);
stop_interaction_closure_.reset();
audio_input_host_->OnConversationTurnStarted(); audio_input_host_->OnConversationTurnStarted();
// Retrieve the cached interaction metadata associated with this conversation // Retrieve the cached interaction metadata associated with this conversation
...@@ -594,6 +618,8 @@ void AssistantManagerServiceImpl::OnConversationTurnFinished( ...@@ -594,6 +618,8 @@ void AssistantManagerServiceImpl::OnConversationTurnFinished(
ENSURE_MAIN_THREAD(&AssistantManagerServiceImpl::OnConversationTurnFinished, ENSURE_MAIN_THREAD(&AssistantManagerServiceImpl::OnConversationTurnFinished,
resolution); resolution);
stop_interaction_closure_.reset();
// TODO(updowndota): Find a better way to handle the edge cases. // TODO(updowndota): Find a better way to handle the edge cases.
if (resolution != Resolution::NORMAL_WITH_FOLLOW_ON && if (resolution != Resolution::NORMAL_WITH_FOLLOW_ON &&
resolution != Resolution::CANCELLED && resolution != Resolution::CANCELLED &&
...@@ -1229,6 +1255,14 @@ void AssistantManagerServiceImpl::SendVoicelessInteraction( ...@@ -1229,6 +1255,14 @@ void AssistantManagerServiceImpl::SendVoicelessInteraction(
interaction, description, voiceless_options, [](auto) {}); interaction, description, voiceless_options, [](auto) {});
} }
void AssistantManagerServiceImpl::MaybeStopPreviousInteraction() {
if (!stop_interaction_closure_ || stop_interaction_closure_->IsCancelled()) {
return;
}
stop_interaction_closure_->callback().Run();
}
std::string AssistantManagerServiceImpl::GetLastSearchSource() { std::string AssistantManagerServiceImpl::GetLastSearchSource() {
return ConsumeLastTriggerSource(); return ConsumeLastTriggerSource();
} }
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include <vector> #include <vector>
#include "ash/public/cpp/assistant/controller/assistant_screen_context_controller.h" #include "ash/public/cpp/assistant/controller/assistant_screen_context_controller.h"
#include "base/cancelable_callback.h"
#include "base/memory/scoped_refptr.h" #include "base/memory/scoped_refptr.h"
#include "base/observer_list.h" #include "base/observer_list.h"
#include "base/optional.h" #include "base/optional.h"
...@@ -293,6 +294,8 @@ class COMPONENT_EXPORT(ASSISTANT_SERVICE) AssistantManagerServiceImpl ...@@ -293,6 +294,8 @@ class COMPONENT_EXPORT(ASSISTANT_SERVICE) AssistantManagerServiceImpl
const std::string& description, const std::string& description,
bool is_user_initiated); bool is_user_initiated);
void MaybeStopPreviousInteraction();
ash::AssistantAlarmTimerController* assistant_alarm_timer_controller(); ash::AssistantAlarmTimerController* assistant_alarm_timer_controller();
ash::AssistantNotificationController* assistant_notification_controller(); ash::AssistantNotificationController* assistant_notification_controller();
ash::AssistantScreenContextController* assistant_screen_context_controller(); ash::AssistantScreenContextController* assistant_screen_context_controller();
...@@ -305,6 +308,9 @@ class COMPONENT_EXPORT(ASSISTANT_SERVICE) AssistantManagerServiceImpl ...@@ -305,6 +308,9 @@ class COMPONENT_EXPORT(ASSISTANT_SERVICE) AssistantManagerServiceImpl
ServiceControllerProxy& service_controller(); ServiceControllerProxy& service_controller();
const ServiceControllerProxy& service_controller() const; const ServiceControllerProxy& service_controller() const;
base::Thread& background_thread(); base::Thread& background_thread();
void set_stop_interaction_delay_for_testing(base::TimeDelta delay) {
stop_interactioin_delay_ = delay;
}
void SetStateAndInformObservers(State new_state); void SetStateAndInformObservers(State new_state);
...@@ -357,6 +363,10 @@ class COMPONENT_EXPORT(ASSISTANT_SERVICE) AssistantManagerServiceImpl ...@@ -357,6 +363,10 @@ class COMPONENT_EXPORT(ASSISTANT_SERVICE) AssistantManagerServiceImpl
// Configuration passed to libassistant. // Configuration passed to libassistant.
std::string libassistant_config_; std::string libassistant_config_;
base::TimeDelta stop_interactioin_delay_ =
base::TimeDelta::FromMilliseconds(500);
std::unique_ptr<base::CancelableOnceClosure> stop_interaction_closure_;
base::ScopedObservation<DeviceActions, base::ScopedObservation<DeviceActions,
AppListEventSubscriber, AppListEventSubscriber,
&DeviceActions::AddAndFireAppListEventSubscriber, &DeviceActions::AddAndFireAppListEventSubscriber,
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
#include "chromeos/assistant/internal/test_support/fake_alarm_timer_manager.h" #include "chromeos/assistant/internal/test_support/fake_alarm_timer_manager.h"
#include "chromeos/assistant/internal/test_support/fake_assistant_manager.h" #include "chromeos/assistant/internal/test_support/fake_assistant_manager.h"
#include "chromeos/assistant/internal/test_support/fake_assistant_manager_internal.h" #include "chromeos/assistant/internal/test_support/fake_assistant_manager_internal.h"
#include "chromeos/assistant/test_support/expect_utils.h"
#include "chromeos/dbus/power/fake_power_manager_client.h" #include "chromeos/dbus/power/fake_power_manager_client.h"
#include "chromeos/services/assistant/assistant_manager_service.h" #include "chromeos/services/assistant/assistant_manager_service.h"
#include "chromeos/services/assistant/proxy/libassistant_service_host.h" #include "chromeos/services/assistant/proxy/libassistant_service_host.h"
...@@ -62,17 +63,15 @@ const char* kNoValue = FakeAssistantManager::kNoValue; ...@@ -62,17 +63,15 @@ const char* kNoValue = FakeAssistantManager::kNoValue;
EXPECT_EQ(_state, assistant_manager_service()->GetState()); EXPECT_EQ(_state, assistant_manager_service()->GetState());
// Adds an AlarmTimerEvent of the given |type| to |events|. // Adds an AlarmTimerEvent of the given |type| to |events|.
static void AddAlarmTimerEvent( void AddAlarmTimerEvent(std::vector<assistant_client::AlarmTimerEvent>* events,
std::vector<assistant_client::AlarmTimerEvent>* events, assistant_client::AlarmTimerEvent::Type type) {
assistant_client::AlarmTimerEvent::Type type) {
events->push_back(assistant_client::AlarmTimerEvent()); events->push_back(assistant_client::AlarmTimerEvent());
events->back().type = type; events->back().type = type;
} }
// Adds an AlarmTimerEvent of type TIMER with the given |state| to |events|. // Adds an AlarmTimerEvent of type TIMER with the given |state| to |events|.
static void AddTimerEvent( void AddTimerEvent(std::vector<assistant_client::AlarmTimerEvent>* events,
std::vector<assistant_client::AlarmTimerEvent>* events, assistant_client::Timer::State state) {
assistant_client::Timer::State state) {
AddAlarmTimerEvent(events, assistant_client::AlarmTimerEvent::TIMER); AddAlarmTimerEvent(events, assistant_client::AlarmTimerEvent::TIMER);
events->back().timer_data.state = state; events->back().timer_data.state = state;
} }
...@@ -81,7 +80,7 @@ static void AddTimerEvent( ...@@ -81,7 +80,7 @@ static void AddTimerEvent(
// authentication errors. This list is created on demand as there is no clear // authentication errors. This list is created on demand as there is no clear
// enum that defines these, and we don't want to hard code this list in the // enum that defines these, and we don't want to hard code this list in the
// test. // test.
static std::vector<int> GetAuthenticationErrorCodes() { std::vector<int> GetAuthenticationErrorCodes() {
const int kMinErrorCode = GetLowestErrorCode(); const int kMinErrorCode = GetLowestErrorCode();
const int kMaxErrorCode = GetHighestErrorCode(); const int kMaxErrorCode = GetHighestErrorCode();
...@@ -97,58 +96,10 @@ static std::vector<int> GetAuthenticationErrorCodes() { ...@@ -97,58 +96,10 @@ static std::vector<int> GetAuthenticationErrorCodes() {
// Return a list of some libassistant error codes that are not considered to be // Return a list of some libassistant error codes that are not considered to be
// authentication errors. Note we do not return all such codes as there are // authentication errors. Note we do not return all such codes as there are
// simply too many and testing them all significantly slows down the tests. // simply too many and testing them all significantly slows down the tests.
static std::vector<int> GetNonAuthenticationErrorCodes() { std::vector<int> GetNonAuthenticationErrorCodes() {
return {-99999, 0, 1}; return {-99999, 0, 1};
} }
// Waits until the AssistantManagerService is in the given state, or until the
// timeout is hit.
class StateWaiter : private AssistantManagerService::StateObserver {
public:
const base::TimeDelta kDefaultTimeout = base::TimeDelta::FromSeconds(5);
explicit StateWaiter(AssistantManagerService* service) : service_(service) {
service_->AddAndFireStateObserver(this);
}
~StateWaiter() override { service_->RemoveStateObserver(this); }
void RunUntilState(AssistantManagerService::State expected_state) {
if (current_state() == expected_state)
return;
expected_state_ = expected_state;
// Ensure we time out after kDefaultTimeout.
base::test::ScopedRunLoopTimeout timeout(FROM_HERE, kDefaultTimeout);
base::RunLoop run_loop;
base::AutoReset<base::OnceClosure> quit_loop(&quit_loop_,
run_loop.QuitClosure());
// And wait until we hit the expected state.
EXPECT_NO_FATAL_FAILURE(run_loop.Run())
<< "Failed waiting for AssistantManagerService::State change."
<< " Expected state " << expected_state_ << " but have "
<< current_state();
}
private:
// StateObserver implementation:
void OnStateChanged(AssistantManagerService::State new_state) override {
if (quit_loop_ && (new_state == expected_state_))
std::move(quit_loop_).Run();
}
AssistantManagerService::State current_state() {
return service_->GetState();
}
AssistantManagerService* const service_;
AssistantManagerService::State expected_state_;
base::OnceClosure quit_loop_;
};
class AssistantAlarmTimerControllerMock class AssistantAlarmTimerControllerMock
: public ash::AssistantAlarmTimerController { : public ash::AssistantAlarmTimerController {
public: public:
...@@ -218,6 +169,13 @@ class StateObserverMock : public AssistantManagerService::StateObserver { ...@@ -218,6 +169,13 @@ class StateObserverMock : public AssistantManagerService::StateObserver {
DISALLOW_COPY_AND_ASSIGN(StateObserverMock); DISALLOW_COPY_AND_ASSIGN(StateObserverMock);
}; };
class FakeLibassistantV1Api : public LibassistantV1Api {
public:
explicit FakeLibassistantV1Api(FakeAssistantManager* assistant_manager)
: LibassistantV1Api(assistant_manager,
&assistant_manager->assistant_manager_internal()) {}
};
class AssistantManagerServiceImplTest : public testing::Test { class AssistantManagerServiceImplTest : public testing::Test {
public: public:
AssistantManagerServiceImplTest() = default; AssistantManagerServiceImplTest() = default;
...@@ -238,11 +196,11 @@ class AssistantManagerServiceImplTest : public testing::Test { ...@@ -238,11 +196,11 @@ class AssistantManagerServiceImplTest : public testing::Test {
service_context_ = std::make_unique<FakeServiceContext>(); service_context_ = std::make_unique<FakeServiceContext>();
service_context_ service_context_
->set_main_task_runner(task_environment.GetMainThreadTaskRunner()) ->set_main_task_runner(task_environment().GetMainThreadTaskRunner())
.set_power_manager_client(PowerManagerClient::Get()) .set_power_manager_client(PowerManagerClient::Get())
.set_assistant_state(&assistant_state_); .set_assistant_state(&assistant_state_);
CreateAssistantManagerServiceImpl(/*libassistant_config=*/{}); CreateAssistantManagerServiceImpl();
} }
void CreateAssistantManagerServiceImpl( void CreateAssistantManagerServiceImpl(
...@@ -274,7 +232,9 @@ class AssistantManagerServiceImplTest : public testing::Test { ...@@ -274,7 +232,9 @@ class AssistantManagerServiceImplTest : public testing::Test {
ash::AssistantState* assistant_state() { return &assistant_state_; } ash::AssistantState* assistant_state() { return &assistant_state_; }
FakeAssistantManager* fake_assistant_manager() { return &assistant_manager_; } FakeAssistantManager* fake_assistant_manager() {
return assistant_manager_.get();
}
FakeAssistantManagerInternal* fake_assistant_manager_internal() { FakeAssistantManagerInternal* fake_assistant_manager_internal() {
return &fake_assistant_manager()->assistant_manager_internal(); return &fake_assistant_manager()->assistant_manager_internal();
...@@ -291,6 +251,8 @@ class AssistantManagerServiceImplTest : public testing::Test { ...@@ -291,6 +251,8 @@ class AssistantManagerServiceImplTest : public testing::Test {
return assistant_manager_service_->action_module_for_testing(); return assistant_manager_service_->action_module_for_testing();
} }
base::test::TaskEnvironment& task_environment() { return task_environment_; }
void Start() { void Start() {
assistant_manager_service()->Start(UserInfo("<user-id>", "<access-token>"), assistant_manager_service()->Start(UserInfo("<user-id>", "<access-token>"),
/*enable_hotword=*/false); /*enable_hotword=*/false);
...@@ -307,8 +269,11 @@ class AssistantManagerServiceImplTest : public testing::Test { ...@@ -307,8 +269,11 @@ class AssistantManagerServiceImplTest : public testing::Test {
} }
void WaitForState(AssistantManagerService::State expected_state) { void WaitForState(AssistantManagerService::State expected_state) {
StateWaiter waiter(assistant_manager_service()); test::ExpectResult(
waiter.RunUntilState(expected_state); expected_state,
base::BindRepeating(&AssistantManagerServiceImpl::GetState,
base::Unretained(assistant_manager_service())),
"AssistantManagerStateImpl");
} }
// Raise all the |libassistant_error_codes| as communication errors from // Raise all the |libassistant_error_codes| as communication errors from
...@@ -338,8 +303,25 @@ class AssistantManagerServiceImplTest : public testing::Test { ...@@ -338,8 +303,25 @@ class AssistantManagerServiceImplTest : public testing::Test {
} }
} }
void SetAssistantManagerInternal(std::unique_ptr<FakeAssistantManagerInternal>
assistant_manager_internal) {
assistant_manager_->set_assistant_manager_internal(
std::move(assistant_manager_internal));
libassistant_v1_api_.reset();
libassistant_v1_api_ =
std::make_unique<FakeLibassistantV1Api>(assistant_manager_.get());
}
void SetAssistantManager(
std::unique_ptr<FakeAssistantManager> assistant_manager) {
assistant_manager_ = std::move(assistant_manager);
libassistant_v1_api_.reset();
libassistant_v1_api_ =
std::make_unique<FakeLibassistantV1Api>(assistant_manager_.get());
}
private: private:
base::test::SingleThreadTaskEnvironment task_environment; base::test::SingleThreadTaskEnvironment task_environment_;
ScopedAssistantClient assistant_client_; ScopedAssistantClient assistant_client_;
ScopedDeviceActions device_actions_; ScopedDeviceActions device_actions_;
...@@ -348,9 +330,10 @@ class AssistantManagerServiceImplTest : public testing::Test { ...@@ -348,9 +330,10 @@ class AssistantManagerServiceImplTest : public testing::Test {
// Fake implementation of the Libassistant Mojom service. // Fake implementation of the Libassistant Mojom service.
FakeLibassistantService libassistant_service_; FakeLibassistantService libassistant_service_;
FakeAssistantManager assistant_manager_; std::unique_ptr<FakeAssistantManager> assistant_manager_{
LibassistantV1Api libassistant_v1_api_{ std::make_unique<FakeAssistantManager>()};
&assistant_manager_, &assistant_manager_.assistant_manager_internal()}; std::unique_ptr<FakeLibassistantV1Api> libassistant_v1_api_{
std::make_unique<FakeLibassistantV1Api>(assistant_manager_.get())};
std::unique_ptr<FakeServiceContext> service_context_; std::unique_ptr<FakeServiceContext> service_context_;
...@@ -749,5 +732,71 @@ TEST_F(AssistantManagerServiceImplTest, ...@@ -749,5 +732,71 @@ TEST_F(AssistantManagerServiceImplTest,
assistant_manager_service()->OnStartFinished(); assistant_manager_service()->OnStartFinished();
} }
class AssistantManagerMock : public FakeAssistantManager {
public:
AssistantManagerMock() = default;
~AssistantManagerMock() override = default;
MOCK_METHOD(void, StartAssistantInteraction, (), (override));
};
class AssistantManagerInternalMock : public FakeAssistantManagerInternal {
public:
AssistantManagerInternalMock() = default;
~AssistantManagerInternalMock() override = default;
MOCK_METHOD(void, StopAssistantInteractionInternal, (bool), (override));
};
TEST_F(AssistantManagerServiceImplTest, ShouldStopInteractionAfterDelay) {
// Start LibAssistant.
Start();
WaitForState(AssistantManagerService::STARTED);
auto assistant_manager_internal_mock =
std::make_unique<AssistantManagerInternalMock>();
auto* mock_ptr = assistant_manager_internal_mock.get();
SetAssistantManagerInternal(std::move(assistant_manager_internal_mock));
EXPECT_CALL(*mock_ptr, StopAssistantInteractionInternal).Times(0);
assistant_manager_service()->StopActiveInteraction(true);
testing::Mock::VerifyAndClearExpectations(mock_ptr);
WAIT_FOR_CALL(*mock_ptr, StopAssistantInteractionInternal);
}
TEST_F(AssistantManagerServiceImplTest,
ShouldStopInteractionImmediatelyBeforeNewInteraction) {
// Start LibAssistant.
Start();
WaitForState(AssistantManagerService::STARTED);
auto assistant_manager_mock = std::make_unique<AssistantManagerMock>();
auto assistant_manager_internal_mock =
std::make_unique<AssistantManagerInternalMock>();
auto* assistant_manager_mock_ptr = assistant_manager_mock.get();
auto* assistant_manager_internal_mock_ptr =
assistant_manager_internal_mock.get();
assistant_manager_mock->set_assistant_manager_internal(
std::move(assistant_manager_internal_mock));
SetAssistantManager(std::move(assistant_manager_mock));
EXPECT_CALL(*assistant_manager_internal_mock_ptr,
StopAssistantInteractionInternal)
.Times(0);
assistant_manager_service()->StopActiveInteraction(true);
testing::Mock::VerifyAndClearExpectations(
assistant_manager_internal_mock_ptr);
EXPECT_CALL(*assistant_manager_internal_mock_ptr,
StopAssistantInteractionInternal)
.Times(1);
EXPECT_CALL(*assistant_manager_mock_ptr, StartAssistantInteraction).Times(1);
assistant_manager_service()->StartVoiceInteraction();
}
} // namespace assistant } // namespace assistant
} // namespace chromeos } // namespace chromeos
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment