1 // Copyright 2017 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/feature_engagement/internal/persistent_availability_store.h"
6
7 #include <stdint.h>
8
9 #include <map>
10 #include <memory>
11 #include <string>
12 #include <utility>
13 #include <vector>
14
15 #include "base/bind.h"
16 #include "base/callback.h"
17 #include "base/macros.h"
18 #include "base/optional.h"
19 #include "base/test/scoped_feature_list.h"
20 #include "components/feature_engagement/internal/proto/availability.pb.h"
21 #include "components/feature_engagement/public/feature_list.h"
22 #include "components/leveldb_proto/public/proto_database.h"
23 #include "components/leveldb_proto/testing/fake_db.h"
24 #include "testing/gtest/include/gtest/gtest.h"
25
26 namespace feature_engagement {
27
28 namespace {
29 const base::Feature kPersistentTestFeatureFoo{
30 "test_foo", base::FEATURE_DISABLED_BY_DEFAULT};
31 const base::Feature kPersistentTestFeatureBar{
32 "test_bar", base::FEATURE_DISABLED_BY_DEFAULT};
33 const base::Feature kPersistentTestFeatureQux{
34 "test_qux", base::FEATURE_DISABLED_BY_DEFAULT};
35 const base::Feature kPersistentTestFeatureNop{
36 "test_nop", base::FEATURE_DISABLED_BY_DEFAULT};
37
CreateAvailability(const base::Feature & feature,uint32_t day)38 Availability CreateAvailability(const base::Feature& feature, uint32_t day) {
39 Availability availability;
40 availability.set_feature_name(feature.name);
41 availability.set_day(day);
42 return availability;
43 }
44
45 class PersistentAvailabilityStoreTest : public testing::Test {
46 public:
PersistentAvailabilityStoreTest()47 PersistentAvailabilityStoreTest()
48 : db_(nullptr),
49 storage_dir_(FILE_PATH_LITERAL("/persistent/store/lalala")) {
50 load_callback_ = base::Bind(&PersistentAvailabilityStoreTest::LoadCallback,
51 base::Unretained(this));
52 }
53
54 ~PersistentAvailabilityStoreTest() override = default;
55
56 // Creates a DB and stores off a pointer to it as a member.
CreateDB()57 std::unique_ptr<leveldb_proto::test::FakeDB<Availability>> CreateDB() {
58 auto db = std::make_unique<leveldb_proto::test::FakeDB<Availability>>(
59 &db_availabilities_);
60 db_ = db.get();
61 return db;
62 }
63
LoadCallback(bool success,std::unique_ptr<std::map<std::string,uint32_t>> availabilities)64 void LoadCallback(
65 bool success,
66 std::unique_ptr<std::map<std::string, uint32_t>> availabilities) {
67 load_successful_ = success;
68 load_results_ = std::move(availabilities);
69 }
70
71 protected:
72 base::test::ScopedFeatureList scoped_feature_list_;
73
74 // The end result of the store pipeline.
75 PersistentAvailabilityStore::OnLoadedCallback load_callback_;
76
77 // Callback results.
78 base::Optional<bool> load_successful_;
79 std::unique_ptr<std::map<std::string, uint32_t>> load_results_;
80
81 // |db_availabilities_| is used during creation of the FakeDB in CreateDB(),
82 // to simplify what the DB has stored.
83 std::map<std::string, Availability> db_availabilities_;
84
85 // The database that is in use.
86 leveldb_proto::test::FakeDB<Availability>* db_;
87
88 // Constant test data.
89 base::FilePath storage_dir_;
90
91 private:
92 DISALLOW_COPY_AND_ASSIGN(PersistentAvailabilityStoreTest);
93 };
94
95 } // namespace
96
TEST_F(PersistentAvailabilityStoreTest,InitFail)97 TEST_F(PersistentAvailabilityStoreTest, InitFail) {
98 PersistentAvailabilityStore::LoadAndUpdateStore(
99 storage_dir_, CreateDB(), FeatureVector(), std::move(load_callback_),
100 14u);
101
102 db_->InitStatusCallback(leveldb_proto::Enums::InitStatus::kError);
103
104 EXPECT_TRUE(load_successful_.has_value());
105 EXPECT_FALSE(load_successful_.value());
106 EXPECT_EQ(0u, load_results_->size());
107 EXPECT_EQ(0u, db_availabilities_.size());
108 }
109
TEST_F(PersistentAvailabilityStoreTest,LoadFail)110 TEST_F(PersistentAvailabilityStoreTest, LoadFail) {
111 PersistentAvailabilityStore::LoadAndUpdateStore(
112 storage_dir_, CreateDB(), FeatureVector(), std::move(load_callback_),
113 14u);
114
115 db_->InitStatusCallback(leveldb_proto::Enums::InitStatus::kOK);
116 EXPECT_FALSE(load_successful_.has_value());
117
118 db_->LoadCallback(false);
119
120 EXPECT_TRUE(load_successful_.has_value());
121 EXPECT_FALSE(load_successful_.value());
122 EXPECT_EQ(0u, load_results_->size());
123 EXPECT_EQ(0u, db_availabilities_.size());
124 }
125
TEST_F(PersistentAvailabilityStoreTest,EmptyDBEmptyFeatureFilterUpdateFailed)126 TEST_F(PersistentAvailabilityStoreTest, EmptyDBEmptyFeatureFilterUpdateFailed) {
127 PersistentAvailabilityStore::LoadAndUpdateStore(
128 storage_dir_, CreateDB(), FeatureVector(), std::move(load_callback_),
129 14u);
130
131 db_->InitStatusCallback(leveldb_proto::Enums::InitStatus::kOK);
132 EXPECT_FALSE(load_successful_.has_value());
133
134 db_->LoadCallback(true);
135 EXPECT_FALSE(load_successful_.has_value());
136
137 db_->UpdateCallback(false);
138
139 EXPECT_TRUE(load_successful_.has_value());
140 EXPECT_FALSE(load_successful_.value());
141 EXPECT_EQ(0u, load_results_->size());
142 EXPECT_EQ(0u, db_availabilities_.size());
143 }
144
TEST_F(PersistentAvailabilityStoreTest,EmptyDBEmptyFeatureFilterUpdateOK)145 TEST_F(PersistentAvailabilityStoreTest, EmptyDBEmptyFeatureFilterUpdateOK) {
146 PersistentAvailabilityStore::LoadAndUpdateStore(
147 storage_dir_, CreateDB(), FeatureVector(), std::move(load_callback_),
148 14u);
149
150 db_->InitStatusCallback(leveldb_proto::Enums::InitStatus::kOK);
151 EXPECT_FALSE(load_successful_.has_value());
152
153 db_->LoadCallback(true);
154 EXPECT_FALSE(load_successful_.has_value());
155
156 db_->UpdateCallback(true);
157
158 EXPECT_TRUE(load_successful_.has_value());
159 EXPECT_TRUE(load_successful_.value());
160 EXPECT_EQ(0u, load_results_->size());
161 EXPECT_EQ(0u, db_availabilities_.size());
162 }
163
TEST_F(PersistentAvailabilityStoreTest,AllNewFeatures)164 TEST_F(PersistentAvailabilityStoreTest, AllNewFeatures) {
165 scoped_feature_list_.InitWithFeatures(
166 {kPersistentTestFeatureFoo, kPersistentTestFeatureBar},
167 {kPersistentTestFeatureQux});
168
169 FeatureVector feature_filter;
170 feature_filter.push_back(&kPersistentTestFeatureFoo); // Enabled. Not in DB.
171 feature_filter.push_back(&kPersistentTestFeatureBar); // Enabled. Not in DB.
172 feature_filter.push_back(&kPersistentTestFeatureQux); // Disabled. Not in DB.
173
174 PersistentAvailabilityStore::LoadAndUpdateStore(
175 storage_dir_, CreateDB(), feature_filter, std::move(load_callback_), 14u);
176
177 db_->InitStatusCallback(leveldb_proto::Enums::InitStatus::kOK);
178 EXPECT_FALSE(load_successful_.has_value());
179
180 db_->LoadCallback(true);
181 EXPECT_FALSE(load_successful_.has_value());
182
183 db_->UpdateCallback(true);
184
185 EXPECT_TRUE(load_successful_.has_value());
186 EXPECT_TRUE(load_successful_.value());
187 ASSERT_EQ(2u, load_results_->size());
188 ASSERT_EQ(2u, db_availabilities_.size());
189
190 ASSERT_TRUE(load_results_->find(kPersistentTestFeatureFoo.name) !=
191 load_results_->end());
192 EXPECT_EQ(14u, (*load_results_)[kPersistentTestFeatureFoo.name]);
193 ASSERT_TRUE(db_availabilities_.find(kPersistentTestFeatureFoo.name) !=
194 db_availabilities_.end());
195 EXPECT_EQ(14u, db_availabilities_[kPersistentTestFeatureFoo.name].day());
196
197 ASSERT_TRUE(load_results_->find(kPersistentTestFeatureBar.name) !=
198 load_results_->end());
199 EXPECT_EQ(14u, (*load_results_)[kPersistentTestFeatureBar.name]);
200 ASSERT_TRUE(db_availabilities_.find(kPersistentTestFeatureBar.name) !=
201 db_availabilities_.end());
202 EXPECT_EQ(14u, db_availabilities_[kPersistentTestFeatureBar.name].day());
203 }
204
TEST_F(PersistentAvailabilityStoreTest,TestAllFilterCombinations)205 TEST_F(PersistentAvailabilityStoreTest, TestAllFilterCombinations) {
206 scoped_feature_list_.InitWithFeatures(
207 {kPersistentTestFeatureFoo, kPersistentTestFeatureBar},
208 {kPersistentTestFeatureQux, kPersistentTestFeatureNop});
209
210 FeatureVector feature_filter;
211 feature_filter.push_back(&kPersistentTestFeatureFoo); // Enabled. Not in DB.
212 feature_filter.push_back(&kPersistentTestFeatureBar); // Enabled. In DB.
213 feature_filter.push_back(&kPersistentTestFeatureQux); // Disabled. Not in DB.
214 feature_filter.push_back(&kPersistentTestFeatureNop); // Disabled. In DB.
215
216 db_availabilities_[kPersistentTestFeatureBar.name] =
217 CreateAvailability(kPersistentTestFeatureBar, 10u);
218 db_availabilities_[kPersistentTestFeatureNop.name] =
219 CreateAvailability(kPersistentTestFeatureNop, 8u);
220
221 PersistentAvailabilityStore::LoadAndUpdateStore(
222 storage_dir_, CreateDB(), feature_filter, std::move(load_callback_), 14u);
223
224 db_->InitStatusCallback(leveldb_proto::Enums::InitStatus::kOK);
225 EXPECT_FALSE(load_successful_.has_value());
226
227 db_->LoadCallback(true);
228 EXPECT_FALSE(load_successful_.has_value());
229
230 db_->UpdateCallback(true);
231
232 EXPECT_TRUE(load_successful_.has_value());
233 EXPECT_TRUE(load_successful_.value());
234 ASSERT_EQ(2u, load_results_->size());
235 ASSERT_EQ(2u, db_availabilities_.size());
236
237 ASSERT_TRUE(load_results_->find(kPersistentTestFeatureFoo.name) !=
238 load_results_->end());
239 EXPECT_EQ(14u, (*load_results_)[kPersistentTestFeatureFoo.name]);
240 ASSERT_TRUE(db_availabilities_.find(kPersistentTestFeatureFoo.name) !=
241 db_availabilities_.end());
242 EXPECT_EQ(14u, db_availabilities_[kPersistentTestFeatureFoo.name].day());
243
244 ASSERT_TRUE(load_results_->find(kPersistentTestFeatureBar.name) !=
245 load_results_->end());
246 EXPECT_EQ(10u, (*load_results_)[kPersistentTestFeatureBar.name]);
247 ASSERT_TRUE(db_availabilities_.find(kPersistentTestFeatureBar.name) !=
248 db_availabilities_.end());
249 EXPECT_EQ(10u, db_availabilities_[kPersistentTestFeatureBar.name].day());
250 }
251
TEST_F(PersistentAvailabilityStoreTest,TestAllCombinationsEmptyFilter)252 TEST_F(PersistentAvailabilityStoreTest, TestAllCombinationsEmptyFilter) {
253 scoped_feature_list_.InitWithFeatures(
254 {kPersistentTestFeatureFoo, kPersistentTestFeatureBar},
255 {kPersistentTestFeatureQux, kPersistentTestFeatureNop});
256
257 // Empty filter, but the following setup:
258 // kPersistentTestFeatureFoo: Enabled. Not in DB.
259 // kPersistentTestFeatureBar: Enabled. In DB.
260 // kPersistentTestFeatureQux: Disabled. Not in DB.
261 // kPersistentTestFeatureNop: Disabled. In DB.
262
263 db_availabilities_[kPersistentTestFeatureBar.name] =
264 CreateAvailability(kPersistentTestFeatureBar, 10u);
265 db_availabilities_[kPersistentTestFeatureNop.name] =
266 CreateAvailability(kPersistentTestFeatureNop, 8u);
267
268 PersistentAvailabilityStore::LoadAndUpdateStore(
269 storage_dir_, CreateDB(), FeatureVector(), std::move(load_callback_),
270 14u);
271
272 db_->InitStatusCallback(leveldb_proto::Enums::InitStatus::kOK);
273 EXPECT_FALSE(load_successful_.has_value());
274
275 db_->LoadCallback(true);
276 EXPECT_FALSE(load_successful_.has_value());
277
278 db_->UpdateCallback(true);
279
280 EXPECT_TRUE(load_successful_.has_value());
281 EXPECT_TRUE(load_successful_.value());
282 EXPECT_EQ(0u, load_results_->size());
283 EXPECT_EQ(0u, db_availabilities_.size());
284 }
285
286 } // namespace feature_engagement
287