Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(157)

Side by Side Diff: rlz/chromeos/lib/rlz_value_store_chromeos.cc

Issue 11308196: [cros] RlzValueStore made protected by a cross-process lock and not persisted over browser lifetime… (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Comment Created 8 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "rlz/chromeos/lib/rlz_value_store_chromeos.h" 5 #include "rlz/chromeos/lib/rlz_value_store_chromeos.h"
6 6
7 #include "base/file_path.h"
8 #include "base/file_util.h" 7 #include "base/file_util.h"
8 #include "base/files/important_file_writer.h"
9 #include "base/json/json_file_value_serializer.h"
10 #include "base/json/json_string_value_serializer.h"
9 #include "base/logging.h" 11 #include "base/logging.h"
10 #include "base/memory/singleton.h"
11 #include "base/prefs/json_pref_store.h"
12 #include "base/sequenced_task_runner.h" 12 #include "base/sequenced_task_runner.h"
13 #include "base/string_number_conversions.h" 13 #include "base/string_number_conversions.h"
14 #include "base/values.h" 14 #include "base/values.h"
15 #include "rlz/lib/lib_values.h" 15 #include "rlz/lib/lib_values.h"
16 #include "rlz/lib/recursive_lock.h" 16 #include "rlz/lib/recursive_cross_process_lock_posix.h"
17 #include "rlz/lib/rlz_lib.h" 17 #include "rlz/lib/rlz_lib.h"
18 18
19 namespace rlz_lib { 19 namespace rlz_lib {
20 20
21 namespace { 21 namespace {
22 22
23 // Product names. 23 // Product names.
24 const char kProductChrome[] = "chrome"; 24 const char kProductChrome[] = "chrome";
25 const char kProductOther[] = "other"; 25 const char kProductOther[] = "other";
26 26
27 // Key names. 27 // Key names.
28 const char kPingTimeKey[] = "ping_time"; 28 const char kPingTimeKey[] = "ping_time";
29 const char kAccessPointKey[] = "access_points"; 29 const char kAccessPointKey[] = "access_points";
30 const char kProductEventKey[] = "product_events"; 30 const char kProductEventKey[] = "product_events";
31 const char kStatefulEventKey[] = "stateful_events"; 31 const char kStatefulEventKey[] = "stateful_events";
32 32
33 // Brand name used when there is no supplementary brand name. 33 // Brand name used when there is no supplementary brand name.
34 const char kNoSupplementaryBrand[] = "_"; 34 const char kNoSupplementaryBrand[] = "_";
35 35
36 // RLZ store filename. 36 // RLZ store filename.
37 const FilePath::CharType kRLZDataFileName[] = FILE_PATH_LITERAL("RLZ Data"); 37 const FilePath::CharType kRLZDataFileName[] = FILE_PATH_LITERAL("RLZ Data");
38 38
39 // RLZ store lock filename
40 const FilePath::CharType kRLZLockFileName[] =
41 FILE_PATH_LITERAL("RLZ Data.lock");
42
39 // RLZ store path for testing. 43 // RLZ store path for testing.
40 FilePath g_testing_rlz_store_path_; 44 FilePath g_testing_rlz_store_path_;
41 45
42 // Returns file path of the RLZ storage. 46 // Returns file path of the RLZ storage.
43 FilePath GetRlzStorePath() { 47 FilePath GetRlzStorePath() {
44 return g_testing_rlz_store_path_.empty() ? 48 return g_testing_rlz_store_path_.empty() ?
45 file_util::GetHomeDir().Append(kRLZDataFileName) : 49 file_util::GetHomeDir().Append(kRLZDataFileName) :
46 g_testing_rlz_store_path_.Append(kRLZDataFileName); 50 g_testing_rlz_store_path_.Append(kRLZDataFileName);
47 } 51 }
48 52
53 // Returns file path of the RLZ storage lock file.
54 FilePath GetRlzStoreLockPath() {
55 return g_testing_rlz_store_path_.empty() ?
56 file_util::GetHomeDir().Append(kRLZLockFileName) :
57 g_testing_rlz_store_path_.Append(kRLZLockFileName);
58 }
59
49 // Returns the dictionary key for storing access point-related prefs. 60 // Returns the dictionary key for storing access point-related prefs.
50 std::string GetKeyName(std::string key, AccessPoint access_point) { 61 std::string GetKeyName(std::string key, AccessPoint access_point) {
51 std::string brand = SupplementaryBranding::GetBrand(); 62 std::string brand = SupplementaryBranding::GetBrand();
52 if (brand.empty()) 63 if (brand.empty())
53 brand = kNoSupplementaryBrand; 64 brand = kNoSupplementaryBrand;
54 return key + "." + GetAccessPointName(access_point) + "." + brand; 65 return key + "." + GetAccessPointName(access_point) + "." + brand;
55 } 66 }
56 67
57 // Returns the dictionary key for storing product-related prefs. 68 // Returns the dictionary key for storing product-related prefs.
58 std::string GetKeyName(std::string key, Product product) { 69 std::string GetKeyName(std::string key, Product product) {
59 std::string brand = SupplementaryBranding::GetBrand(); 70 std::string brand = SupplementaryBranding::GetBrand();
60 if (brand.empty()) 71 if (brand.empty())
61 brand = kNoSupplementaryBrand; 72 brand = kNoSupplementaryBrand;
62 return key + "." + GetProductName(product) + "." + brand; 73 return key + "." + GetProductName(product) + "." + brand;
63 } 74 }
64 75
65 } // namespace 76 } // namespace
66 77
67 // static 78 RlzValueStoreChromeOS::RlzValueStoreChromeOS(const FilePath& store_path)
68 base::SequencedTaskRunner* RlzValueStoreChromeOS::io_task_runner_ = NULL; 79 : rlz_store_(new base::DictionaryValue),
69 80 store_path_(store_path),
70 // static 81 read_only_(true) {
71 bool RlzValueStoreChromeOS::created_; 82 DCHECK(CalledOnValidThread());
Roger Tawa OOO till Jul 10th 2012/11/26 19:16:51 why call CalledOnValidThread() in ctor?
Ivan Korotkov 2012/11/27 11:37:25 Right, removed.
72 83 ReadStore();
73 // static
74 RlzValueStoreChromeOS* RlzValueStoreChromeOS::GetInstance() {
75 return Singleton<RlzValueStoreChromeOS>::get();
76 }
77
78 // static
79 void RlzValueStoreChromeOS::SetIOTaskRunner(
80 base::SequencedTaskRunner* io_task_runner) {
81 io_task_runner_ = io_task_runner;
82 // Make sure |io_task_runner_| lives until constructor is called.
83 io_task_runner_->AddRef();
84 }
85
86 // static
87 void RlzValueStoreChromeOS::ResetForTesting() {
88 // Make sure we don't create an instance if it didn't exist.
89 if (created_)
90 GetInstance()->ReadPrefs();
91 }
92
93 // static
94 void RlzValueStoreChromeOS::Cleanup() {
95 if (created_)
96 GetInstance()->rlz_store_ = NULL;
97 }
98
99 RlzValueStoreChromeOS::RlzValueStoreChromeOS() {
100 ReadPrefs();
101 created_ = true;
102 } 84 }
103 85
104 RlzValueStoreChromeOS::~RlzValueStoreChromeOS() { 86 RlzValueStoreChromeOS::~RlzValueStoreChromeOS() {
87 DCHECK(CalledOnValidThread());
Roger Tawa OOO till Jul 10th 2012/11/26 19:16:51 the base class dtor already calls CalledOnValidThr
Ivan Korotkov 2012/11/27 11:37:25 Removed.
88 WriteStore();
105 } 89 }
106 90
107 bool RlzValueStoreChromeOS::HasAccess(AccessType type) { 91 bool RlzValueStoreChromeOS::HasAccess(AccessType type) {
108 return type == kReadAccess || !rlz_store_->ReadOnly(); 92 DCHECK(CalledOnValidThread());
93 return type == kReadAccess || !read_only_;
109 } 94 }
110 95
111 bool RlzValueStoreChromeOS::WritePingTime(Product product, int64 time) { 96 bool RlzValueStoreChromeOS::WritePingTime(Product product, int64 time) {
112 std::string value = base::Int64ToString(time); 97 DCHECK(CalledOnValidThread());
113 rlz_store_->SetValue(GetKeyName(kPingTimeKey, product), 98 rlz_store_->SetString(GetKeyName(kPingTimeKey, product),
114 base::Value::CreateStringValue(value)); 99 base::Int64ToString(time));
Roger Tawa OOO till Jul 10th 2012/11/26 19:16:51 one extra space.
Ivan Korotkov 2012/11/27 11:37:25 Done.
115 return true; 100 return true;
116 } 101 }
117 102
118 bool RlzValueStoreChromeOS::ReadPingTime(Product product, int64* time) { 103 bool RlzValueStoreChromeOS::ReadPingTime(Product product, int64* time) {
119 const base::Value* value = NULL; 104 DCHECK(CalledOnValidThread());
120 rlz_store_->GetValue(GetKeyName(kPingTimeKey, product), &value); 105 std::string ping_time;
121 std::string s_value; 106 return rlz_store_->GetString(GetKeyName(kPingTimeKey, product), &ping_time) &&
122 return value && value->GetAsString(&s_value) && 107 base::StringToInt64(ping_time, time);
123 base::StringToInt64(s_value, time);
124 } 108 }
125 109
126 bool RlzValueStoreChromeOS::ClearPingTime(Product product) { 110 bool RlzValueStoreChromeOS::ClearPingTime(Product product) {
127 rlz_store_->RemoveValue(GetKeyName(kPingTimeKey, product)); 111 DCHECK(CalledOnValidThread());
112 rlz_store_->Remove(GetKeyName(kPingTimeKey, product), NULL);
128 return true; 113 return true;
129 } 114 }
130 115
131 bool RlzValueStoreChromeOS::WriteAccessPointRlz(AccessPoint access_point, 116 bool RlzValueStoreChromeOS::WriteAccessPointRlz(AccessPoint access_point,
132 const char* new_rlz) { 117 const char* new_rlz) {
133 rlz_store_->SetValue( 118 DCHECK(CalledOnValidThread());
134 GetKeyName(kAccessPointKey, access_point), 119 rlz_store_->SetString(
135 base::Value::CreateStringValue(new_rlz)); 120 GetKeyName(kAccessPointKey, access_point), new_rlz);
136 return true; 121 return true;
137 } 122 }
138 123
139 bool RlzValueStoreChromeOS::ReadAccessPointRlz(AccessPoint access_point, 124 bool RlzValueStoreChromeOS::ReadAccessPointRlz(AccessPoint access_point,
140 char* rlz, 125 char* rlz,
141 size_t rlz_size) { 126 size_t rlz_size) {
142 const base::Value* value = NULL; 127 DCHECK(CalledOnValidThread());
143 rlz_store_->GetValue( 128 std::string rlz_value;
144 GetKeyName(kAccessPointKey, access_point), &value); 129 rlz_store_->GetString(GetKeyName(kAccessPointKey, access_point), &rlz_value);
145 std::string s_value; 130 if (rlz_value.size() < rlz_size) {
146 if (value) 131 strncpy(rlz, rlz_value.c_str(), rlz_size);
147 value->GetAsString(&s_value);
148 if (s_value.size() < rlz_size) {
149 strncpy(rlz, s_value.c_str(), rlz_size);
150 return true; 132 return true;
151 } 133 }
152 if (rlz_size > 0) 134 if (rlz_size > 0)
153 *rlz = '\0'; 135 *rlz = '\0';
154 return false; 136 return false;
155 } 137 }
156 138
157 bool RlzValueStoreChromeOS::ClearAccessPointRlz(AccessPoint access_point) { 139 bool RlzValueStoreChromeOS::ClearAccessPointRlz(AccessPoint access_point) {
158 rlz_store_->RemoveValue( 140 DCHECK(CalledOnValidThread());
159 GetKeyName(kAccessPointKey, access_point)); 141 rlz_store_->Remove(GetKeyName(kAccessPointKey, access_point), NULL);
160 return true; 142 return true;
161 } 143 }
162 144
163 bool RlzValueStoreChromeOS::AddProductEvent(Product product, 145 bool RlzValueStoreChromeOS::AddProductEvent(Product product,
164 const char* event_rlz) { 146 const char* event_rlz) {
147 DCHECK(CalledOnValidThread());
165 return AddValueToList(GetKeyName(kProductEventKey, product), 148 return AddValueToList(GetKeyName(kProductEventKey, product),
166 base::Value::CreateStringValue(event_rlz)); 149 base::Value::CreateStringValue(event_rlz));
167 } 150 }
168 151
169 bool RlzValueStoreChromeOS::ReadProductEvents( 152 bool RlzValueStoreChromeOS::ReadProductEvents(
170 Product product, 153 Product product,
171 std::vector<std::string>* events) { 154 std::vector<std::string>* events) {
172 base::ListValue* events_list = GetList(GetKeyName(kProductEventKey, product)); 155 DCHECK(CalledOnValidThread());
173 if (!events_list) 156 base::ListValue* events_list = NULL; ;
157 if (!rlz_store_->GetList(GetKeyName(kProductEventKey, product), &events_list))
174 return false; 158 return false;
175 events->clear(); 159 events->clear();
176 for (size_t i = 0; i < events_list->GetSize(); ++i) { 160 for (size_t i = 0; i < events_list->GetSize(); ++i) {
177 std::string event; 161 std::string event;
178 if (events_list->GetString(i, &event)) 162 if (events_list->GetString(i, &event))
179 events->push_back(event); 163 events->push_back(event);
180 } 164 }
181 return true; 165 return true;
182 } 166 }
183 167
184 bool RlzValueStoreChromeOS::ClearProductEvent(Product product, 168 bool RlzValueStoreChromeOS::ClearProductEvent(Product product,
185 const char* event_rlz) { 169 const char* event_rlz) {
170 DCHECK(CalledOnValidThread());
186 base::StringValue event_value(event_rlz); 171 base::StringValue event_value(event_rlz);
187 return RemoveValueFromList(GetKeyName(kProductEventKey, product), 172 return RemoveValueFromList(GetKeyName(kProductEventKey, product),
188 event_value); 173 event_value);
189 } 174 }
190 175
191 bool RlzValueStoreChromeOS::ClearAllProductEvents(Product product) { 176 bool RlzValueStoreChromeOS::ClearAllProductEvents(Product product) {
192 rlz_store_->RemoveValue(GetKeyName(kProductEventKey, product)); 177 DCHECK(CalledOnValidThread());
178 rlz_store_->Remove(GetKeyName(kProductEventKey, product), NULL);
193 return true; 179 return true;
194 } 180 }
195 181
196 bool RlzValueStoreChromeOS::AddStatefulEvent(Product product, 182 bool RlzValueStoreChromeOS::AddStatefulEvent(Product product,
197 const char* event_rlz) { 183 const char* event_rlz) {
184 DCHECK(CalledOnValidThread());
198 return AddValueToList(GetKeyName(kStatefulEventKey, product), 185 return AddValueToList(GetKeyName(kStatefulEventKey, product),
199 base::Value::CreateStringValue(event_rlz)); 186 base::Value::CreateStringValue(event_rlz));
200 } 187 }
201 188
202 bool RlzValueStoreChromeOS::IsStatefulEvent(Product product, 189 bool RlzValueStoreChromeOS::IsStatefulEvent(Product product,
203 const char* event_rlz) { 190 const char* event_rlz) {
204 base::ListValue* events_list = 191 DCHECK(CalledOnValidThread());
205 GetList(GetKeyName(kStatefulEventKey, product));
206 base::StringValue event_value(event_rlz); 192 base::StringValue event_value(event_rlz);
207 return events_list && events_list->Find(event_value) != events_list->end(); 193 base::ListValue* events_list = NULL;
194 return rlz_store_->GetList(GetKeyName(kStatefulEventKey, product),
195 &events_list) &&
196 events_list->Find(event_value) != events_list->end();
208 } 197 }
209 198
210 bool RlzValueStoreChromeOS::ClearAllStatefulEvents(Product product) { 199 bool RlzValueStoreChromeOS::ClearAllStatefulEvents(Product product) {
211 rlz_store_->RemoveValue(GetKeyName(kStatefulEventKey, product)); 200 DCHECK(CalledOnValidThread());
201 rlz_store_->Remove(GetKeyName(kStatefulEventKey, product), NULL);
212 return true; 202 return true;
213 } 203 }
214 204
215 void RlzValueStoreChromeOS::CollectGarbage() { 205 void RlzValueStoreChromeOS::CollectGarbage() {
206 DCHECK(CalledOnValidThread());
216 NOTIMPLEMENTED(); 207 NOTIMPLEMENTED();
217 } 208 }
218 209
219 void RlzValueStoreChromeOS::ReadPrefs() { 210 void RlzValueStoreChromeOS::ReadStore() {
220 DCHECK(io_task_runner_) 211 int error_code = 0;
221 << "Calling GetInstance or ResetForTesting before SetIOTaskRunner?"; 212 std::string error_msg;
222 rlz_store_ = new JsonPrefStore(GetRlzStorePath(), io_task_runner_); 213 JSONFileValueSerializer serializer(store_path_);
223 rlz_store_->ReadPrefs(); 214 scoped_ptr<base::Value> value(
224 switch (rlz_store_->GetReadError()) { 215 serializer.Deserialize(&error_code, &error_msg));
225 case PersistentPrefStore::PREF_READ_ERROR_NONE: 216 switch (error_code) {
226 case PersistentPrefStore::PREF_READ_ERROR_NO_FILE: 217 case JSONFileValueSerializer::JSON_NO_SUCH_FILE:
218 read_only_ = false;
Roger Tawa OOO till Jul 10th 2012/11/26 19:16:51 why set read-only to false here?
Ivan Korotkov 2012/11/27 11:37:25 NO_SUCH_FILE means RLZ store hasn't been created y
219 break;
220 case JSONFileValueSerializer::JSON_NO_ERROR:
221 read_only_ = false;
222 rlz_store_.reset(static_cast<base::DictionaryValue*>(value.release()));
227 break; 223 break;
228 default: 224 default:
229 LOG(ERROR) << "Error read RLZ store: " << rlz_store_->GetReadError(); 225 LOG(ERROR) << "Error reading RLZ store: " << error_msg;
230 } 226 }
231 // Restore refcount modified by SetIOTaskRunner().
232 io_task_runner_->Release();
233 io_task_runner_ = NULL;
234 } 227 }
235 228
236 base::ListValue* RlzValueStoreChromeOS::GetList(std::string list_name) { 229 void RlzValueStoreChromeOS::WriteStore() {
237 base::Value* list_value = NULL; 230 std::string json_data;
238 rlz_store_->GetMutableValue(list_name, &list_value); 231 JSONStringValueSerializer serializer(&json_data);
239 base::ListValue* list = NULL; 232 serializer.set_pretty_print(true);
240 if (!list_value || !list_value->GetAsList(&list)) 233 scoped_ptr<DictionaryValue> copy(rlz_store_->DeepCopyWithoutEmptyChildren());
241 return NULL; 234 if (!serializer.Serialize(*copy.get())) {
242 return list; 235 LOG(ERROR) << "Failed to serialize RLZ data";
236 NOTREACHED();
237 return;
238 }
239 if (!base::ImportantFileWriter::WriteToDisk(store_path_, json_data))
240 LOG(ERROR) << "Error writing RLZ store";
243 } 241 }
244 242
245 bool RlzValueStoreChromeOS::AddValueToList(std::string list_name, 243 bool RlzValueStoreChromeOS::AddValueToList(std::string list_name,
246 base::Value* value) { 244 base::Value* value) {
247 base::ListValue* list = GetList(list_name); 245 base::ListValue* list_value = NULL;
248 if (!list) { 246 if (!rlz_store_->GetList(list_name, &list_value)) {
249 list = new base::ListValue; 247 list_value = new base::ListValue;
250 rlz_store_->SetValue(list_name, list); 248 rlz_store_->Set(list_name, list_value);
251 } 249 }
252 if (list->AppendIfNotPresent(value)) 250 list_value->AppendIfNotPresent(value);
253 rlz_store_->ReportValueChanged(list_name);
254 return true; 251 return true;
255 } 252 }
256 253
257 bool RlzValueStoreChromeOS::RemoveValueFromList(std::string list_name, 254 bool RlzValueStoreChromeOS::RemoveValueFromList(std::string list_name,
258 const base::Value& value) { 255 const base::Value& value) {
259 base::ListValue* list = GetList(list_name); 256 base::ListValue* list_value = NULL;
260 if (!list) 257 if (!rlz_store_->GetList(list_name, &list_value))
261 return false; 258 return false;
262 rlz_store_->MarkNeedsEmptyValue(list_name); 259 // rlz_store_->MarkNeedsEmptyValue(list_name);
Roger Tawa OOO till Jul 10th 2012/11/26 19:16:51 remove line?
Ivan Korotkov 2012/11/27 11:37:25 Done.
263 size_t index; 260 size_t index;
264 if (list->Remove(value, &index)) 261 list_value->Remove(value, &index);
265 rlz_store_->ReportValueChanged(list_name);
266 return true; 262 return true;
267 } 263 }
268 264
265 namespace {
269 266
270 ScopedRlzValueStoreLock::ScopedRlzValueStoreLock() 267 // RlzValueStoreChromeOS keeps its data in memory and only writes it to disk
271 : store_(RlzValueStoreChromeOS::GetInstance()) { 268 // when ScopedRlzValueStoreLock goes out of scope. Hence, if several
269 // ScopedRlzValueStoreLocks are nested, they all need to use the same store
270 // object.
271
272 RecursiveCrossProcessLock g_recursive_lock =
273 RECURSIVE_CROSS_PROCESS_LOCK_INITIALIZER;
274
275 // This counts the nesting depth of |ScopedRlzValueStoreLock|.
276 int g_lock_depth = 0;
277
278 // This is the shared store object. Non-|NULL| only when |g_lock_depth > 0|.
279 RlzValueStoreChromeOS* g_store = NULL;
280
281 } // namespace
282
283 ScopedRlzValueStoreLock::ScopedRlzValueStoreLock() {
284 bool got_cross_process_lock =
285 g_recursive_lock.TryGetCrossProcessLock(GetRlzStoreLockPath());
286 // At this point, we hold the in-process lock, no matter the value of
287 // |got_cross_process_lock|.
288
289 ++g_lock_depth;
290 if (!got_cross_process_lock) {
291 // Acquiring cross-process lock failed, so simply return here.
292 // In-process lock will be released in dtor.
293 DCHECK(!g_store);
Roger Tawa OOO till Jul 10th 2012/11/26 19:16:51 Maybe add DCHECK(g_lock_depth==1)? Does not happe
Ivan Korotkov 2012/11/27 11:37:25 See my comment in Lock
294 return;
295 }
296
297 if (g_lock_depth > 1) {
298 // Reuse the already existing store object.
299 DCHECK(g_store);
300 store_.reset(g_store);
301 return;
302 }
303
304 // This is the topmost lock, create a new store object.
305 DCHECK(!g_store);
306 g_store = new RlzValueStoreChromeOS(GetRlzStorePath());
307 store_.reset(g_store);
272 } 308 }
273 309
274 ScopedRlzValueStoreLock::~ScopedRlzValueStoreLock() { 310 ScopedRlzValueStoreLock::~ScopedRlzValueStoreLock() {
311 --g_lock_depth;
312 DCHECK(g_lock_depth >= 0);
313
314 if (g_lock_depth > 0) {
315 // Other locks are still using store_, so don't free it yet.
316 ignore_result(store_.release());
317 return;
318 }
319
320 g_store = NULL;
321
322 g_recursive_lock.ReleaseLock();
275 } 323 }
276 324
277 RlzValueStore* ScopedRlzValueStoreLock::GetStore() { 325 RlzValueStore* ScopedRlzValueStoreLock::GetStore() {
278 return store_; 326 return store_.get();
279 } 327 }
280 328
281 namespace testing { 329 namespace testing {
282 330
283 void SetRlzStoreDirectory(const FilePath& directory) { 331 void SetRlzStoreDirectory(const FilePath& directory) {
284 g_testing_rlz_store_path_ = directory; 332 g_testing_rlz_store_path_ = directory;
285 } 333 }
286 334
335 std::string RlzStoreFilenameStr() {
336 return GetRlzStorePath().value();
337 }
338
287 } // namespace testing 339 } // namespace testing
288 340
289 } // namespace rlz_lib 341 } // namespace rlz_lib
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698