1use crate::stash_store::StashStore;
6use crate::storage_store::StorageStore;
7use anyhow::{format_err, Context, Error};
8use fidl_fuchsia_stash as fidl_stash;
9use fuchsia_component::client::connect_to_protocol;
10use wlan_metrics_registry::StashMigrationResultsMetricDimensionMigrationResult as MigrationResult;
11
12pub use wlan_storage_constants::{
13 self, Credential, NetworkIdentifier, PersistentData, PersistentStorageData, SecurityType,
14 POLICY_STORAGE_ID,
15};
16
17const FILE_PATH_FORMAT: &str = "/data/network-data.";
19
20pub struct PolicyStorage {
23 root: StorageStore,
25 legacy_stash: Result<StashStore, Error>,
29 cobalt_proxy: Option<fidl_fuchsia_metrics::MetricEventLoggerProxy>,
30}
31
32impl PolicyStorage {
33 pub async fn new_with_id(id: &str) -> Self {
36 let path = format!("{FILE_PATH_FORMAT}{id}");
37 let root = StorageStore::new(&path);
38
39 let proxy = connect_to_protocol::<fidl_stash::SecureStoreMarker>();
40 let legacy_stash =
41 proxy.and_then(|p| StashStore::from_secure_store_proxy(id, p)).map_err(|e| e);
42
43 let cobalt_proxy = init_telemetry_channel()
44 .await
45 .inspect_err(|e| {
46 log::info!(
47 "Error accessing telemetry. Stash migration metric will not be logged: {}",
48 e
49 );
50 })
51 .ok();
52
53 Self { root, legacy_stash, cobalt_proxy }
54 }
55
56 pub fn new_with_stash_proxy_and_id(
58 stash_proxy: fidl_stash::SecureStoreProxy,
59 id: &str,
60 ) -> Self {
61 let root = StorageStore::new(id);
62 let legacy_stash = StashStore::from_secure_store_proxy(id, stash_proxy);
63 let cobalt_proxy = None;
64 Self { root, legacy_stash, cobalt_proxy }
65 }
66
67 pub async fn load(&mut self) -> Result<Vec<PersistentStorageData>, Error> {
71 let load_err = match self.root.load() {
74 Ok(networks) => {
75 self.log_load_metric(MigrationResult::AlreadyMigrated).await;
76 return Ok(networks);
77 }
78 Err(e) => e,
79 };
80 let stash_store: &mut StashStore = if let Ok(stash) = self.legacy_stash.as_mut() {
81 stash
82 } else {
83 return Err(format_err!("error accessing stash"));
84 };
85 if let Ok(config) = stash_store.load().await {
87 let mut networks_list = Vec::new();
89 for (id, legacy_configs) in config.into_iter() {
90 let mut new_configs = legacy_configs
91 .into_iter()
92 .map(|c| PersistentStorageData::new_from_legacy_data(id.clone(), c));
93 networks_list.extend(&mut new_configs);
94 }
95
96 match self.root.write(networks_list.clone()) {
98 Ok(_) => {
99 log::info!("Migrated saved networks from stash");
100 let delete_result = stash_store.delete_store().await;
102 match delete_result {
103 Ok(()) => {
104 self.log_load_metric(MigrationResult::Success).await;
105 }
106 Err(e) => {
107 log::info!(
108 "Failed to delete legacy stash data after migration: {:?}",
109 e
110 );
111 self.log_load_metric(MigrationResult::MigratedButFailedToDeleteLegacy)
112 .await;
113 }
114 }
115 }
116 Err(e) => {
117 log::info!(e:?; "Failed to write migrated saved networks");
118 self.log_load_metric(MigrationResult::FailedToWriteNewStore).await;
119 }
120 }
121 Ok(networks_list)
122 } else {
123 log::info!(load_err:?; "Failed to read saved networks from file and legacy stash, new file will be created when a network is saved",);
127 self.log_load_metric(MigrationResult::FailedToLoadLegacyData).await;
128 Ok(Vec::new())
129 }
130 }
131
132 pub fn write(&self, network_configs: Vec<PersistentStorageData>) -> Result<(), Error> {
135 self.root.write(network_configs)
136 }
137
138 pub fn clear(&mut self) -> Result<(), Error> {
141 self.root.empty_store()
142 }
143
144 async fn log_load_metric(&self, result_event_code: MigrationResult) {
145 let cobalt_proxy = match &self.cobalt_proxy {
148 Some(proxy) => proxy,
149 None => return,
150 };
151
152 let events = &[fidl_fuchsia_metrics::MetricEvent {
153 metric_id: wlan_metrics_registry::STASH_MIGRATION_RESULTS_METRIC_ID,
154 event_codes: vec![result_event_code as u32],
155 payload: fidl_fuchsia_metrics::MetricEventPayload::Count(1),
156 }];
157
158 match cobalt_proxy.log_metric_events(events).await {
160 Err(e) => {
161 log::info!(
162 "Error logging metric {:?} for migration result: {:?}",
163 result_event_code,
164 e
165 );
166 }
167 Ok(Err(e)) => {
168 log::info!(
169 "Error sending metric {:?} for migration result: {:?}",
170 result_event_code,
171 e
172 );
173 }
174 Ok(_) => (),
175 }
176 }
177}
178
179async fn init_telemetry_channel() -> Result<fidl_fuchsia_metrics::MetricEventLoggerProxy, Error> {
180 let factory_proxy = fuchsia_component::client::connect_to_protocol::<
182 fidl_fuchsia_metrics::MetricEventLoggerFactoryMarker,
183 >()?;
184
185 let (cobalt_proxy, cobalt_1dot1_server) =
186 fidl::endpoints::create_proxy::<fidl_fuchsia_metrics::MetricEventLoggerMarker>();
187
188 let project_spec = fidl_fuchsia_metrics::ProjectSpec {
189 customer_id: None, project_id: Some(wlan_metrics_registry::PROJECT_ID),
191 ..Default::default()
192 };
193
194 factory_proxy
195 .create_metric_event_logger(&project_spec, cobalt_1dot1_server)
196 .await
197 .context("failed to create metrics event logger")?
198 .map_err(|e| format_err!("failed to create metrics event logger: {:?}", e))?;
199
200 Ok(cobalt_proxy)
201}
202
203#[cfg(test)]
204mod tests {
205 use super::*;
206 use crate::tests::{network_id, rand_string};
207 use assert_matches::assert_matches;
208 use fidl::endpoints::create_request_stream;
209 use fidl_fuchsia_stash::{SecureStoreRequest, StoreAccessorRequest};
210 use fuchsia_async as fasync;
211 use futures::task::Poll;
212 use futures::{StreamExt, TryStreamExt};
213 use ieee80211::Ssid;
214 use std::convert::TryFrom;
215 use std::io::Write;
216 use std::sync::atomic::{AtomicBool, Ordering};
217 use std::sync::Arc;
218 use wlan_storage_constants::PersistentData;
219
220 pub const PSK_BYTE_LEN: usize = 32;
224
225 #[fuchsia::test]
226 async fn write_and_read() {
227 let mut store = new_storage(&rand_string()).await;
228 let cfg = PersistentStorageData {
229 ssid: Ssid::try_from("foo").unwrap().to_vec(),
230 security_type: SecurityType::Wpa2,
231 credential: Credential::Password(b"password".to_vec()),
232 has_ever_connected: true,
233 };
234
235 store.write(vec![cfg.clone()]).expect("Failed writing to storage");
237
238 let cfgs_from_store = store.load().await.expect("Failed reading from storage");
240 assert_eq!(1, cfgs_from_store.len());
241 assert_eq!(vec![cfg.clone()], cfgs_from_store);
242
243 let cfg_2 = PersistentStorageData {
245 ssid: Ssid::try_from("foo").unwrap().to_vec(),
246 security_type: SecurityType::Wpa2,
247 credential: Credential::Password(b"other-password".to_vec()),
248 has_ever_connected: false,
249 };
250 store.write(vec![cfg.clone(), cfg_2.clone()]).expect("Failed writing to storage");
251
252 let cfgs_from_store = store.load().await.expect("Failed reading from stash");
254 assert_eq!(2, cfgs_from_store.len());
255 assert!(cfgs_from_store.contains(&cfg));
256 assert!(cfgs_from_store.contains(&cfg_2));
257 }
258
259 #[fuchsia::test]
260 async fn write_read_security_types() {
261 let mut store = new_storage(&rand_string()).await;
262 let password = Credential::Password(b"config-password".to_vec());
263
264 let cfg_open = PersistentStorageData {
266 ssid: Ssid::try_from("foo").unwrap().to_vec(),
267 security_type: SecurityType::None,
268 credential: Credential::None,
269 has_ever_connected: false,
270 };
271 let cfg_wep = PersistentStorageData {
272 ssid: Ssid::try_from("foo").unwrap().to_vec(),
273 security_type: SecurityType::Wep,
274 credential: password.clone(),
275 has_ever_connected: false,
276 };
277 let cfg_wpa = PersistentStorageData {
278 ssid: Ssid::try_from("foo").unwrap().to_vec(),
279 security_type: SecurityType::Wpa,
280 credential: password.clone(),
281 has_ever_connected: false,
282 };
283 let cfg_wpa2 = PersistentStorageData {
284 ssid: Ssid::try_from("foo").unwrap().to_vec(),
285 security_type: SecurityType::Wpa2,
286 credential: password.clone(),
287 has_ever_connected: false,
288 };
289 let cfg_wpa3 = PersistentStorageData {
290 ssid: Ssid::try_from("foo").unwrap().to_vec(),
291 security_type: SecurityType::Wpa3,
292 credential: password.clone(),
293 has_ever_connected: false,
294 };
295
296 let mut saved_networks = vec![cfg_open.clone()];
298 store.write(saved_networks.clone()).expect("failed to write config");
299 saved_networks.push(cfg_wep.clone());
300 store.write(saved_networks.clone()).expect("failed to write config");
301 saved_networks.push(cfg_wpa.clone());
302 store.write(saved_networks.clone()).expect("failed to write config");
303 saved_networks.push(cfg_wpa2.clone());
304 store.write(saved_networks.clone()).expect("failed to write config");
305 saved_networks.push(cfg_wpa3.clone());
306 store.write(saved_networks.clone()).expect("failed to write config");
307
308 let configs = store.load().await.expect("failed loading from storage");
310 assert_eq!(configs.len(), 5);
311 assert!(configs.contains(&cfg_open));
312 assert!(configs.contains(&cfg_wep));
313 assert!(configs.contains(&cfg_wpa));
314 assert!(configs.contains(&cfg_wpa2));
315 assert!(configs.contains(&cfg_wpa3));
316 }
317
318 #[fuchsia::test]
319 async fn write_read_credentials() {
320 let mut store = new_storage(&rand_string()).await;
321
322 let password = Credential::Password(b"config-password".to_vec());
324 let psk = Credential::Psk([65; PSK_BYTE_LEN].to_vec());
325
326 let cfg_none = PersistentStorageData {
327 ssid: b"bar-none".to_vec(),
328 security_type: SecurityType::None,
329 credential: Credential::None,
330 has_ever_connected: false,
331 };
332 let cfg_password = PersistentStorageData {
333 ssid: b"bar-password".to_vec(),
334 security_type: SecurityType::Wpa2,
335 credential: password,
336 has_ever_connected: false,
337 };
338 let cfg_psk = PersistentStorageData {
339 ssid: b"bar-psk".to_vec(),
340 security_type: SecurityType::Wpa2,
341 credential: psk,
342 has_ever_connected: false,
343 };
344
345 let mut saved_networks = vec![cfg_none.clone()];
347 store.write(saved_networks.clone()).expect("failed to write");
348 saved_networks.push(cfg_password.clone());
349 store.write(saved_networks.clone()).expect("failed to write");
350 saved_networks.push(cfg_psk.clone());
351 store.write(saved_networks.clone()).expect("failed to write");
352
353 let configs = store.load().await.expect("failed loading from storage");
355 assert_eq!(3, configs.len());
356 assert!(configs.contains(&cfg_none));
357 assert!(configs.contains(&cfg_password));
358 assert!(configs.contains(&cfg_psk));
359 }
360
361 #[fuchsia::test]
362 async fn write_persists() {
363 let path = rand_string();
364 let store = new_storage(&path).await;
365 let cfg = PersistentStorageData {
366 ssid: Ssid::try_from("foo").unwrap().to_vec(),
367 security_type: SecurityType::Wpa2,
368 credential: Credential::Password(b"password".to_vec()),
369 has_ever_connected: true,
370 };
371
372 store.write(vec![cfg.clone()]).expect("Failed writing to storage");
374
375 let mut store = PolicyStorage::new_with_id(&path).await;
377
378 let cfgs_from_store = store.load().await.expect("Failed reading from storage");
380 assert_eq!(1, cfgs_from_store.len());
381 assert!(cfgs_from_store.contains(&cfg));
382 }
383
384 #[fuchsia::test]
385 async fn load_storage() {
386 let mut store = new_storage(&rand_string()).await;
387 let cfg_foo = PersistentStorageData {
388 ssid: Ssid::try_from("foo").unwrap().to_vec(),
389 security_type: SecurityType::Wpa2,
390 credential: Credential::Password(b"12345678".to_vec()),
391 has_ever_connected: true,
392 };
393 let cfg_bar = PersistentStorageData {
394 ssid: Ssid::try_from("bar").unwrap().to_vec(),
395 security_type: SecurityType::Wpa2,
396 credential: Credential::Password(b"qwertyuiop".to_vec()),
397 has_ever_connected: true,
398 };
399
400 store
402 .write(vec![cfg_foo.clone(), cfg_bar.clone()])
403 .expect("Failed to save configs to stash");
404
405 let expected_cfgs = vec![cfg_foo, cfg_bar];
407 assert_eq!(expected_cfgs, store.load().await.expect("Failed to load configs from stash"));
408 }
409
410 #[fuchsia::test]
411 async fn load_empty_storage_does_loads_empty_list() {
412 let store_id = &rand_string();
413 let mut store = new_storage(&store_id).await;
414
415 store.write(vec![]).expect("failed to write value");
417
418 let loaded_configs = store.load().await.expect("failed to load store");
420 assert!(loaded_configs.is_empty());
421 }
422
423 #[fuchsia::test]
424 pub async fn load_no_file_creates_file() {
425 let store_id = &rand_string();
429 let backing_file_path = format!("{}{}", FILE_PATH_FORMAT, store_id).to_string();
430 let mut store = PolicyStorage::new_with_id(store_id).await;
431
432 std::fs::read(&backing_file_path).expect_err("The file for the store should not exist yet");
434
435 let loaded_configs = store.load().await.expect("failed to load store");
437 assert_eq!(loaded_configs, vec![]);
438
439 let file_contents = std::fs::read(&backing_file_path).expect(
442 "Failed to read file that should have been created when loading non-existant file",
443 );
444 assert!(!file_contents.is_empty());
445
446 let cfg_id = NetworkIdentifier {
448 ssid: Ssid::try_from(rand_string().as_str()).unwrap().to_vec(),
449 security_type: SecurityType::Wpa2,
450 };
451 let cfg = PersistentData {
452 credential: Credential::Password(rand_string().as_bytes().to_vec()),
453 has_ever_connected: true,
454 };
455
456 match store.legacy_stash.as_mut() {
457 Ok(stash) => {
458 stash.write(&cfg_id, &[cfg]).await.expect("Failed writing to legacy stash");
459 stash.flush().await.expect("Failed to flush legacy stash");
460 }
461 Err(e) => {
462 panic!("error initializing legacy stash: {}", e);
463 }
464 }
465 let loaded_configs = store.load().await.expect("failed to load store");
466 assert!(loaded_configs.is_empty());
467
468 let file_contents = std::fs::read(&backing_file_path).expect(
470 "Failed to read file that should have been created when loading non-existant file",
471 );
472 assert!(!file_contents.is_empty());
473 }
474
475 #[fuchsia::test]
476 async fn clear_storage() {
477 let storage_id = &rand_string();
478 let mut storage = new_storage(&storage_id).await;
479
480 let cfg_foo = PersistentStorageData {
482 ssid: Ssid::try_from("foo").unwrap().to_vec(),
483 security_type: SecurityType::Wpa2,
484 credential: Credential::Password(b"qwertyuio".to_vec()),
485 has_ever_connected: true,
486 };
487 let cfg_bar = PersistentStorageData {
488 ssid: Ssid::try_from("bar").unwrap().to_vec(),
489 security_type: SecurityType::Wpa2,
490 credential: Credential::Password(b"12345678".to_vec()),
491 has_ever_connected: false,
492 };
493 storage.write(vec![cfg_foo.clone(), cfg_bar.clone()]).expect("Failed to write to storage");
494
495 let configs_from_storage = storage.load().await.expect("Failed to read");
497 assert_eq!(2, configs_from_storage.len());
498 assert!(configs_from_storage.contains(&cfg_foo));
499 assert!(configs_from_storage.contains(&cfg_bar));
500
501 storage.clear().expect("Failed to clear storage");
503 let configs_from_storage = storage.load().await.expect("Failed to read");
505 assert_eq!(0, configs_from_storage.len());
506
507 let mut storage = PolicyStorage::new_with_id(storage_id).await;
509 let configs_from_storage = storage.load().await.expect("Failed to read");
510 assert_eq!(0, configs_from_storage.len());
511 }
512
513 #[fuchsia::test]
514 async fn test_migration() {
515 let storage_id = rand_string();
516 let stash_client = connect_to_protocol::<fidl_stash::SecureStoreMarker>()
517 .expect("failed to connect to store");
518 let ssid = "foo";
519 let security_type = SecurityType::Wpa2;
520 let credential = Credential::Password(b"password".to_vec());
521 let has_ever_connected = false;
522 let network_id = network_id(ssid, security_type);
524 let previous_data = PersistentData { credential: credential.clone(), has_ever_connected };
525
526 let network_config = vec![PersistentStorageData {
528 ssid: ssid.into(),
529 security_type: security_type,
530 credential: credential.clone(),
531 has_ever_connected,
532 }];
533
534 let stash = StashStore::from_secure_store_proxy(&storage_id, stash_client.clone())
536 .expect("failed to get stash proxy");
537 stash.write(&network_id, &vec![previous_data]).await.expect("write failed");
538
539 let mut storage = PolicyStorage::new_with_id(&storage_id).await;
541 storage.legacy_stash = Ok(stash);
542 assert_eq!(storage.load().await.expect("load failed"), network_config);
543
544 let stash_client = connect_to_protocol::<fidl_stash::SecureStoreMarker>()
547 .expect("failed to connect to store");
548 let stash = StashStore::from_secure_store_proxy(&storage_id, stash_client)
549 .expect("failed to get stash proxy");
550 assert!(stash.load().await.expect("load failed").is_empty());
551
552 let mut storage = PolicyStorage::new_with_id(&storage_id).await;
554 assert_eq!(storage.load().await.expect("load failed"), network_config);
555 }
556
557 #[fuchsia::test]
558 async fn test_migration_with_bad_stash() {
559 let store_id = rand_string();
560
561 let (client, mut request_stream) = create_request_stream::<fidl_stash::SecureStoreMarker>();
562
563 let read_from_stash = Arc::new(AtomicBool::new(false));
566
567 let _task = {
571 let read_from_stash = read_from_stash.clone();
572 fasync::Task::spawn(async move {
573 while let Some(request) = request_stream.next().await {
574 match request.unwrap() {
575 SecureStoreRequest::Identify { .. } => {}
576 SecureStoreRequest::CreateAccessor { accessor_request, .. } => {
577 let read_from_stash = read_from_stash.clone();
578 fuchsia_async::Task::spawn(async move {
579 let mut request_stream = accessor_request.into_stream();
580 while let Some(request) = request_stream.next().await {
581 match request.unwrap() {
582 StoreAccessorRequest::ListPrefix { .. } => {
583 read_from_stash.store(true, Ordering::Relaxed);
584 }
587 _ => unreachable!(),
588 }
589 }
590 })
591 .detach();
592 }
593 }
594 }
595 })
596 };
597
598 let mut store = PolicyStorage::new_with_id(&store_id).await;
600 let proxy_fn = client.into_proxy();
601 store.legacy_stash = StashStore::from_secure_store_proxy(&store_id, proxy_fn);
602
603 assert!(&store.load().await.expect("load failed").is_empty());
605
606 assert!(read_from_stash.load(Ordering::Relaxed));
608 }
609
610 pub async fn new_storage(id: &str) -> PolicyStorage {
613 let mut store = PolicyStorage::new_with_id(id).await;
614 store.clear().expect("failed to clear stash");
615 store
616 }
617
618 struct MetricsTestValues {
620 store: PolicyStorage,
621 cobalt_stream: fidl_fuchsia_metrics::MetricEventLoggerRequestStream,
622 stash_stream: fidl_stash::SecureStoreRequestStream,
623 }
624
625 fn migration_metrics_test_values() -> MetricsTestValues {
629 let (cobalt_proxy, cobalt_stream) = fidl::endpoints::create_proxy_and_stream::<
630 fidl_fuchsia_metrics::MetricEventLoggerMarker,
631 >();
632
633 let (legacy_stash, stash_stream) = stash_for_test();
634 let root = StorageStore::new(format!("{FILE_PATH_FORMAT}{}", rand_string()));
635 let store = PolicyStorage { root, legacy_stash, cobalt_proxy: Some(cobalt_proxy) };
636
637 MetricsTestValues { store, cobalt_stream, stash_stream }
638 }
639
640 fn stash_for_test() -> (Result<StashStore, Error>, fidl_stash::SecureStoreRequestStream) {
641 let (client, stash_stream) = create_request_stream::<fidl_stash::SecureStoreMarker>();
642 let proxy_fn = client.into_proxy();
643 let id = rand_string();
644 let legacy_stash = StashStore::from_secure_store_proxy(&id, proxy_fn);
645
646 (legacy_stash, stash_stream)
647 }
648
649 fn check_load_metric(
652 logged_metric: fidl_fuchsia_metrics::MetricEventLoggerRequest,
653 expected_event: MigrationResult,
654 ) {
655 assert_matches!(logged_metric, fidl_fuchsia_metrics::MetricEventLoggerRequest::LogMetricEvents {
656 mut events, responder, ..
657 } => {
658 assert_eq!(events.len(), 1);
659 let event = events.pop().unwrap();
660 assert_matches!(event, fidl_fuchsia_metrics::MetricEvent { metric_id, event_codes, payload: _payload } => {
661 assert_eq!(metric_id, wlan_metrics_registry::STASH_MIGRATION_RESULTS_METRIC_ID);
662 assert_eq!(event_codes, [expected_event as u32]);
663 });
664
665 assert!(responder.send(Ok(())).is_ok());
666 });
667 }
668
669 fn process_init_stash(
670 exec: &mut fasync::TestExecutor,
671 mut stash_stream: fidl_stash::SecureStoreRequestStream,
672 ) -> fidl_stash::StoreAccessorRequestStream {
673 assert_matches!(
674 exec.run_until_stalled(&mut stash_stream.next()),
675 Poll::Ready(Some(Ok(SecureStoreRequest::Identify { .. })))
676 );
677
678 let accessor_req_stream = assert_matches!(
679 exec.run_until_stalled(&mut stash_stream.next()),
680 Poll::Ready(Some(Ok(SecureStoreRequest::CreateAccessor { accessor_request, .. }))) =>
681 {
682 accessor_request.into_stream()
683 });
684
685 accessor_req_stream
686 }
687
688 fn respond_to_stash_list_prefix(
691 exec: &mut fasync::TestExecutor,
692 stash_server: &mut fidl_stash::StoreAccessorRequestStream,
693 ) {
694 let request = assert_matches!(exec.run_until_stalled(&mut stash_server.next()), Poll::Ready(req) => {
695 req.expect("ListPrefix stash request not recieved.")
696 });
697 match request.unwrap() {
698 StoreAccessorRequest::ListPrefix { it, .. } => {
699 let mut iter = it.into_stream();
700 assert_matches!(
701 exec.run_until_stalled(&mut iter.try_next()),
702 Poll::Ready(Ok(Some(fidl_stash::ListIteratorRequest::GetNext { responder }))) => {
703 responder.send(&[]).expect("error sending stash response");
704 });
705 }
706 _ => unreachable!(),
707 }
708 }
709
710 fn process_stash_delete(
711 exec: &mut fasync::TestExecutor,
712 stash_server: &mut fidl_stash::StoreAccessorRequestStream,
713 ) {
714 let request = assert_matches!(exec.run_until_stalled(&mut stash_server.next()), Poll::Ready(req) => {
716 req.expect("DeletePrefix stash request not recieved.")
717 });
718 match request.unwrap() {
719 StoreAccessorRequest::DeletePrefix { .. } => {}
720 _ => unreachable!(),
721 }
722
723 assert_matches!(
725 exec.run_until_stalled(&mut stash_server.try_next()),
726 Poll::Ready(Ok(Some(fidl_stash::StoreAccessorRequest::Flush{responder}))) => {
727 responder.send(Ok(())).expect("failed to send stash response");
728 }
729 );
730 }
731
732 #[fuchsia::test]
733 pub fn test_load_logs_result_success_metric() {
734 let mut exec = fasync::TestExecutor::new();
735 let mut test_values = migration_metrics_test_values();
738
739 {
741 let load_fut = test_values.store.load();
742 futures::pin_mut!(load_fut);
743 assert_matches!(exec.run_until_stalled(&mut load_fut), Poll::Pending);
744
745 let mut accessor_req_stream = process_init_stash(&mut exec, test_values.stash_stream);
747 assert_matches!(exec.run_until_stalled(&mut load_fut), Poll::Pending);
748
749 respond_to_stash_list_prefix(&mut exec, &mut accessor_req_stream);
751 assert_matches!(exec.run_until_stalled(&mut load_fut), Poll::Pending);
752
753 process_stash_delete(&mut exec, &mut accessor_req_stream);
755 assert_matches!(exec.run_until_stalled(&mut load_fut), Poll::Pending);
756
757 let mut metric_fut = test_values.cobalt_stream.next();
758 assert_matches!(exec.run_until_stalled(&mut metric_fut), Poll::Ready(Some(Ok(logged_metric))) => {
759 check_load_metric(logged_metric, MigrationResult::Success);
760 });
761 assert_matches!(exec.run_until_stalled(&mut load_fut), Poll::Ready(Ok(_)));
762 }
763
764 let load_fut = test_values.store.load();
767 futures::pin_mut!(load_fut);
768 assert_matches!(exec.run_until_stalled(&mut load_fut), Poll::Pending);
769
770 let mut metric_fut = test_values.cobalt_stream.next();
771 assert_matches!(exec.run_until_stalled(&mut metric_fut), Poll::Ready(Some(Ok(logged_metric))) => {
772 check_load_metric(logged_metric, MigrationResult::AlreadyMigrated);
773 });
774
775 assert_matches!(exec.run_until_stalled(&mut load_fut), Poll::Ready(Ok(_)));
776
777 assert_matches!(exec.run_until_stalled(&mut metric_fut), Poll::Pending);
779 }
780
781 #[fuchsia::test]
782 pub fn test_load_failure_logs_result_metric() {
783 let mut exec = fuchsia_async::TestExecutor::new();
784 let mut test_values = migration_metrics_test_values();
785 let load_fut = test_values.store.load();
786 futures::pin_mut!(load_fut);
787
788 assert_matches!(exec.run_until_stalled(&mut load_fut), Poll::Pending);
791
792 let mut accessor_req_stream = process_init_stash(&mut exec, test_values.stash_stream);
794 assert_matches!(exec.run_until_stalled(&mut load_fut), Poll::Pending);
795
796 let request = assert_matches!(exec.run_until_stalled(&mut accessor_req_stream.next()), Poll::Ready(req) => {
798 req.expect("ListPrefix stash request not recieved.")
799 });
800 match request.unwrap() {
801 StoreAccessorRequest::ListPrefix { it: _, .. } => {
802 }
804 _ => unreachable!(),
805 }
806
807 assert_matches!(exec.run_until_stalled(&mut load_fut), Poll::Pending);
809
810 assert_matches!(exec.run_until_stalled(&mut test_values.cobalt_stream.next()), Poll::Ready(Some(Ok(metric))) => {
812 check_load_metric(metric, MigrationResult::FailedToLoadLegacyData);
813 });
814
815 assert_matches!(exec.run_until_stalled(&mut load_fut), Poll::Ready(Ok(data)) => {
817 assert!(data.is_empty());
818 });
819
820 assert_matches!(
822 exec.run_until_stalled(&mut test_values.cobalt_stream.next()),
823 Poll::Pending
824 );
825 }
826
827 #[fuchsia::test]
828 pub fn test_load_delete_stash_failure_logs_result_metric() {
829 let mut exec = fuchsia_async::TestExecutor::new();
830 let mut test_values = migration_metrics_test_values();
831 let load_fut = test_values.store.load();
832 futures::pin_mut!(load_fut);
833
834 assert_matches!(exec.run_until_stalled(&mut load_fut), Poll::Pending);
837
838 let mut accessor_req_stream = process_init_stash(&mut exec, test_values.stash_stream);
840 assert_matches!(exec.run_until_stalled(&mut load_fut), Poll::Pending);
841
842 respond_to_stash_list_prefix(&mut exec, &mut accessor_req_stream);
844
845 drop(accessor_req_stream);
847
848 assert_matches!(exec.run_until_stalled(&mut load_fut), Poll::Pending);
850
851 assert_matches!(exec.run_until_stalled(&mut test_values.cobalt_stream.next()), Poll::Ready(Some(Ok(metric))) => {
853 check_load_metric(metric, MigrationResult::MigratedButFailedToDeleteLegacy);
854 });
855
856 assert_matches!(exec.run_until_stalled(&mut load_fut), Poll::Ready(Ok(data)) => {
858 assert!(data.is_empty());
859 });
860
861 assert_matches!(
863 exec.run_until_stalled(&mut test_values.cobalt_stream.next()),
864 Poll::Pending
865 );
866 }
867
868 #[fuchsia::test]
869 pub fn test_load_logs_result_failed_to_write_metric() {
870 let mut exec = fasync::TestExecutor::new();
871 let store_id = "//";
873 let mut test_values = migration_metrics_test_values();
874
875 test_values.store.root = StorageStore::new(std::path::Path::new(store_id));
877
878 let load_fut = test_values.store.load();
880 futures::pin_mut!(load_fut);
881 assert_matches!(exec.run_until_stalled(&mut load_fut), Poll::Pending);
882
883 let mut accessor_req_stream = process_init_stash(&mut exec, test_values.stash_stream);
885 assert_matches!(exec.run_until_stalled(&mut load_fut), Poll::Pending);
886
887 respond_to_stash_list_prefix(&mut exec, &mut accessor_req_stream);
889 assert_matches!(exec.run_until_stalled(&mut load_fut), Poll::Pending);
890
891 let mut metric_fut = test_values.cobalt_stream.next();
893 assert_matches!(exec.run_until_stalled(&mut metric_fut), Poll::Ready(Some(Ok(logged_metric))) => {
894 check_load_metric(logged_metric, MigrationResult::FailedToWriteNewStore);
895 });
896
897 assert_matches!(exec.run_until_stalled(&mut load_fut), Poll::Ready(Ok(_)));
898
899 assert_matches!(
901 exec.run_until_stalled(&mut test_values.cobalt_stream.next()),
902 Poll::Pending
903 );
904 }
905
906 #[fuchsia::test]
907 pub fn load_wpa2_and_open_network_golden_file_test() {
908 let file_contents =
915 "{\"saved_networks\":[\
916 {\
917 \"ssid\":[115,111,109,101,45,110,101,116,119,111,114,107],\
918 \"security_type\":\"Wpa2\",\
919 \"credential\":{\"Password\":[115,111,109,101,45,112,97,115,115,119,111,114,100]},\
920 \"has_ever_connected\":false
921 },\
922 {
923 \"ssid\":[111,112,101,110,45,110,101,116,119,111,114,107],\
924 \"security_type\":\"None\",\
925 \"credential\":\"None\",\
926 \"has_ever_connected\":true\
927 }],\
928 \"version\":1\
929 }"
930 .as_bytes();
931 let store_id = &rand_string();
932
933 let network_configs = vec![
934 PersistentStorageData {
935 ssid: vec![115, 111, 109, 101, 45, 110, 101, 116, 119, 111, 114, 107],
936 security_type: SecurityType::Wpa2,
937 credential: Credential::Password(vec![100, 100, 100, 100, 100, 100]),
938 has_ever_connected: false,
939 },
940 PersistentStorageData {
941 ssid: vec![111, 112, 101, 110, 45, 110, 101, 116, 119, 111, 114, 107],
942 security_type: SecurityType::None,
943 credential: Credential::None,
944 has_ever_connected: true,
945 },
946 ];
947
948 let path = format!("/data/config.{}", store_id);
950 let mut file = std::fs::File::create(&path).expect("failed to open file for writing");
951 assert_eq!(
952 file.write(&file_contents).expect("Failed to write to file"),
953 file_contents.len()
954 );
955 file.flush().expect("failed to flush contents of file");
956
957 let store = StorageStore::new(&path);
959 let loaded_configs = store.load().expect("load failed");
960 assert_eq!(loaded_configs.len(), network_configs.len());
961 for config in network_configs.iter() {
962 assert!(network_configs.contains(config));
963 }
964 }
965}