1 // Copyright 2020 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "components/feed/core/v2/feed_stream.h"
6 
7 #include <memory>
8 #include <string>
9 #include <utility>
10 
11 #include "base/optional.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "base/test/bind_test_util.h"
14 #include "base/test/scoped_run_loop_timeout.h"
15 #include "base/test/simple_test_clock.h"
16 #include "base/test/simple_test_tick_clock.h"
17 #include "base/test/task_environment.h"
18 #include "base/threading/sequenced_task_runner_handle.h"
19 #include "components/feed/core/common/pref_names.h"
20 #include "components/feed/core/proto/v2/store.pb.h"
21 #include "components/feed/core/proto/v2/ui.pb.h"
22 #include "components/feed/core/proto/v2/wire/request.pb.h"
23 #include "components/feed/core/shared_prefs/pref_names.h"
24 #include "components/feed/core/v2/feed_network.h"
25 #include "components/feed/core/v2/refresh_task_scheduler.h"
26 #include "components/feed/core/v2/scheduling.h"
27 #include "components/feed/core/v2/stream_model.h"
28 #include "components/feed/core/v2/stream_model_update_request.h"
29 #include "components/feed/core/v2/tasks/load_stream_from_store_task.h"
30 #include "components/feed/core/v2/test/stream_builder.h"
31 #include "components/leveldb_proto/public/proto_database_provider.h"
32 #include "components/prefs/pref_registry_simple.h"
33 #include "components/prefs/testing_pref_service.h"
34 #include "testing/gtest/include/gtest/gtest.h"
35 
36 namespace feed {
37 namespace {
38 
LoadModelFromStore(FeedStore * store)39 std::unique_ptr<StreamModel> LoadModelFromStore(FeedStore* store) {
40   LoadStreamFromStoreTask::Result result;
41   auto complete = [&](LoadStreamFromStoreTask::Result task_result) {
42     result = std::move(task_result);
43   };
44   LoadStreamFromStoreTask load_task(
45       store, /*clock=*/nullptr,
46       UserClass::kActiveSuggestionsConsumer,  // Has no effect.
47       base::BindLambdaForTesting(complete));
48   // We want to load the data no matter how stale.
49   load_task.IgnoreStalenessForTesting();
50 
51   base::RunLoop run_loop;
52   load_task.Execute(run_loop.QuitClosure());
53   run_loop.Run();
54 
55   if (result.status == LoadStreamStatus::kLoadedFromStore) {
56     auto model = std::make_unique<StreamModel>();
57     model->Update(std::move(result.update_request));
58     return model;
59   }
60   LOG(WARNING) << "LoadModelFromStore failed with " << result.status;
61   return nullptr;
62 }
63 
64 // Returns the model state string (|StreamModel::DumpStateForTesting()|),
65 // given a model initialized with |update_request| and having |operations|
66 // applied.
ModelStateFor(std::unique_ptr<StreamModelUpdateRequest> update_request,std::vector<feedstore::DataOperation> operations={},std::vector<feedstore::DataOperation> more_operations={})67 std::string ModelStateFor(
68     std::unique_ptr<StreamModelUpdateRequest> update_request,
69     std::vector<feedstore::DataOperation> operations = {},
70     std::vector<feedstore::DataOperation> more_operations = {}) {
71   StreamModel model;
72   model.Update(std::move(update_request));
73   model.ExecuteOperations(operations);
74   model.ExecuteOperations(more_operations);
75   return model.DumpStateForTesting();
76 }
77 
78 // Returns the model state string (|StreamModel::DumpStateForTesting()|),
79 // given a model initialized with |store|.
ModelStateFor(FeedStore * store)80 std::string ModelStateFor(FeedStore* store) {
81   auto model = LoadModelFromStore(store);
82   if (model) {
83     return model->DumpStateForTesting();
84   }
85   return "{Failed to load model from store}";
86 }
87 
88 // This is EXPECT_EQ, but also dumps the string values for ease of reading.
89 #define EXPECT_STRINGS_EQUAL(WANT, GOT)                                       \
90   {                                                                           \
91     std::string want = (WANT), got = (GOT);                                   \
92     EXPECT_EQ(want, got) << "Wanted:\n" << (want) << "\nBut got:\n" << (got); \
93   }
94 
95 class TestSurface : public FeedStream::SurfaceInterface {
96  public:
97   // FeedStream::SurfaceInterface.
StreamUpdate(const feedui::StreamUpdate & stream_update)98   void StreamUpdate(const feedui::StreamUpdate& stream_update) override {
99     if (!initial_state)
100       initial_state = stream_update;
101     update = stream_update;
102     ++update_count_;
103   }
104 
105   // Test functions.
106 
Clear()107   void Clear() {
108     initial_state = base::nullopt;
109     update = base::nullopt;
110     update_count_ = 0;
111   }
112 
113   // Describe what is shown on the surface in a format that can be easily
114   // asserted against.
Describe()115   std::string Describe() {
116     if (!initial_state)
117       return "empty";
118 
119     if (update->updated_slices().size() == 1 &&
120         update->updated_slices()[0].has_slice() &&
121         update->updated_slices()[0].slice().has_zero_state_slice()) {
122       return "zero-state";
123     }
124 
125     std::stringstream ss;
126     ss << update->updated_slices().size() << " slices";
127     // If there's more than one update, we want to know that.
128     if (update_count_ > 1) {
129       ss << " " << update_count_ << " updates";
130     }
131     return ss.str();
132   }
133 
134   base::Optional<feedui::StreamUpdate> initial_state;
135   base::Optional<feedui::StreamUpdate> update;
136 
137  private:
138   int update_count_ = 0;
139 };
140 
141 class TestUserClassifier : public UserClassifier {
142  public:
TestUserClassifier(PrefService * pref_service,const base::Clock * clock)143   TestUserClassifier(PrefService* pref_service, const base::Clock* clock)
144       : UserClassifier(pref_service, clock) {}
145   // UserClassifier.
GetUserClass() const146   UserClass GetUserClass() const override {
147     return overridden_user_class_.value_or(UserClassifier::GetUserClass());
148   }
149 
150   // Test use.
OverrideUserClass(UserClass user_class)151   void OverrideUserClass(UserClass user_class) {
152     overridden_user_class_ = user_class;
153   }
154 
155  private:
156   base::Optional<UserClass> overridden_user_class_;
157 };
158 
159 class TestFeedNetwork : public FeedNetwork {
160  public:
161   // FeedNetwork implementation.
SendQueryRequest(const feedwire::Request & request,base::OnceCallback<void (QueryRequestResult)> callback)162   void SendQueryRequest(
163       const feedwire::Request& request,
164       base::OnceCallback<void(QueryRequestResult)> callback) override {
165     ++send_query_call_count;
166     // Emulate a successful response.
167     // The response body is currently an empty message, because most of the
168     // time we want to inject a translated response for ease of test-writing.
169     query_request_sent = request;
170     QueryRequestResult result;
171     result.status_code = 200;
172     result.response_body = std::make_unique<feedwire::Response>();
173     base::SequencedTaskRunnerHandle::Get()->PostTask(
174         FROM_HERE, base::BindOnce(std::move(callback), std::move(result)));
175   }
SendActionRequest(const feedwire::ActionRequest & request,base::OnceCallback<void (ActionRequestResult)> callback)176   void SendActionRequest(
177       const feedwire::ActionRequest& request,
178       base::OnceCallback<void(ActionRequestResult)> callback) override {
179     NOTIMPLEMENTED();
180   }
CancelRequests()181   void CancelRequests() override { NOTIMPLEMENTED(); }
182 
183   base::Optional<feedwire::Request> query_request_sent;
184   int send_query_call_count = 0;
185 };
186 
187 // Forwards to |FeedStream::WireResponseTranslator| unless a response is
188 // injected.
189 class TestWireResponseTranslator : public FeedStream::WireResponseTranslator {
190  public:
TranslateWireResponse(feedwire::Response response,base::TimeDelta response_time,base::Time current_time)191   std::unique_ptr<StreamModelUpdateRequest> TranslateWireResponse(
192       feedwire::Response response,
193       base::TimeDelta response_time,
194       base::Time current_time) override {
195     if (injected_response_) {
196       return std::move(injected_response_);
197     }
198     return FeedStream::WireResponseTranslator::TranslateWireResponse(
199         std::move(response), response_time, current_time);
200   }
InjectResponse(std::unique_ptr<StreamModelUpdateRequest> response)201   void InjectResponse(std::unique_ptr<StreamModelUpdateRequest> response) {
202     injected_response_ = std::move(response);
203   }
InjectedResponseConsumed() const204   bool InjectedResponseConsumed() const { return !injected_response_; }
205 
206  private:
207   std::unique_ptr<StreamModelUpdateRequest> injected_response_;
208 };
209 
210 class FakeRefreshTaskScheduler : public RefreshTaskScheduler {
211  public:
212   // RefreshTaskScheduler implementation.
EnsureScheduled(base::TimeDelta period)213   void EnsureScheduled(base::TimeDelta period) override {
214     scheduled_period = period;
215   }
Cancel()216   void Cancel() override { canceled = true; }
RefreshTaskComplete()217   void RefreshTaskComplete() override { refresh_task_complete = true; }
218 
219   base::Optional<base::TimeDelta> scheduled_period;
220   bool canceled = false;
221   bool refresh_task_complete = false;
222 };
223 
224 class TestEventObserver : public FeedStream::EventObserver {
225  public:
226   // FeedStreamUnittest::StreamEventObserver.
OnLoadStream(LoadStreamStatus load_from_store_status,LoadStreamStatus final_status)227   void OnLoadStream(LoadStreamStatus load_from_store_status,
228                     LoadStreamStatus final_status) override {
229     load_stream_status = final_status;
230     LOG(INFO) << "OnLoadStream: " << final_status
231               << " (store status: " << load_from_store_status << ")";
232   }
OnMaybeTriggerRefresh(TriggerType trigger,bool clear_all_before_refresh)233   void OnMaybeTriggerRefresh(TriggerType trigger,
234                              bool clear_all_before_refresh) override {
235     refresh_trigger_type = trigger;
236   }
OnClearAll(base::TimeDelta time_since_last_clear)237   void OnClearAll(base::TimeDelta time_since_last_clear) override {
238     this->time_since_last_clear = time_since_last_clear;
239   }
240 
241   // Test access.
242 
243   base::Optional<LoadStreamStatus> load_stream_status;
244   base::Optional<base::TimeDelta> time_since_last_clear;
245   base::Optional<TriggerType> refresh_trigger_type;
246 };
247 
248 class FeedStreamTest : public testing::Test, public FeedStream::Delegate {
249  public:
SetUp()250   void SetUp() override {
251     feed::prefs::RegisterFeedSharedProfilePrefs(profile_prefs_.registry());
252     feed::RegisterProfilePrefs(profile_prefs_.registry());
253     CHECK_EQ(kTestTimeEpoch, task_environment_.GetMockClock()->Now());
254     stream_ = std::make_unique<FeedStream>(
255         &refresh_scheduler_, &event_observer_, this, &profile_prefs_, &network_,
256         store_.get(), task_environment_.GetMockClock(),
257         task_environment_.GetMockTickClock(),
258         task_environment_.GetMainThreadTaskRunner());
259 
260     // Set the user classifier.
261     auto user_classifier = std::make_unique<TestUserClassifier>(
262         &profile_prefs_, task_environment_.GetMockClock());
263     user_classifier_ = user_classifier.get();
264     stream_->SetUserClassifierForTesting(std::move(user_classifier));
265 
266     WaitForIdleTaskQueue();  // Wait for any initialization.
267 
268     stream_->SetWireResponseTranslatorForTesting(&response_translator_);
269   }
270 
TearDown()271   void TearDown() override {
272     // Ensure the task queue can return to idle. Failure to do so may be due
273     // to a stuck task that never called |TaskComplete()|.
274     WaitForIdleTaskQueue();
275     // Store requires PostTask to clean up.
276     store_.reset();
277     task_environment_.RunUntilIdle();
278   }
279 
280   // FeedStream::Delegate.
IsEulaAccepted()281   bool IsEulaAccepted() override { return is_eula_accepted_; }
IsOffline()282   bool IsOffline() override { return is_offline_; }
283 
284   // For tests.
285 
IsTaskQueueIdle() const286   bool IsTaskQueueIdle() const {
287     return !stream_->GetTaskQueueForTesting()->HasPendingTasks() &&
288            !stream_->GetTaskQueueForTesting()->HasRunningTask();
289   }
290 
WaitForIdleTaskQueue()291   void WaitForIdleTaskQueue() {
292     if (IsTaskQueueIdle())
293       return;
294     base::test::ScopedRunLoopTimeout run_timeout(
295         FROM_HERE, base::TimeDelta::FromSeconds(1));
296     base::RunLoop run_loop;
297     stream_->SetIdleCallbackForTesting(run_loop.QuitClosure());
298     run_loop.Run();
299   }
300 
UnloadModel()301   void UnloadModel() {
302     WaitForIdleTaskQueue();
303     stream_->UnloadModelForTesting();
304   }
305 
306  protected:
307   base::test::TaskEnvironment task_environment_{
308       base::test::TaskEnvironment::TimeSource::MOCK_TIME};
309   TestUserClassifier* user_classifier_;
310   TestEventObserver event_observer_;
311   TestingPrefServiceSimple profile_prefs_;
312   TestFeedNetwork network_;
313   TestWireResponseTranslator response_translator_;
314 
315   std::unique_ptr<FeedStore> store_ = std::make_unique<FeedStore>(
316       leveldb_proto::ProtoDatabaseProvider::GetUniqueDB<feedstore::Record>(
317           leveldb_proto::ProtoDbType::FEED_STREAM_DATABASE,
318           /*file_path=*/{},
319           task_environment_.GetMainThreadTaskRunner()));
320   FakeRefreshTaskScheduler refresh_scheduler_;
321   std::unique_ptr<FeedStream> stream_;
322   bool is_eula_accepted_ = true;
323   bool is_offline_ = false;
324 };
325 
TEST_F(FeedStreamTest,IsArticlesListVisibleByDefault)326 TEST_F(FeedStreamTest, IsArticlesListVisibleByDefault) {
327   EXPECT_TRUE(stream_->IsArticlesListVisible());
328 }
329 
TEST_F(FeedStreamTest,SetArticlesListVisible)330 TEST_F(FeedStreamTest, SetArticlesListVisible) {
331   EXPECT_TRUE(stream_->IsArticlesListVisible());
332   stream_->SetArticlesListVisible(false);
333   EXPECT_FALSE(stream_->IsArticlesListVisible());
334   stream_->SetArticlesListVisible(true);
335   EXPECT_TRUE(stream_->IsArticlesListVisible());
336 }
337 
TEST_F(FeedStreamTest,RefreshIsScheduledOnInitialize)338 TEST_F(FeedStreamTest, RefreshIsScheduledOnInitialize) {
339   stream_->InitializeScheduling();
340   EXPECT_TRUE(refresh_scheduler_.scheduled_period);
341 }
342 
TEST_F(FeedStreamTest,ScheduledRefreshTriggersRefresh)343 TEST_F(FeedStreamTest, ScheduledRefreshTriggersRefresh) {
344   stream_->InitializeScheduling();
345   stream_->ExecuteRefreshTask();
346 
347   EXPECT_EQ(TriggerType::kFixedTimer, event_observer_.refresh_trigger_type);
348   // TODO(harringtond): Once we actually perform the refresh, make sure
349   // RefreshTaskComplete() is called.
350   // EXPECT_TRUE(refresh_scheduler_.refresh_task_complete);
351 }
352 
TEST_F(FeedStreamTest,DoNotRefreshIfArticlesListIsHidden)353 TEST_F(FeedStreamTest, DoNotRefreshIfArticlesListIsHidden) {
354   stream_->SetArticlesListVisible(false);
355   stream_->InitializeScheduling();
356   stream_->ExecuteRefreshTask();
357 
358   EXPECT_TRUE(refresh_scheduler_.canceled);
359   EXPECT_FALSE(event_observer_.refresh_trigger_type);
360 }
361 
TEST_F(FeedStreamTest,SurfaceReceivesInitialContent)362 TEST_F(FeedStreamTest, SurfaceReceivesInitialContent) {
363   {
364     auto model = std::make_unique<StreamModel>();
365     model->Update(MakeTypicalInitialModelState());
366     stream_->LoadModelForTesting(std::move(model));
367   }
368   TestSurface surface;
369   stream_->AttachSurface(&surface);
370   ASSERT_TRUE(surface.initial_state);
371   const feedui::StreamUpdate& initial_state = surface.initial_state.value();
372   ASSERT_EQ(2, initial_state.updated_slices().size());
373   EXPECT_NE("", initial_state.updated_slices(0).slice().slice_id());
374   EXPECT_EQ("f:0", initial_state.updated_slices(0)
375                        .slice()
376                        .xsurface_slice()
377                        .xsurface_frame());
378   EXPECT_NE("", initial_state.updated_slices(1).slice().slice_id());
379   EXPECT_EQ("f:1", initial_state.updated_slices(1)
380                        .slice()
381                        .xsurface_slice()
382                        .xsurface_frame());
383   ASSERT_EQ(1, initial_state.new_shared_states().size());
384   EXPECT_EQ("ss:0",
385             initial_state.new_shared_states()[0].xsurface_shared_state());
386 }
387 
TEST_F(FeedStreamTest,SurfaceReceivesInitialContentLoadedAfterAttach)388 TEST_F(FeedStreamTest, SurfaceReceivesInitialContentLoadedAfterAttach) {
389   TestSurface surface;
390   stream_->AttachSurface(&surface);
391   ASSERT_FALSE(surface.initial_state);
392   {
393     auto model = std::make_unique<StreamModel>();
394     model->Update(MakeTypicalInitialModelState());
395     stream_->LoadModelForTesting(std::move(model));
396   }
397 
398   ASSERT_EQ("2 slices", surface.Describe());
399   const feedui::StreamUpdate& initial_state = surface.initial_state.value();
400 
401   EXPECT_NE("", initial_state.updated_slices(0).slice().slice_id());
402   EXPECT_EQ("f:0", initial_state.updated_slices(0)
403                        .slice()
404                        .xsurface_slice()
405                        .xsurface_frame());
406   EXPECT_NE("", initial_state.updated_slices(1).slice().slice_id());
407   EXPECT_EQ("f:1", initial_state.updated_slices(1)
408                        .slice()
409                        .xsurface_slice()
410                        .xsurface_frame());
411   ASSERT_EQ(1, initial_state.new_shared_states().size());
412   EXPECT_EQ("ss:0",
413             initial_state.new_shared_states()[0].xsurface_shared_state());
414 }
415 
TEST_F(FeedStreamTest,SurfaceReceivesUpdatedContent)416 TEST_F(FeedStreamTest, SurfaceReceivesUpdatedContent) {
417   {
418     auto model = std::make_unique<StreamModel>();
419     model->ExecuteOperations(MakeTypicalStreamOperations());
420     stream_->LoadModelForTesting(std::move(model));
421   }
422   TestSurface surface;
423   stream_->AttachSurface(&surface);
424   // Remove #1, add #2.
425   stream_->ExecuteOperations({
426       MakeOperation(MakeRemove(MakeClusterId(1))),
427       MakeOperation(MakeCluster(2, MakeRootId())),
428       MakeOperation(MakeContentNode(2, MakeClusterId(2))),
429       MakeOperation(MakeContent(2)),
430   });
431   ASSERT_TRUE(surface.update);
432   const feedui::StreamUpdate& initial_state = surface.initial_state.value();
433   const feedui::StreamUpdate& update = surface.update.value();
434 
435   ASSERT_EQ("2 slices 2 updates", surface.Describe());
436   // First slice is just an ID that matches the old 1st slice ID.
437   EXPECT_EQ(initial_state.updated_slices(0).slice().slice_id(),
438             update.updated_slices(0).slice_id());
439   // Second slice is a new xsurface slice.
440   EXPECT_NE("", update.updated_slices(1).slice().slice_id());
441   EXPECT_EQ("f:2",
442             update.updated_slices(1).slice().xsurface_slice().xsurface_frame());
443 }
444 
TEST_F(FeedStreamTest,SurfaceReceivesSecondUpdatedContent)445 TEST_F(FeedStreamTest, SurfaceReceivesSecondUpdatedContent) {
446   {
447     auto model = std::make_unique<StreamModel>();
448     model->ExecuteOperations(MakeTypicalStreamOperations());
449     stream_->LoadModelForTesting(std::move(model));
450   }
451   TestSurface surface;
452   stream_->AttachSurface(&surface);
453   // Add #2.
454   stream_->ExecuteOperations({
455       MakeOperation(MakeCluster(2, MakeRootId())),
456       MakeOperation(MakeContentNode(2, MakeClusterId(2))),
457       MakeOperation(MakeContent(2)),
458   });
459 
460   // Clear the last update and add #3.
461   stream_->ExecuteOperations({
462       MakeOperation(MakeCluster(3, MakeRootId())),
463       MakeOperation(MakeContentNode(3, MakeClusterId(3))),
464       MakeOperation(MakeContent(3)),
465   });
466 
467   // The last update should have only one new piece of content.
468   // This verifies the current content set is tracked properly.
469   ASSERT_EQ("4 slices 3 updates", surface.Describe());
470 
471   ASSERT_EQ(4, surface.update->updated_slices().size());
472   EXPECT_FALSE(surface.update->updated_slices(0).has_slice());
473   EXPECT_FALSE(surface.update->updated_slices(1).has_slice());
474   EXPECT_FALSE(surface.update->updated_slices(2).has_slice());
475   EXPECT_EQ("f:3", surface.update->updated_slices(3)
476                        .slice()
477                        .xsurface_slice()
478                        .xsurface_frame());
479 }
480 
TEST_F(FeedStreamTest,DetachSurface)481 TEST_F(FeedStreamTest, DetachSurface) {
482   {
483     auto model = std::make_unique<StreamModel>();
484     model->ExecuteOperations(MakeTypicalStreamOperations());
485     stream_->LoadModelForTesting(std::move(model));
486   }
487   TestSurface surface;
488   stream_->AttachSurface(&surface);
489   EXPECT_TRUE(surface.initial_state);
490   stream_->DetachSurface(&surface);
491   surface.Clear();
492 
493   // Arbitrary stream change. Surface should not see the update.
494   stream_->ExecuteOperations({
495       MakeOperation(MakeRemove(MakeClusterId(1))),
496   });
497   EXPECT_FALSE(surface.update);
498 }
499 
TEST_F(FeedStreamTest,LoadFromNetwork)500 TEST_F(FeedStreamTest, LoadFromNetwork) {
501   // Store is empty, so we should fallback to a network request.
502   response_translator_.InjectResponse(MakeTypicalInitialModelState());
503   TestSurface surface;
504   stream_->AttachSurface(&surface);
505   WaitForIdleTaskQueue();
506 
507   EXPECT_TRUE(network_.query_request_sent);
508   EXPECT_TRUE(response_translator_.InjectedResponseConsumed());
509   EXPECT_EQ("2 slices", surface.Describe());
510   // Verify the model is filled correctly.
511   EXPECT_STRINGS_EQUAL(ModelStateFor(MakeTypicalInitialModelState()),
512                        stream_->GetModel()->DumpStateForTesting());
513   // Verify the data was written to the store.
514   EXPECT_STRINGS_EQUAL(ModelStateFor(store_.get()),
515                        ModelStateFor(MakeTypicalInitialModelState()));
516 }
517 
TEST_F(FeedStreamTest,LoadFromNetworkBecauseStoreIsStale)518 TEST_F(FeedStreamTest, LoadFromNetworkBecauseStoreIsStale) {
519   // Fill the store with stream data that is just barely stale, and verify we
520   // fetch new data over the network.
521   user_classifier_->OverrideUserClass(UserClass::kActiveSuggestionsConsumer);
522   store_->SaveFullStream(MakeTypicalInitialModelState(
523 
524                              kTestTimeEpoch - base::TimeDelta::FromHours(12) -
525                              base::TimeDelta::FromMinutes(1)),
526                          base::DoNothing());
527 
528   // Store is stale, so we should fallback to a network request.
529   response_translator_.InjectResponse(MakeTypicalInitialModelState());
530   TestSurface surface;
531   stream_->AttachSurface(&surface);
532   WaitForIdleTaskQueue();
533 
534   EXPECT_TRUE(network_.query_request_sent);
535   EXPECT_TRUE(response_translator_.InjectedResponseConsumed());
536   ASSERT_TRUE(surface.initial_state);
537 }
538 
TEST_F(FeedStreamTest,LoadFromNetworkFailsDueToProtoTranslation)539 TEST_F(FeedStreamTest, LoadFromNetworkFailsDueToProtoTranslation) {
540   // No data in the store, so we should fetch from the network.
541   // The network will respond with an empty response, which should fail proto
542   // translation.
543   TestSurface surface;
544   stream_->AttachSurface(&surface);
545   WaitForIdleTaskQueue();
546 
547   EXPECT_EQ(LoadStreamStatus::kProtoTranslationFailed,
548             event_observer_.load_stream_status);
549 }
550 
TEST_F(FeedStreamTest,DoNotLoadFromNetworkWhenOffline)551 TEST_F(FeedStreamTest, DoNotLoadFromNetworkWhenOffline) {
552   is_offline_ = true;
553   response_translator_.InjectResponse(MakeTypicalInitialModelState());
554   TestSurface surface;
555   stream_->AttachSurface(&surface);
556   WaitForIdleTaskQueue();
557 
558   EXPECT_EQ(LoadStreamStatus::kCannotLoadFromNetworkOffline,
559             event_observer_.load_stream_status);
560   EXPECT_EQ("zero-state", surface.Describe());
561 }
562 
TEST_F(FeedStreamTest,DoNotLoadStreamWhenArticleListIsHidden)563 TEST_F(FeedStreamTest, DoNotLoadStreamWhenArticleListIsHidden) {
564   stream_->SetArticlesListVisible(false);
565   response_translator_.InjectResponse(MakeTypicalInitialModelState());
566   TestSurface surface;
567   stream_->AttachSurface(&surface);
568   WaitForIdleTaskQueue();
569 
570   EXPECT_EQ(LoadStreamStatus::kLoadNotAllowedArticlesListHidden,
571             event_observer_.load_stream_status);
572   EXPECT_EQ("zero-state", surface.Describe());
573 }
574 
TEST_F(FeedStreamTest,DoNotLoadStreamWhenEulaIsNotAccepted)575 TEST_F(FeedStreamTest, DoNotLoadStreamWhenEulaIsNotAccepted) {
576   is_eula_accepted_ = false;
577   response_translator_.InjectResponse(MakeTypicalInitialModelState());
578   TestSurface surface;
579   stream_->AttachSurface(&surface);
580   WaitForIdleTaskQueue();
581 
582   EXPECT_EQ(LoadStreamStatus::kLoadNotAllowedEulaNotAccepted,
583             event_observer_.load_stream_status);
584   EXPECT_EQ("zero-state", surface.Describe());
585 }
586 
TEST_F(FeedStreamTest,DoNotLoadFromNetworkAfterHistoryIsDeleted)587 TEST_F(FeedStreamTest, DoNotLoadFromNetworkAfterHistoryIsDeleted) {
588   stream_->OnHistoryDeleted();
589   task_environment_.FastForwardBy(kSuppressRefreshDuration -
590                                   base::TimeDelta::FromSeconds(1));
591   response_translator_.InjectResponse(MakeTypicalInitialModelState());
592   TestSurface surface;
593   stream_->AttachSurface(&surface);
594   WaitForIdleTaskQueue();
595 
596   EXPECT_EQ("zero-state", surface.Describe());
597 
598   EXPECT_EQ(LoadStreamStatus::kCannotLoadFromNetworkSupressedForHistoryDelete,
599             event_observer_.load_stream_status);
600 
601   stream_->DetachSurface(&surface);
602   task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(2));
603   stream_->AttachSurface(&surface);
604   WaitForIdleTaskQueue();
605 
606   EXPECT_EQ("2 slices 2 updates", surface.Describe());
607 }
608 
TEST_F(FeedStreamTest,ShouldMakeFeedQueryRequestConsumesQuota)609 TEST_F(FeedStreamTest, ShouldMakeFeedQueryRequestConsumesQuota) {
610   LoadStreamStatus status = LoadStreamStatus::kNoStatus;
611   for (; status == LoadStreamStatus::kNoStatus;
612        status = stream_->ShouldMakeFeedQueryRequest()) {
613   }
614 
615   ASSERT_EQ(LoadStreamStatus::kCannotLoadFromNetworkThrottled, status);
616 }
617 
TEST_F(FeedStreamTest,LoadStreamFromStore)618 TEST_F(FeedStreamTest, LoadStreamFromStore) {
619   // Fill the store with stream data that is just barely fresh, and verify it
620   // loads.
621   user_classifier_->OverrideUserClass(UserClass::kActiveSuggestionsConsumer);
622   store_->SaveFullStream(MakeTypicalInitialModelState(
623                              kTestTimeEpoch - base::TimeDelta::FromHours(12) +
624                              base::TimeDelta::FromMinutes(1)),
625                          base::DoNothing());
626   TestSurface surface;
627   stream_->AttachSurface(&surface);
628   WaitForIdleTaskQueue();
629 
630   ASSERT_EQ("2 slices", surface.Describe());
631   EXPECT_FALSE(network_.query_request_sent);
632   // Verify the model is filled correctly.
633   EXPECT_STRINGS_EQUAL(ModelStateFor(MakeTypicalInitialModelState()),
634                        stream_->GetModel()->DumpStateForTesting());
635 }
636 
TEST_F(FeedStreamTest,DetachSurfaceWhileLoadingModel)637 TEST_F(FeedStreamTest, DetachSurfaceWhileLoadingModel) {
638   response_translator_.InjectResponse(MakeTypicalInitialModelState());
639   TestSurface surface;
640   stream_->AttachSurface(&surface);
641   stream_->DetachSurface(&surface);
642   WaitForIdleTaskQueue();
643 
644   EXPECT_EQ("empty", surface.Describe());
645   EXPECT_TRUE(network_.query_request_sent);
646 }
647 
TEST_F(FeedStreamTest,AttachMultipleSurfacesLoadsModelOnce)648 TEST_F(FeedStreamTest, AttachMultipleSurfacesLoadsModelOnce) {
649   response_translator_.InjectResponse(MakeTypicalInitialModelState());
650   TestSurface surface;
651   TestSurface other_surface;
652   stream_->AttachSurface(&surface);
653   stream_->AttachSurface(&other_surface);
654   WaitForIdleTaskQueue();
655 
656   ASSERT_EQ(1, network_.send_query_call_count);
657 
658   // After load, another surface doesn't trigger any tasks.
659   TestSurface later_surface;
660   stream_->AttachSurface(&later_surface);
661 
662   EXPECT_TRUE(IsTaskQueueIdle());
663 }
664 
TEST_F(FeedStreamTest,ModelChangesAreSavedToStorage)665 TEST_F(FeedStreamTest, ModelChangesAreSavedToStorage) {
666   store_->SaveFullStream(MakeTypicalInitialModelState(), base::DoNothing());
667   TestSurface surface;
668   stream_->AttachSurface(&surface);
669   WaitForIdleTaskQueue();
670   ASSERT_TRUE(surface.initial_state);
671 
672   // Remove #1, add #2.
673   const std::vector<feedstore::DataOperation> operations = {
674       MakeOperation(MakeRemove(MakeClusterId(1))),
675       MakeOperation(MakeCluster(2, MakeRootId())),
676       MakeOperation(MakeContentNode(2, MakeClusterId(2))),
677       MakeOperation(MakeContent(2)),
678   };
679   stream_->ExecuteOperations(operations);
680 
681   WaitForIdleTaskQueue();
682 
683   // Verify changes are applied to storage.
684   EXPECT_STRINGS_EQUAL(
685       ModelStateFor(MakeTypicalInitialModelState(), operations),
686       ModelStateFor(store_.get()));
687 
688   // Unload and reload the model from the store, and verify we can still apply
689   // operations correctly.
690   stream_->DetachSurface(&surface);
691   surface.Clear();
692   UnloadModel();
693   stream_->AttachSurface(&surface);
694   WaitForIdleTaskQueue();
695   ASSERT_TRUE(surface.initial_state);
696 
697   // Remove #2, add #3.
698   const std::vector<feedstore::DataOperation> operations2 = {
699       MakeOperation(MakeRemove(MakeClusterId(2))),
700       MakeOperation(MakeCluster(3, MakeRootId())),
701       MakeOperation(MakeContentNode(3, MakeClusterId(3))),
702       MakeOperation(MakeContent(3)),
703   };
704   stream_->ExecuteOperations(operations2);
705 
706   WaitForIdleTaskQueue();
707   EXPECT_STRINGS_EQUAL(
708       ModelStateFor(MakeTypicalInitialModelState(), operations, operations2),
709       ModelStateFor(store_.get()));
710 }
711 
712 }  // namespace
713 }  // namespace feed
714