blob: f2544d3299159b8ee1f97fc3fbd16d95d26769ed [file] [log] [blame]
// Copyright 2020 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/public/cpp/autotest_ambient_api.h"
#include <utility>
#include "ash/ambient/ambient_controller.h"
#include "ash/ambient/metrics/ambient_metrics.h"
#include "ash/ambient/model/ambient_photo_config.h"
#include "ash/ambient/ui/ambient_view_delegate.h"
#include "ash/ambient/ui/ambient_view_ids.h"
#include "ash/public/cpp/ambient/ambient_backend_controller.h"
#include "ash/public/cpp/ash_web_view.h"
#include "ash/root_window_controller.h"
#include "ash/shell.h"
#include "base/check.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/memory/raw_ptr.h"
#include "base/task/sequenced_task_runner.h"
#include "base/time/default_tick_clock.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "ui/views/view.h"
#include "ui/views/widget/widget.h"
namespace ash {
namespace {
class PhotoTransitionAnimationObserver : public AmbientViewDelegateObserver {
public:
PhotoTransitionAnimationObserver(int num_completions,
base::TimeDelta timeout,
base::OnceClosure on_complete,
base::OnceClosure on_timeout)
: num_completions_(num_completions),
on_complete_(std::move(on_complete)),
on_timeout_(std::move(on_timeout)) {
DCHECK_GT(num_completions, 0);
DCHECK_GT(timeout, base::TimeDelta());
DCHECK(on_complete_);
DCHECK(on_timeout_);
// |base::Unretained| is safe here because this timer will be abandoned in
// the destructor.
timer_.Start(FROM_HERE, timeout,
base::BindOnce(&PhotoTransitionAnimationObserver::OnTimeout,
base::Unretained(this)));
scoped_observation_.Observe(
Shell::Get()->ambient_controller()->ambient_view_delegate());
}
PhotoTransitionAnimationObserver(const PhotoTransitionAnimationObserver&) =
delete;
PhotoTransitionAnimationObserver& operator=(
const PhotoTransitionAnimationObserver&) = delete;
~PhotoTransitionAnimationObserver() override = default;
// AmbientViewDelegateObserver:
void OnMarkerHit(AmbientPhotoConfig::Marker marker) override {
if (marker != AmbientPhotoConfig::Marker::kUiCycleEnded)
return;
--num_completions_;
if (num_completions_ == 0) {
Cleanup();
std::move(on_complete_).Run();
delete this;
}
}
private:
void OnTimeout() {
Cleanup();
std::move(on_timeout_).Run();
delete this;
}
void Cleanup() {
timer_.AbandonAndStop();
scoped_observation_.Reset();
}
int num_completions_;
base::OnceClosure on_complete_;
base::OnceClosure on_timeout_;
base::OneShotTimer timer_;
base::ScopedObservation<AmbientViewDelegate, AmbientViewDelegateObserver>
scoped_observation_{this};
};
// Parameters needed to complete one call to `WaitForVideoToStart()`. They get
// forwarded through the async sequence of functions below until `on_complete`
// or `on_error` is run.
struct VideoPlaybackStatusTestParams {
// Time at which the call to `WaitForVideoToStart()` was made.
base::TimeTicks start_time;
base::TimeDelta timeout;
base::OnceClosure on_complete;
base::OnceCallback<void(std::string)> on_error;
// Never null. Points to default clock if a testing clock was not provided.
raw_ptr<const base::TickClock> tick_clock;
};
void ScheduleVideoPlaybackStatusCheck(VideoPlaybackStatusTestParams params);
void OnVideoPlaybackStatusReceived(VideoPlaybackStatusTestParams params,
ambient::AmbientVideoSessionStatus status);
// Polls the ambient video view once every second to see if playback has started
// successfully. The signal it uses is the "playback_started" field in the
// video view's URL that gets set when the <video> element has started playback.
// This coincidentally is the same signal that's used for metrics purposes.
void CheckVideoPlaybackStatusForTesting(VideoPlaybackStatusTestParams params) {
CHECK(params.tick_clock);
if (params.tick_clock->NowTicks() - params.start_time >= params.timeout) {
std::move(params.on_error)
.Run("Timed out waiting for ambient video playback to start.");
return;
}
views::Widget* ambient_widget =
Shell::Get()
->GetPrimaryRootWindowController()
->ambient_widget_for_testing(); // IN-TEST
if (!ambient_widget) {
DVLOG(4) << "Ambient session not active yet";
ScheduleVideoPlaybackStatusCheck(std::move(params));
return;
}
AshWebView* video_web_view = static_cast<AshWebView*>(
ambient_widget->GetContentsView()->GetViewByID(kAmbientVideoWebView));
if (!video_web_view) {
std::move(params.on_error)
.Run(
"Video view missing from ambient widget. Video theme must be "
"inactive.");
return;
}
ambient::GetAmbientModeVideoSessionStatus(
video_web_view,
base::BindOnce(&OnVideoPlaybackStatusReceived, std::move(params)));
}
// Schedules the next polling check for whether video playback started.
void ScheduleVideoPlaybackStatusCheck(VideoPlaybackStatusTestParams params) {
static constexpr base::TimeDelta kPollingPeriod = base::Seconds(1);
base::SequencedTaskRunner::GetCurrentDefault()->PostDelayedTask(
FROM_HERE,
base::BindOnce(&CheckVideoPlaybackStatusForTesting, std::move(params)),
kPollingPeriod);
}
// Either runs one of the completion callbacks or schedules the next polling
// check if the the video is still loading.
void OnVideoPlaybackStatusReceived(VideoPlaybackStatusTestParams params,
ambient::AmbientVideoSessionStatus status) {
CHECK(params.on_complete && params.on_error);
switch (status) {
case ambient::AmbientVideoSessionStatus::kSuccess:
std::move(params.on_complete).Run();
break;
case ambient::AmbientVideoSessionStatus::kFailed:
std::move(params.on_error)
.Run(
"Ambient video playback failed with a hard error in the "
"webview.");
break;
case ambient::AmbientVideoSessionStatus::kLoading:
ScheduleVideoPlaybackStatusCheck(std::move(params));
break;
}
}
} // namespace
AutotestAmbientApi::AutotestAmbientApi() = default;
AutotestAmbientApi::~AutotestAmbientApi() = default;
void AutotestAmbientApi::WaitForPhotoTransitionAnimationCompleted(
int num_completions,
base::TimeDelta timeout,
base::OnceClosure on_complete,
base::OnceClosure on_timeout) {
new PhotoTransitionAnimationObserver(
num_completions, timeout, std::move(on_complete), std::move(on_timeout));
}
void AutotestAmbientApi::WaitForVideoToStart(
base::TimeDelta timeout,
base::OnceClosure on_complete,
base::OnceCallback<void(std::string)> on_error,
const base::TickClock* tick_clock) {
if (!tick_clock) {
tick_clock = base::DefaultTickClock::GetInstance();
}
CheckVideoPlaybackStatusForTesting({tick_clock->NowTicks(), timeout,
std::move(on_complete),
std::move(on_error), tick_clock});
}
} // namespace ash