зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1759175 pt10 - Add additional documentation for the crashreporter client r=gsvelto
Differential Revision: https://phabricator.services.mozilla.com/D201993
This commit is contained in:
Родитель
f8b9feb941
Коммит
d541a9f856
|
@ -2,6 +2,10 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
//! Data binding types are used to implement dynamic behaviors in UIs. [`Event`] is the primitive
|
||||
//! type underlying most others. [Properties](Property) are what should usually be used in UI
|
||||
//! models, since they have `From` impls allowing different binding behaviors to be set.
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
|
||||
|
@ -141,7 +145,11 @@ impl<T> Synchronized<T> {
|
|||
|
||||
/// A runtime value that can be fetched on-demand (read-only).
|
||||
///
|
||||
/// Consumers call `read` or `get` to retrieve the value.
|
||||
/// Consumers call [`read`] or [`get`] to retrieve the value, while producers call [`register`] to
|
||||
/// set the function which is called to retrieve the value. This is of most use for things like
|
||||
/// editable text strings, where it would be unnecessarily expensive to e.g. update a
|
||||
/// `Synchronized` property as the text string is changed (debouncing could be used, but if change
|
||||
/// notification isn't needed then it's still unnecessary).
|
||||
pub struct OnDemand<T> {
|
||||
get: Rc<RefCell<Option<Box<dyn Fn(&mut T) + 'static>>>>,
|
||||
}
|
||||
|
@ -213,8 +221,8 @@ impl<T> OnDemand<T> {
|
|||
/// * `T` can be converted to static bindings.
|
||||
/// * `Synchronized<T>` can be converted to dynamic bindings which will be updated
|
||||
/// bidirectionally.
|
||||
/// * `OnDemand<T>` can be converted to dynamic bindings which can be read from the property
|
||||
/// owner.
|
||||
/// * `OnDemand<T>` can be converted to dynamic bindings which can be queried on an as-needed
|
||||
/// basis.
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Property<T> {
|
||||
Static(T),
|
||||
|
|
|
@ -2,6 +2,32 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
//! The crash reporter application.
|
||||
//!
|
||||
//! # Architecture
|
||||
//! The application uses a simple declarative [UI model](ui::model) to define the UI. This model
|
||||
//! contains [data bindings](data) which provide the dynamic behaviors of the UI. Separate UI
|
||||
//! implementations for linux (gtk), macos (cocoa), and windows (win32) exist, as well as a test UI
|
||||
//! which is virtual (no actual interface is presented) but allows runtime introspection.
|
||||
//!
|
||||
//! # Mocking
|
||||
//! This application contains mock interfaces for all the `std` functions it uses which interact
|
||||
//! with the host system. You can see their implementation in [`crate::std`]. To enable mocking,
|
||||
//! use the `mock` feature or build with `MOZ_CRASHREPORTER_MOCK` set (which, in `build.rs`, is
|
||||
//! translated to a `cfg` option). *Note* that this cfg _must_ be enabled when running tests.
|
||||
//! Unfortunately it is not possible to detect whether tests are being built in `build.rs, which
|
||||
//! is why a feature needed to be made in the first place (it is enabled automatically when running
|
||||
//! `mach rusttests`).
|
||||
//!
|
||||
//! Currently the input program configuration which is mocked when running the application is fixed
|
||||
//! (see the [`main`] implementation in this file). If needed in the future, it would be nice to
|
||||
//! extend this to allow runtime tweaking.
|
||||
//!
|
||||
//! # Development
|
||||
//! Because of the mocking support previously mentioned, in generally any `std` imports should
|
||||
//! actually use `crate::std`. If mocked functions/types are missing, they should be added with
|
||||
//! appropriate mocking hooks.
|
||||
|
||||
// Use the WINDOWS windows subsystem. This prevents a console window from opening with the
|
||||
// application.
|
||||
#![cfg_attr(windows, windows_subsystem = "windows")]
|
||||
|
|
|
@ -2,6 +2,9 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
//! Support for legacy telemetry ping creation. The ping support serialization which should be used
|
||||
//! when submitting.
|
||||
|
||||
use anyhow::Context;
|
||||
use serde::Serialize;
|
||||
use std::collections::BTreeMap;
|
||||
|
|
|
@ -2,6 +2,11 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
//! Support for crash report creation and upload.
|
||||
//!
|
||||
//! Upload currently uses the system libcurl or curl binary rather than a rust network stack (as
|
||||
//! curl is more mature, albeit the code to interact with it must be a bit more careful).
|
||||
|
||||
use crate::std::{ffi::OsStr, path::Path, process::Child};
|
||||
use anyhow::Context;
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ use std::path::{Path, PathBuf};
|
|||
use std::sync::{Arc, Mutex};
|
||||
use std::time::SystemTime;
|
||||
|
||||
/// Mock filesystem file content.
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct MockFileContent(Arc<Mutex<Vec<u8>>>);
|
||||
|
||||
|
@ -57,10 +58,14 @@ impl From<&[u8]> for MockFileContent {
|
|||
}
|
||||
}
|
||||
|
||||
/// Mocked filesystem directory entries.
|
||||
pub type MockDirEntries = HashMap<OsString, MockFSItem>;
|
||||
|
||||
/// The content of a mock filesystem item.
|
||||
pub enum MockFSContent {
|
||||
/// File content.
|
||||
File(Result<MockFileContent>),
|
||||
/// A directory with the given entries.
|
||||
Dir(MockDirEntries),
|
||||
}
|
||||
|
||||
|
@ -73,9 +78,12 @@ impl std::fmt::Debug for MockFSContent {
|
|||
}
|
||||
}
|
||||
|
||||
/// A mock filesystem item.
|
||||
#[derive(Debug)]
|
||||
pub struct MockFSItem {
|
||||
/// The content of the item (file/dir).
|
||||
pub content: MockFSContent,
|
||||
/// The modification time of the item.
|
||||
pub modified: SystemTime,
|
||||
}
|
||||
|
||||
|
@ -88,6 +96,7 @@ impl From<MockFSContent> for MockFSItem {
|
|||
}
|
||||
}
|
||||
|
||||
/// A mock filesystem.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct MockFiles {
|
||||
root: Arc<Mutex<MockFSItem>>,
|
||||
|
@ -102,19 +111,27 @@ impl Default for MockFiles {
|
|||
}
|
||||
|
||||
impl MockFiles {
|
||||
/// Create a new, empty filesystem.
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
/// Add a mocked file with the given content. The modification time will be the unix epoch.
|
||||
///
|
||||
/// Pancis if the parent directory is not already mocked.
|
||||
pub fn add_file<P: AsRef<Path>, C: Into<MockFileContent>>(&self, path: P, content: C) -> &Self {
|
||||
self.add_file_result(path, Ok(content.into()), SystemTime::UNIX_EPOCH)
|
||||
}
|
||||
|
||||
/// Add a mocked directory.
|
||||
pub fn add_dir<P: AsRef<Path>>(&self, path: P) -> &Self {
|
||||
self.path(path, true, |_| ()).unwrap();
|
||||
self
|
||||
}
|
||||
|
||||
/// Add a mocked file that returns the given result and has the given modification time.
|
||||
///
|
||||
/// Pancis if the parent directory is not already mocked.
|
||||
pub fn add_file_result<P: AsRef<Path>>(
|
||||
&self,
|
||||
path: P,
|
||||
|
@ -173,6 +190,8 @@ impl MockFiles {
|
|||
Ok(f(cur_entry))
|
||||
}
|
||||
|
||||
/// Get the mocked parent directory of the given path and call a callback on the mocked
|
||||
/// directory's entries.
|
||||
pub fn parent_dir<P: AsRef<Path>, F, R>(&self, path: P, f: F) -> Result<R>
|
||||
where
|
||||
F: FnOnce(&mut MockDirEntries) -> R,
|
||||
|
@ -188,6 +207,7 @@ impl MockFiles {
|
|||
.and_then(|r| r)
|
||||
}
|
||||
|
||||
/// Return a file assertion helper for the mocked filesystem.
|
||||
pub fn assert_files(&self) -> AssertFiles {
|
||||
let mut files = HashMap::new();
|
||||
let root = self.root.lock().unwrap();
|
||||
|
@ -210,6 +230,10 @@ impl MockFiles {
|
|||
}
|
||||
}
|
||||
|
||||
/// A utility for asserting the state of the mocked filesystem.
|
||||
///
|
||||
/// All files must be accounted for; when dropped, a panic will occur if some files remain which
|
||||
/// weren't checked.
|
||||
#[derive(Debug)]
|
||||
pub struct AssertFiles {
|
||||
files: HashMap<PathBuf, MockFileContent>,
|
||||
|
@ -228,6 +252,7 @@ fn remove_prefix(p: &Path) -> &Path {
|
|||
}
|
||||
|
||||
impl AssertFiles {
|
||||
/// Assert that the given path contains the given content (as a utf8 string).
|
||||
pub fn check<P: AsRef<Path>, S: AsRef<str>>(&mut self, path: P, content: S) -> &mut Self {
|
||||
let p = remove_prefix(path.as_ref());
|
||||
let Some(mfc) = self.files.remove(p) else {
|
||||
|
@ -243,6 +268,7 @@ impl AssertFiles {
|
|||
self
|
||||
}
|
||||
|
||||
/// Assert that the given path contains the given byte content.
|
||||
pub fn check_bytes<P: AsRef<Path>, B: AsRef<[u8]>>(
|
||||
&mut self,
|
||||
path: P,
|
||||
|
@ -262,11 +288,13 @@ impl AssertFiles {
|
|||
self
|
||||
}
|
||||
|
||||
/// Ignore the given file (whether it exists or not).
|
||||
pub fn ignore<P: AsRef<Path>>(&mut self, path: P) -> &mut Self {
|
||||
self.files.remove(remove_prefix(path.as_ref()));
|
||||
self
|
||||
}
|
||||
|
||||
/// Assert that the given path exists without checking its content.
|
||||
pub fn check_exists<P: AsRef<Path>>(&mut self, path: P) -> &mut Self {
|
||||
let p = remove_prefix(path.as_ref());
|
||||
if self.files.remove(p).is_none() {
|
||||
|
@ -275,6 +303,11 @@ impl AssertFiles {
|
|||
self
|
||||
}
|
||||
|
||||
/// Finish checking files.
|
||||
///
|
||||
/// This panics if all files were not checked.
|
||||
///
|
||||
/// This is also called when the value is dropped.
|
||||
pub fn finish(&mut self) {
|
||||
let files = std::mem::take(&mut self.files);
|
||||
if !files.is_empty() {
|
||||
|
|
|
@ -2,6 +2,24 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
//! Mocking utilities.
|
||||
//!
|
||||
//! Mock data is set on a per-thread basis. [`crate::std::thread`] handles this automatically for
|
||||
//! scoped threads, and warns about creating threads otherwise (which won't be able to
|
||||
//! automatically share mocked data, but it can be easily done with [`SharedMockData`] when
|
||||
//! appropriate).
|
||||
//!
|
||||
//! Mock data is stored using type erasure, with a [`MockKey`] indexing arbitrary values. Use
|
||||
//! [`mock_key!`] to define keys and the values to which they map. This approach was taken as a
|
||||
//! matter of covenience for programmers, and the resulting creation and consumption APIs are
|
||||
//! succinct yet extensible.
|
||||
//!
|
||||
//! Consumers should define keys (and expose them for mockers), and at runtime create a mock key
|
||||
//! instance and call [`MockKey::get`] or [`MockKey::try_get`] to retrieve mocked values to use.
|
||||
//!
|
||||
//! Mockers should call [`builder`] to create a builder, [`set`](Builder::set) key/value mappings,
|
||||
//! and call [`run`](Builder::run) to execute code with the mock data set.
|
||||
|
||||
use std::any::{Any, TypeId};
|
||||
use std::collections::{hash_map::DefaultHasher, HashMap};
|
||||
use std::hash::{Hash, Hasher};
|
||||
|
@ -13,6 +31,7 @@ thread_local! {
|
|||
static MOCK_DATA: AtomicPtr<MockDataMap> = Default::default();
|
||||
}
|
||||
|
||||
/// A trait intended to be used as a trait object interface for mock keys.
|
||||
pub trait MockKeyStored: Any + std::fmt::Debug + Sync {
|
||||
fn eq(&self, other: &dyn MockKeyStored) -> bool;
|
||||
fn hash(&self, state: &mut DefaultHasher);
|
||||
|
@ -45,9 +64,12 @@ impl dyn MockKeyStored {
|
|||
}
|
||||
}
|
||||
|
||||
/// A type which can be used as a mock key.
|
||||
pub trait MockKey: MockKeyStored + Sized {
|
||||
/// The value to which the key maps.
|
||||
type Value: Any + Send + Sync;
|
||||
|
||||
/// Get the value set for this key, returning `None` if no data is set.
|
||||
fn try_get<F, R>(&self, f: F) -> Option<R>
|
||||
where
|
||||
F: FnOnce(&Self::Value) -> R,
|
||||
|
@ -64,6 +86,9 @@ pub trait MockKey: MockKeyStored + Sized {
|
|||
})
|
||||
}
|
||||
|
||||
/// Get the value set for this key.
|
||||
///
|
||||
/// Panics if no mock data is set for the key.
|
||||
fn get<F, R>(&self, f: F) -> R
|
||||
where
|
||||
F: FnOnce(&Self::Value) -> R,
|
||||
|
@ -75,6 +100,7 @@ pub trait MockKey: MockKeyStored + Sized {
|
|||
}
|
||||
}
|
||||
|
||||
/// Mock data which can be shared amongst threads.
|
||||
pub struct SharedMockData(AtomicPtr<MockDataMap>);
|
||||
|
||||
impl Clone for SharedMockData {
|
||||
|
@ -84,6 +110,7 @@ impl Clone for SharedMockData {
|
|||
}
|
||||
|
||||
impl SharedMockData {
|
||||
/// Create a `SharedMockData` which stores the mock data from the current thread.
|
||||
pub fn new() -> Self {
|
||||
MOCK_DATA.with(|ptr| SharedMockData(AtomicPtr::new(ptr.load(Relaxed))))
|
||||
}
|
||||
|
@ -103,21 +130,25 @@ pub fn builder() -> Builder {
|
|||
Builder::new()
|
||||
}
|
||||
|
||||
/// A mock data builder.
|
||||
#[derive(Default)]
|
||||
pub struct Builder {
|
||||
data: MockDataMap,
|
||||
}
|
||||
|
||||
impl Builder {
|
||||
/// Create a new, empty builder.
|
||||
pub fn new() -> Self {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
/// Set a mock data key/value mapping.
|
||||
pub fn set<K: MockKey>(&mut self, key: K, value: K::Value) -> &mut Self {
|
||||
self.data.insert(Box::new(key), Box::new(value));
|
||||
self
|
||||
}
|
||||
|
||||
/// Run the given function with mock data set.
|
||||
pub fn run<F, R>(&mut self, f: F) -> R
|
||||
where
|
||||
F: FnOnce() -> R,
|
||||
|
@ -129,6 +160,9 @@ impl Builder {
|
|||
}
|
||||
}
|
||||
|
||||
/// A general-purpose [`MockKey`] keyed by an identifier string and the stored type.
|
||||
///
|
||||
/// Use [`hook`] or [`try_hook`] in code accessing the values.
|
||||
pub struct MockHook<T> {
|
||||
name: &'static str,
|
||||
_p: std::marker::PhantomData<fn() -> T>,
|
||||
|
@ -157,6 +191,7 @@ impl<T: Any + Send + Sync + 'static> MockKey for MockHook<T> {
|
|||
}
|
||||
|
||||
impl<T> MockHook<T> {
|
||||
/// Create a new mock hook key with the given name.
|
||||
pub fn new(name: &'static str) -> Self {
|
||||
MockHook {
|
||||
name,
|
||||
|
@ -165,16 +200,26 @@ impl<T> MockHook<T> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Create a mock hook with the given name. When mocking isn't enabled, the given value will be
|
||||
/// used instead. Panics if the hook isn't set.
|
||||
pub fn hook<T: Any + Send + Sync + Clone>(_normally: T, name: &'static str) -> T {
|
||||
MockHook::new(name).get(|v: &T| v.clone())
|
||||
}
|
||||
|
||||
/// Create a mock hook with the given name. When mocking isn't enabled or the hook hasn't been set,
|
||||
/// the given value will be used instead.
|
||||
pub fn try_hook<T: Any + Send + Sync + Clone>(fallback: T, name: &'static str) -> T {
|
||||
MockHook::new(name)
|
||||
.try_get(|v: &T| v.clone())
|
||||
.unwrap_or(fallback)
|
||||
}
|
||||
|
||||
/// Create a mock key with an associated value type.
|
||||
///
|
||||
/// Supports the following syntaxes:
|
||||
/// * Unit struct: `<visibility> struct NAME => VALUE_TYPE`
|
||||
/// * Tuple struct: `<visibility> struct NAME(ITEMS) => VALUE_TYPE`
|
||||
/// * Normal struct: `<visibility> struct NAME { FIELDS } => VALUE_TYPE`
|
||||
macro_rules! mock_key {
|
||||
( $vis:vis struct $name:ident => $value:ty ) => {
|
||||
$crate::std::mock::mock_key! { @structdef[$vis struct $name;] $name $value }
|
||||
|
|
|
@ -3,11 +3,17 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
#![allow(dead_code)]
|
||||
|
||||
//! Stubs used when mocking isn't enabled.
|
||||
|
||||
/// Create a mock hook with the given name. When mocking isn't enabled, the given value will be
|
||||
/// used instead. Panics if the hook isn't set.
|
||||
#[inline(always)]
|
||||
pub fn hook<T: std::any::Any + Send + Sync + Clone>(normally: T, _name: &'static str) -> T {
|
||||
normally
|
||||
}
|
||||
|
||||
/// Create a mock hook with the given name. When mocking isn't enabled or the hook hasn't been set,
|
||||
/// the given value will be used instead.
|
||||
#[inline(always)]
|
||||
pub fn try_hook<T: std::any::Any + Send + Sync + Clone>(fallback: T, _name: &'static str) -> T {
|
||||
fallback
|
||||
|
|
|
@ -3,6 +3,12 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
//! Standard library wrapper (for mocking in tests).
|
||||
//!
|
||||
//! In general this should always be used rather than `std` directly, and _especially_ when using
|
||||
//! `std` functions and types which interact with the runtime host environment.
|
||||
//!
|
||||
//! Note that, in some cases, this wrapper extends the `std` library. Notably, the [`mock`] module
|
||||
//! adds mocking functions.
|
||||
|
||||
pub use std::*;
|
||||
|
||||
|
|
|
@ -2,6 +2,10 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
//! Tests here mostly interact with the [test UI](crate::ui::test). As such, most tests read a bit
|
||||
//! more like integration tests than unit tests, testing the behavior of the application as a
|
||||
//! whole.
|
||||
|
||||
use super::*;
|
||||
use crate::config::{test::MINIDUMP_PRUNE_SAVE_COUNT, Config};
|
||||
use crate::settings::Settings;
|
||||
|
@ -18,22 +22,28 @@ use crate::std::{
|
|||
};
|
||||
use crate::ui::{self, test::model, ui_impl::Interact};
|
||||
|
||||
/// A simple thread-safe counter which can be used in tests to mark that certain code paths were
|
||||
/// hit.
|
||||
#[derive(Clone, Default)]
|
||||
struct Counter(Arc<AtomicUsize>);
|
||||
|
||||
impl Counter {
|
||||
/// Create a new zero counter.
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
/// Increment the counter.
|
||||
pub fn inc(&self) {
|
||||
self.0.fetch_add(1, Relaxed);
|
||||
}
|
||||
|
||||
/// Get the current count.
|
||||
pub fn count(&self) -> usize {
|
||||
self.0.load(Relaxed)
|
||||
}
|
||||
|
||||
/// Assert that the current count is 1.
|
||||
pub fn assert_one(&self) {
|
||||
assert_eq!(self.count(), 1);
|
||||
}
|
||||
|
@ -119,6 +129,7 @@ fn current_system_time() -> ::std::time::SystemTime {
|
|||
current_datetime().into()
|
||||
}
|
||||
|
||||
/// A basic configuration which populates some necessary/useful fields.
|
||||
fn test_config() -> Config {
|
||||
let mut cfg = Config::default();
|
||||
cfg.data_dir = Some("data_dir".into());
|
||||
|
@ -129,13 +140,19 @@ fn test_config() -> Config {
|
|||
cfg
|
||||
}
|
||||
|
||||
/// A test fixture to make configuration, mocking, and assertions easier.
|
||||
struct GuiTest {
|
||||
/// The configuration used in the test. Initialized to [`test_config`].
|
||||
pub config: Config,
|
||||
/// The mock builder used in the test, initialized with a basic set of mocked values to ensure
|
||||
/// most things will work out of the box.
|
||||
pub mock: mock::Builder,
|
||||
/// The mocked filesystem, which can be used for mock setup and assertions after completion.
|
||||
pub files: MockFiles,
|
||||
}
|
||||
|
||||
impl GuiTest {
|
||||
/// Create a new GuiTest with enough configured for the application to run
|
||||
pub fn new() -> Self {
|
||||
// Create a default set of files which allow successful operation.
|
||||
let mock_files = MockFiles::new();
|
||||
|
@ -184,6 +201,9 @@ impl GuiTest {
|
|||
}
|
||||
}
|
||||
|
||||
/// Run the test as configured, using the given function to interact with the GUI.
|
||||
///
|
||||
/// Returns the final result of the application logic.
|
||||
pub fn try_run<F: FnOnce(Interact) + Send + 'static>(
|
||||
&mut self,
|
||||
interact: F,
|
||||
|
@ -199,6 +219,10 @@ impl GuiTest {
|
|||
mock.run(move || gui_interact(move || try_run(&mut config), interact))
|
||||
}
|
||||
|
||||
/// Run the test as configured, using the given function to interact with the GUI.
|
||||
///
|
||||
/// Panics if the application logic returns an error (which would normally be displayed to the
|
||||
/// user).
|
||||
pub fn run<F: FnOnce(Interact) + Send + 'static>(&mut self, interact: F) {
|
||||
if let Err(e) = self.try_run(interact) {
|
||||
panic!(
|
||||
|
@ -208,6 +232,7 @@ impl GuiTest {
|
|||
}
|
||||
}
|
||||
|
||||
/// Get the file assertion helper.
|
||||
pub fn assert_files(&self) -> AssertFiles {
|
||||
AssertFiles {
|
||||
data_dir: "data_dir".into(),
|
||||
|
@ -217,6 +242,11 @@ impl GuiTest {
|
|||
}
|
||||
}
|
||||
|
||||
/// A wrapper around the mock [`AssertFiles`](crate::std::fs::AssertFiles).
|
||||
///
|
||||
/// This implements higher-level assertions common across tests, but also supports the lower-level
|
||||
/// assertions (though those return the [`AssertFiles`](crate::std::fs::AssertFiles) reference so
|
||||
/// higher-level assertions must be chained first).
|
||||
struct AssertFiles {
|
||||
data_dir: String,
|
||||
events_dir: String,
|
||||
|
@ -232,6 +262,7 @@ impl AssertFiles {
|
|||
format!("{}/{rest}", &self.events_dir)
|
||||
}
|
||||
|
||||
/// Set the data dir if not the default.
|
||||
pub fn set_data_dir<S: ToString>(&mut self, data_dir: S) -> &mut Self {
|
||||
let data_dir = data_dir.to_string();
|
||||
// Data dir should be relative to root.
|
||||
|
@ -239,11 +270,13 @@ impl AssertFiles {
|
|||
self
|
||||
}
|
||||
|
||||
/// Ignore the generated log file.
|
||||
pub fn ignore_log(&mut self) -> &mut Self {
|
||||
self.inner.ignore(self.data("submit.log"));
|
||||
self
|
||||
}
|
||||
|
||||
/// Assert that the crash report was submitted according to the filesystem.
|
||||
pub fn submitted(&mut self) -> &mut Self {
|
||||
self.inner.check(
|
||||
self.data(&format!("submitted/{MOCK_REMOTE_CRASH_ID}.txt")),
|
||||
|
@ -252,6 +285,7 @@ impl AssertFiles {
|
|||
self
|
||||
}
|
||||
|
||||
/// Assert that the given settings where saved.
|
||||
pub fn saved_settings(&mut self, settings: Settings) -> &mut Self {
|
||||
self.inner.check(
|
||||
self.data("crashreporter_settings.json"),
|
||||
|
@ -260,6 +294,7 @@ impl AssertFiles {
|
|||
self
|
||||
}
|
||||
|
||||
/// Assert that a crash is pending according to the filesystem.
|
||||
pub fn pending(&mut self) -> &mut Self {
|
||||
let dmp = self.data("pending/minidump.dmp");
|
||||
self.inner
|
||||
|
@ -268,6 +303,7 @@ impl AssertFiles {
|
|||
self
|
||||
}
|
||||
|
||||
/// Assert that a crash ping was sent according to the filesystem.
|
||||
pub fn ping(&mut self) -> &mut Self {
|
||||
self.inner.check(
|
||||
format!("ping_dir/{MOCK_PING_UUID}.json"),
|
||||
|
@ -310,6 +346,7 @@ impl AssertFiles {
|
|||
self
|
||||
}
|
||||
|
||||
/// Assert that a crash submission event was written with the given submission status.
|
||||
pub fn submission_event(&mut self, success: bool) -> &mut Self {
|
||||
self.inner.check(
|
||||
self.events("minidump-submission"),
|
||||
|
@ -1039,11 +1076,20 @@ fn response_stop_sending_reports() {
|
|||
.check_exists("data_dir/EndOfLife100.0");
|
||||
}
|
||||
|
||||
/// A real temporary directory in the host filesystem.
|
||||
///
|
||||
/// The directory is guaranteed to be unique to the test suite process (in case of crash, it can be
|
||||
/// inspected).
|
||||
///
|
||||
/// When dropped, the directory is deleted.
|
||||
struct TempDir {
|
||||
path: ::std::path::PathBuf,
|
||||
}
|
||||
|
||||
impl TempDir {
|
||||
/// Create a new directory with the given identifying name.
|
||||
///
|
||||
/// The name should be unique to deconflict amongst concurrent tests.
|
||||
pub fn new(name: &str) -> Self {
|
||||
let path = ::std::env::temp_dir().join(format!(
|
||||
"{}-test-{}-{name}",
|
||||
|
@ -1054,6 +1100,7 @@ impl TempDir {
|
|||
TempDir { path }
|
||||
}
|
||||
|
||||
/// Get the temporary directory path.
|
||||
pub fn path(&self) -> &::std::path::Path {
|
||||
&self.path
|
||||
}
|
||||
|
@ -1066,6 +1113,9 @@ impl Drop for TempDir {
|
|||
}
|
||||
}
|
||||
|
||||
/// A mock crash report server.
|
||||
///
|
||||
/// When dropped, the server is shutdown.
|
||||
struct TestCrashReportServer {
|
||||
addr: ::std::net::SocketAddr,
|
||||
shutdown_and_thread: Option<(
|
||||
|
@ -1075,6 +1125,8 @@ struct TestCrashReportServer {
|
|||
}
|
||||
|
||||
impl TestCrashReportServer {
|
||||
/// Create and start a mock crash report server on an ephemeral port, returning a handle to the
|
||||
/// server.
|
||||
pub fn run() -> Self {
|
||||
let (shutdown, rx) = tokio::sync::oneshot::channel();
|
||||
|
||||
|
@ -1130,6 +1182,7 @@ impl TestCrashReportServer {
|
|||
}
|
||||
}
|
||||
|
||||
/// Get the url to which to submit crash reports for this mocked server.
|
||||
pub fn submit_url(&self) -> String {
|
||||
format!("http://{}/submit", self.addr)
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
use super::{Element, ElementBuilder};
|
||||
use crate::data::Event;
|
||||
|
||||
/// A clickable button.
|
||||
#[derive(Default, Debug)]
|
||||
pub struct Button {
|
||||
pub content: Option<Box<Element>>,
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
use crate::data::Property;
|
||||
|
||||
/// A checkbox (with optional label).
|
||||
#[derive(Default, Debug)]
|
||||
pub struct Checkbox {
|
||||
pub checked: Property<bool>,
|
||||
|
|
|
@ -3,6 +3,20 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
//! The UI model.
|
||||
//!
|
||||
//! Model elements should generally be declared as types with all fields `pub` (to be accessed by
|
||||
//! UI implementations), though accessor methods are acceptable if needed. An
|
||||
//! `ElementBuilder<TYPE>` impl should be provided to create methods that will be used in the
|
||||
//! [`ui!`] macro. The model types are accessible when being _consumed_ by a UI implementation,
|
||||
//! whereas the `ElementBuilder` types are accessible when the model is being _created_.
|
||||
//!
|
||||
//! All elements should be listed in the `element_types!` macro in this file (note that [`Window`],
|
||||
//! while an element, isn't listed here as it cannot be a child element). This populates the
|
||||
//! `ElementType` enum and generates `From<Element>` for `ElementType`, and `TryFrom<ElementType>`
|
||||
//! for the element (as well as reference `TryFrom`).
|
||||
//!
|
||||
//! The model is written to accommodate layout and text direction differences (e.g. for RTL
|
||||
//! languages), and UI implementations are expected to account for this correctly.
|
||||
|
||||
use crate::data::Property;
|
||||
pub use button::Button;
|
||||
|
@ -26,6 +40,8 @@ mod vbox;
|
|||
mod window;
|
||||
|
||||
/// A GUI element, including general style attributes and a more specific type.
|
||||
///
|
||||
/// `From<ElementBuilder<...>>` is implemented for all elements listed in `element_types!`.
|
||||
#[derive(Debug)]
|
||||
pub struct Element {
|
||||
pub style: ElementStyle,
|
||||
|
@ -119,6 +135,8 @@ impl Default for ElementStyle {
|
|||
}
|
||||
|
||||
/// A builder for `Element`s.
|
||||
///
|
||||
/// Each element should add an `impl ElementBuilder<TYPE>` to add methods to their builder.
|
||||
#[derive(Debug, Default)]
|
||||
pub struct ElementBuilder<T> {
|
||||
pub style: ElementStyle,
|
||||
|
@ -215,7 +233,11 @@ impl<T> ElementBuilder<T> {
|
|||
}
|
||||
}
|
||||
|
||||
/// A typed `Element`.
|
||||
/// A typed [`Element`].
|
||||
///
|
||||
/// This is useful for the [`ui!`] macro when a method should accept a specific element type, since
|
||||
/// the macro always creates [`ElementBuilder<T>`](ElementBuilder) and ends with a `.into()` (and this implements
|
||||
/// `From<ElementBuilder<T>>`).
|
||||
#[derive(Debug, Default)]
|
||||
pub struct TypedElement<T> {
|
||||
pub style: ElementStyle,
|
||||
|
@ -232,6 +254,9 @@ impl<T> From<ElementBuilder<T>> for TypedElement<T> {
|
|||
}
|
||||
|
||||
/// The alignment of an element in one direction.
|
||||
///
|
||||
/// Note that rather than `Left`/`Right`, this class has `Start`/`End` as it is meant to be
|
||||
/// layout-direction-aware.
|
||||
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
|
||||
#[allow(dead_code)]
|
||||
pub enum Alignment {
|
||||
|
@ -272,6 +297,9 @@ pub struct Margin {
|
|||
/// mutable reference. This means that element types must implement Default and must implement
|
||||
/// builder methods on `ElementBuilder<ElementTypeName>`. The children block is optional, and calls
|
||||
/// `add_child(child: Element)` for each provided child (so implement this method if desired).
|
||||
///
|
||||
/// For testing, a string identifier can be set on any element with a `["my_identifier"]` following
|
||||
/// the element type.
|
||||
macro_rules! ui {
|
||||
( $el:ident
|
||||
$([ $id:literal ])?
|
||||
|
|
|
@ -4,6 +4,14 @@
|
|||
|
||||
//! A renderer for use in tests, which doesn't actually render a GUI but allows programmatic
|
||||
//! interaction.
|
||||
//!
|
||||
//! The [`ui!`](super::ui) macro supports labeling any element with a string identifier, which can
|
||||
//! be used to access the element in this UI.
|
||||
//!
|
||||
//! The [`Interact`] hook must be created to interact with the test UI, before the UI is run and on
|
||||
//! the same thread as the UI.
|
||||
//!
|
||||
//! See how this UI is used in [`crate::test`].
|
||||
|
||||
use super::model::{self, Application, Element};
|
||||
use std::cell::RefCell;
|
||||
|
|
|
@ -258,6 +258,15 @@ Environment variables used internally
|
|||
- ``MOZ_CRASHREPORTER_STRINGS_OVERRIDE`` - Overrides the path used to load the
|
||||
.ini file holding the strings used in the crash reporter client UI.
|
||||
|
||||
Environment variables used for development
|
||||
------------------------------------------
|
||||
|
||||
Set these at build time (e.g. ``ac_add_options`` in ``.mozconfig``).
|
||||
|
||||
- ``MOZ_CRASHREPORTER_MOCK`` - When set, causes the crash reporter client to
|
||||
mock its interfaces to the system so that you can test the GUI behavior. The
|
||||
GUI will not interact with the host system at all when this is set.
|
||||
|
||||
Other topics
|
||||
============
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче