Skip to main content

omaha_client/
app_set.rs

1// Copyright 2021 The Fuchsia Authors
2//
3// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0
4// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT
5// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option.
6// This file may not be copied, modified, or distributed except according to
7// those terms.
8
9use {
10    crate::{common::App, state_machine::update_check::AppResponse, storage::Storage},
11    futures::{future::LocalBoxFuture, prelude::*},
12};
13
14/// The trait for the platform-specific AppSet to implement.
15pub trait AppSet {
16    fn get_apps(&self) -> Vec<App>;
17    fn iter_mut_apps(&mut self) -> Box<dyn Iterator<Item = &mut App> + '_>;
18    fn get_system_app_id(&self) -> &str;
19}
20
21pub trait AppSetExt: AppSet {
22    /// Return whether all the apps in the app set are valid.
23    fn all_valid(&self) -> bool {
24        self.get_apps().iter().all(|app| app.valid())
25    }
26
27    /// Update the cohort and user counting for each app from Omaha app response.
28    fn update_from_omaha(&mut self, app_responses: &[AppResponse]) {
29        for app in self.iter_mut_apps() {
30            for app_response in app_responses {
31                if app.id == app_response.app_id {
32                    app.cohort.update_from_omaha(app_response.cohort.clone());
33                    app.user_counting = app_response.user_counting.clone();
34                    break;
35                }
36            }
37        }
38    }
39
40    /// Load data from |storage|, only overwrite existing fields if data exists.
41    #[must_use]
42    fn load<'a>(&'a mut self, storage: &'a impl Storage) -> LocalBoxFuture<'a, ()> {
43        async move {
44            for app in self.iter_mut_apps() {
45                app.load(storage).await;
46            }
47        }
48        .boxed_local()
49    }
50
51    /// Persist cohort and user counting to |storage|, will try to set all of them to storage even
52    /// if previous set fails.
53    /// It will NOT call commit() on |storage|, caller is responsible to call commit().
54    #[must_use]
55    fn persist<'a>(&'a self, storage: &'a mut impl Storage) -> LocalBoxFuture<'a, ()> {
56        async move {
57            for app in self.get_apps() {
58                app.persist(storage).await;
59            }
60        }
61        .boxed_local()
62    }
63}
64
65impl<T> AppSetExt for T where T: AppSet {}
66
67/// An AppSet implementation based on Vec, the first app will be treated as the system app.
68pub struct VecAppSet {
69    pub apps: Vec<App>,
70}
71
72impl VecAppSet {
73    /// Panics if the passed apps is empty.
74    pub fn new(apps: Vec<App>) -> Self {
75        assert!(!apps.is_empty());
76        Self { apps }
77    }
78}
79
80impl AppSet for VecAppSet {
81    fn get_apps(&self) -> Vec<App> {
82        self.apps.clone()
83    }
84    fn iter_mut_apps(&mut self) -> Box<dyn Iterator<Item = &mut App> + '_> {
85        Box::new(self.apps.iter_mut())
86    }
87    fn get_system_app_id(&self) -> &str {
88        &self.apps[0].id
89    }
90}
91
92#[cfg(test)]
93mod tests {
94    use super::*;
95    use crate::{common::UserCounting, protocol::Cohort, state_machine::update_check::Action};
96
97    #[test]
98    fn test_appsetext_update_from_omaha() {
99        let mut app_set = VecAppSet::new(vec![
100            App::builder().id("some_id").version([0, 1]).build(),
101            App::builder().id("not_updated_id").version([2]).build(),
102        ]);
103        let cohort = Cohort { name: Some("some-channel".to_string()), ..Cohort::default() };
104        let user_counting = UserCounting::ClientRegulatedByDate(Some(42));
105        let app_responses = vec![
106            AppResponse {
107                app_id: "some_id".to_string(),
108                cohort: cohort.clone(),
109                user_counting: user_counting.clone(),
110                result: Action::Updated,
111            },
112            AppResponse {
113                app_id: "some_other_id".to_string(),
114                cohort: cohort.clone(),
115                user_counting: user_counting.clone(),
116                result: Action::NoUpdate,
117            },
118        ];
119
120        app_set.update_from_omaha(&app_responses);
121        let apps = app_set.get_apps();
122        assert_eq!(cohort, apps[0].cohort);
123        assert_eq!(user_counting, apps[0].user_counting);
124
125        assert_eq!(apps[1], App::builder().id("not_updated_id").version([2]).build());
126    }
127
128    #[test]
129    fn test_appsetext_valid() {
130        let app_set = VecAppSet::new(vec![App::builder().id("some_id").version([0, 1]).build()]);
131        assert!(app_set.all_valid());
132        let app_set = VecAppSet::new(vec![
133            App::builder().id("some_id").version([0, 1]).build(),
134            App::builder().id("some_id_2").version([1]).build(),
135        ]);
136        assert!(app_set.all_valid());
137    }
138
139    #[test]
140    fn test_appsetext_not_valid() {
141        let app_set = VecAppSet::new(vec![
142            App::builder().id("some_id").version([0, 1]).build(),
143            App::builder().id("").version([0, 1]).build(),
144        ]);
145        assert!(!app_set.all_valid());
146        let app_set = VecAppSet::new(vec![
147            App::builder().id("some_id").version([0]).build(),
148            App::builder().id("some_id_2").version([0, 1]).build(),
149        ]);
150        assert!(!app_set.all_valid());
151        let app_set = VecAppSet::new(vec![
152            App::builder().id("some_id").version([0]).build(),
153            App::builder().id("").version([0, 1]).build(),
154        ]);
155        assert!(!app_set.all_valid());
156    }
157
158    #[test]
159    fn test_get_apps() {
160        let apps = vec![App::builder().id("some_id").version([0, 1]).build()];
161        let app_set = VecAppSet::new(apps.clone());
162        assert_eq!(app_set.get_apps(), apps);
163    }
164
165    #[test]
166    fn test_iter_mut_apps() {
167        let apps = vec![
168            App::builder().id("id1").version([1]).build(),
169            App::builder().id("id2").version([2]).build(),
170        ];
171        let mut app_set = VecAppSet::new(apps);
172        for app in app_set.iter_mut_apps() {
173            app.id += "_mutated";
174        }
175        assert_eq!(
176            app_set.get_apps(),
177            vec![
178                App::builder().id("id1_mutated").version([1]).build(),
179                App::builder().id("id2_mutated").version([2]).build()
180            ]
181        );
182    }
183
184    #[test]
185    fn test_get_system_app_id() {
186        let apps = vec![
187            App::builder().id("id1").version([1]).build(),
188            App::builder().id("id2").version([2]).build(),
189        ];
190        let app_set = VecAppSet::new(apps);
191        assert_eq!(app_set.get_system_app_id(), "id1");
192    }
193}