blob: ef50f2d76a93fac12e6b787f205f31e1435e287f [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use {
super::*,
fidl_fuchsia_recovery::FactoryResetRequestStream,
fidl_fuchsia_update_channel::ProviderRequestStream,
fuchsia_async as fasync,
futures::prelude::*,
parking_lot::Mutex,
serde_json::json,
std::{fs, sync::Arc},
tempfile::TempDir,
};
fn spawn_update_info_with_channel(mut stream: ProviderRequestStream, channel: String) {
fasync::Task::spawn(async move {
let req = stream
.try_next()
.await
.expect("Failed to get request from stream")
.expect("Failed to get request from stream");
let responder = req.into_get_current().expect("Got unexpected Provider request.");
responder.send(&channel).expect("Failed to send response");
})
.detach();
}
fn spawn_factory_reset(mut stream: FactoryResetRequestStream) -> Arc<Mutex<i32>> {
let call_count = Arc::new(Mutex::new(0));
let ret = call_count.clone();
fasync::Task::spawn(async move {
let req = stream
.try_next()
.await
.expect("Failed to get request from stream")
.expect("Failed to get request from stream");
let responder = req.into_reset().expect("Got unexpected FactoryReset request.");
responder.send(0).expect("Failed to send response");
*call_count.lock() += 1;
})
.detach();
ret
}
fn spawn(dir: &TempDir, channel: &str) -> (ForcedFDR, Arc<Mutex<i32>>) {
let (mock, info_stream, fdr_stream) =
ForcedFDR::new_mock(dir.path().to_path_buf(), dir.path().to_path_buf());
spawn_update_info_with_channel(info_stream, channel.into());
let fdr_call_count = spawn_factory_reset(fdr_stream);
(mock, fdr_call_count)
}
fn write_config_file(dir: &TempDir, contents: String) {
fs::write(dir.path().join("forced-fdr-channel-indices.config"), contents)
.expect("write forced-fdr-channel-indices.config")
}
fn write_config_channels(dir: &TempDir, items: Vec<(&str, i32)>) {
let map: HashMap<&str, i32> = items.iter().cloned().collect();
let json = json!({
"version": "1",
"content": {
"channel_indices": map
}
});
write_config_file(dir, json.to_string())
}
fn write_stored_index(dir: &TempDir, index: StoredIndex) {
let path = dir.path().join("stored-index.json");
let contents = serde_json::to_string(&index).expect("serialize StoredIndex to string");
fs::write(path, contents).expect("write stored index")
}
fn assert_factory_reset_triggered(call_count: Arc<Mutex<i32>>) {
let counter = call_count.lock();
assert_eq!(*counter, 1);
}
fn assert_factory_reset_not_triggered(call_count: Arc<Mutex<i32>>) {
let counter = call_count.lock();
assert_eq!(*counter, 0);
}
fn assert_index_written(dir: &TempDir, index: StoredIndex) {
let path = dir.path().join("stored-index.json");
let actual = fs::read_to_string(path).expect("File should be written");
let expected = serde_json::to_string(&index).expect("serialize StoredIndex to string");
assert_eq!(actual, expected)
}
#[fasync::run_singlethreaded(test)]
async fn test_it_fdrs_nominally() {
// Setup
let dir = TempDir::new().expect("create tempdir");
write_config_channels(&dir, vec![("channel-one", 29), ("channel-two", 44)]);
let stored_index = StoredIndex::Version1 { channel: "channel-two".into(), index: 18 };
write_stored_index(&dir, stored_index);
let (mock, fdr_call_count) = spawn(&dir, "channel-two");
// Act
let result = run(mock).await;
// Assert
assert!(result.is_ok());
assert_factory_reset_triggered(fdr_call_count);
}
#[fasync::run_singlethreaded(test)]
async fn test_it_fdrs_nominally_at_boundry() {
// Setup
let dir = TempDir::new().expect("create tempdir");
write_config_channels(&dir, vec![("channel-one", 29), ("channel-two", 44)]);
let stored_index = StoredIndex::Version1 { channel: "channel-two".into(), index: 43 };
write_stored_index(&dir, stored_index);
let (mock, fdr_call_count) = spawn(&dir, "channel-two");
// Act
let result = run(mock).await;
// Assert
assert!(result.is_ok());
assert_factory_reset_triggered(fdr_call_count);
}
#[fasync::run_singlethreaded(test)]
async fn test_it_does_not_fdr_when_equal_index() {
// Setup
let dir = TempDir::new().expect("create tempdir");
write_config_channels(&dir, vec![("channel-one", 12), ("channel-two", 17)]);
let stored_index = StoredIndex::Version1 { channel: "channel-two".into(), index: 17 };
write_stored_index(&dir, stored_index);
let (mock, fdr_call_count) = spawn(&dir, "channel-two");
// Act
let result = run(mock).await;
// Assert
assert!(result.is_err());
assert_factory_reset_not_triggered(fdr_call_count);
}
#[fasync::run_singlethreaded(test)]
async fn test_it_does_not_fdr_when_greater_index() {
// Setup
let dir = TempDir::new().expect("create tempdir");
write_config_channels(&dir, vec![("channel-one", 29), ("channel-two", 17)]);
let stored_index = StoredIndex::Version1 { channel: "channel-two".into(), index: 18 };
write_stored_index(&dir, stored_index);
let (mock, fdr_call_count) = spawn(&dir, "channel-two");
// Act
let result = run(mock).await;
// Assert
assert!(result.is_err());
assert_factory_reset_not_triggered(fdr_call_count);
}
#[fasync::run_singlethreaded(test)]
async fn test_it_does_not_fdr_when_channel_not_in_list() {
// Setup
let dir = TempDir::new().expect("create tempdir");
write_config_channels(&dir, vec![("channel-one", 44), ("channel-two", 29)]);
let stored_index = StoredIndex::Version1 { channel: "channel-three".into(), index: 18 };
write_stored_index(&dir, stored_index);
let (mock, fdr_call_count) = spawn(&dir, "channel-three");
// Act
let result = run(mock).await;
// Assert
assert!(result.is_err());
assert_factory_reset_not_triggered(fdr_call_count);
}
#[fasync::run_singlethreaded(test)]
async fn test_it_writes_stored_file_when_missing() {
// Setup
let dir = TempDir::new().expect("create tempdir");
write_config_channels(&dir, vec![("channel-one", 29), ("channel-two", 44)]);
// Skipping: write_stored_index(..);
let (mock, fdr_call_count) = spawn(&dir, "channel-two");
// Act
let result = run(mock).await;
// Assert
assert!(result.is_ok());
let stored_index = StoredIndex::Version1 { channel: "channel-two".into(), index: 44 };
assert_index_written(&dir, stored_index);
assert_factory_reset_not_triggered(fdr_call_count);
}
#[fasync::run_singlethreaded(test)]
async fn test_it_writes_stored_file_when_file_empty() {
// Setup
let dir = TempDir::new().expect("create tempdir");
write_config_channels(&dir, vec![("channel-one", 29), ("channel-two", 56)]);
// Write empty file
let path = dir.path().join("stored-index.json");
fs::write(path, "").expect("write stored index");
let (mock, fdr_call_count) = spawn(&dir, "channel-two");
// Act
let result = run(mock).await;
// Assert
assert!(result.is_ok());
let stored_index = StoredIndex::Version1 { channel: "channel-two".into(), index: 56 };
assert_index_written(&dir, stored_index);
assert_factory_reset_not_triggered(fdr_call_count);
}
#[fasync::run_singlethreaded(test)]
async fn test_it_writes_stored_file_when_file_invalid() {
// Setup
let dir = TempDir::new().expect("create tempdir");
write_config_channels(&dir, vec![("channel-one", 29), ("channel-two", 63)]);
// Write invalid file
let path = dir.path().join("stored-index.json");
fs::write(path, "SOME INVALID \n\ntype of file []{}//\\123456768790)(*&^%$#@!")
.expect("write stored index");
let (mock, fdr_call_count) = spawn(&dir, "channel-two");
// Act
let result = run(mock).await;
// Assert
assert!(result.is_ok());
let stored_index = StoredIndex::Version1 { channel: "channel-two".into(), index: 63 };
assert_index_written(&dir, stored_index);
assert_factory_reset_not_triggered(fdr_call_count);
}
#[fasync::run_singlethreaded(test)]
async fn test_it_skips_fdr_when_config_invalid() {
// Setup
let dir = TempDir::new().expect("create tempdir");
write_config_file(&dir, "SOME INVALID \n\ntype of file []{}//\\123456768790)(*&^%$#@!".into());
let stored_index = StoredIndex::Version1 { channel: "channel-two".into(), index: 18 };
write_stored_index(&dir, stored_index);
let (mock, fdr_call_count) = spawn(&dir, "channel-two");
// Act
let result = run(mock).await;
// Assert
assert!(result.is_err());
assert_factory_reset_not_triggered(fdr_call_count);
}
#[fasync::run_singlethreaded(test)]
async fn test_it_skips_fdr_when_config_empty() {
// Setup
let dir = TempDir::new().expect("create tempdir");
write_config_file(&dir, "".into());
let stored_index = StoredIndex::Version1 { channel: "channel-two".into(), index: 18 };
write_stored_index(&dir, stored_index);
let (mock, fdr_call_count) = spawn(&dir, "channel-two");
// Act
let result = run(mock).await;
// Assert
assert!(result.is_err());
assert_factory_reset_not_triggered(fdr_call_count);
}
#[fasync::run_singlethreaded(test)]
async fn test_it_skips_fdr_when_config_missing() {
// Setup
let dir = TempDir::new().expect("create tempdir");
// Skipping: write_config_file(..)
let stored_index = StoredIndex::Version1 { channel: "channel-two".into(), index: 18 };
write_stored_index(&dir, stored_index);
let (mock, fdr_call_count) = spawn(&dir, "channel-two");
// Act
let result = run(mock).await;
// Assert
assert!(result.is_err());
assert_factory_reset_not_triggered(fdr_call_count);
}
#[fasync::run_singlethreaded(test)]
async fn test_it_skips_fdr_when_config_channel_list_empty() {
// Setup
let dir = TempDir::new().expect("create tempdir");
write_config_channels(&dir, vec![]);
let stored_index = StoredIndex::Version1 { channel: "channel-two".into(), index: 18 };
write_stored_index(&dir, stored_index);
let (mock, fdr_call_count) = spawn(&dir, "channel-two");
// Act
let result = run(mock).await;
// Assert
assert!(result.is_err());
assert_factory_reset_not_triggered(fdr_call_count);
}
#[fasync::run_singlethreaded(test)]
async fn test_it_skips_fdr_when_channel_unavailable() {
// Setup
let dir = TempDir::new().expect("create tempdir");
write_config_channels(&dir, vec![("channel-one", 29), ("channel-two", 44)]);
let stored_index = StoredIndex::Version1 { channel: "channel-two".into(), index: 18 };
write_stored_index(&dir, stored_index);
let (mock, _, fdr_stream) =
ForcedFDR::new_mock(dir.path().to_path_buf(), dir.path().to_path_buf());
// Skipping: spawn_update_info_with_channel(..);
let fdr_call_count = spawn_factory_reset(fdr_stream);
// Act
let result = run(mock).await;
// Assert
assert!(result.is_err());
assert_factory_reset_not_triggered(fdr_call_count);
}
#[fasync::run_singlethreaded(test)]
async fn test_it_overwrites_stored_index_on_channel_change() {
// Setup
let dir = TempDir::new().expect("create tempdir");
write_config_channels(&dir, vec![("channel-one", 19), ("channel-two", 18)]);
let stored_index = StoredIndex::Version1 { channel: "channel-two".into(), index: 18 };
write_stored_index(&dir, stored_index);
let (mock, fdr_call_count) = spawn(&dir, "channel-one");
// Act
let result = run(mock).await;
// Assert
let stored_index = StoredIndex::Version1 { channel: "channel-one".into(), index: 19 };
assert_index_written(&dir, stored_index);
assert!(result.is_ok());
assert_factory_reset_not_triggered(fdr_call_count);
}