Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/toolkit/crashreporter/client/app/src/ui/   (Firefox Browser Version 153.0.1©)  Datei vom 27.6.2026 mit Größe 10 kB image not shown  

Quelle  test.rs

  Sprache: Rust
 

/* This Source Code Form is subject to the terms of the Mozilla Public
 * 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/. */


//! 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;
use std::collections::HashMap;
use std::sync::{
    atomic::{AtomicBool, AtomicU8, Ordering::Relaxed},
    mpsc, Arc, Condvar, Mutex,
};

thread_local! {
    static INTERACT: RefCell<Option<Arc<State>>> = Default::default();
}

/// A test UI which allows access to the UI elements.
#[derive(Default)]
pub struct UI {
    interface: Mutex<Option<UIInterface>>,
}

impl UI {
    pub fn run_loop(&self, app: Application) {
        let (tx, rx) = mpsc::channel();
        let interface = UIInterface { work: tx };

        let elements = id_elements(&app);
        INTERACT.with(cc! { (interface) move |r| {
            if let Some(state) = &*r.borrow() {
                state.set_interface(interface);
            }
        }});
        *self.interface.lock().unwrap() = Some(interface.clone());

        // Close the UI when the root windows are closed.
        // Use a bitfield rather than a count in case the `close` event is fired multiple times.
        assert!(app.windows.len() <= 8);
        let mut windows = 0u8;
        for i in 0..app.windows.len() {
            windows |= 1 << i;
        }
        let windows = Arc::new(AtomicU8::new(windows));
        for (index, window) in app.windows.iter().enumerate() {
            if let Some(c) = &window.element_type.close {
                c.subscribe(cc! { (interface, windows) move |&()| {
                    let old = windows
                        .fetch_update(Relaxed, Relaxed, |x| Some(x & !(1u8 << index)))
                        .unwrap();
                    if old == 1u8 << index {
                        interface.work.send(Command::Finish).unwrap();
                    }
                }});
            } else {
                // No close event, so we must assume a closed state (and assume that _some_ window
                // will have a close event registered so we don't drop the interface now).
                windows
                    .fetch_update(Relaxed, Relaxed, |x| Some(x & !(1u8 << index)))
                    .unwrap();
            }
        }

        while let Ok(f) = rx.recv() {
            match f {
                Command::Invoke(f) => f(),
                Command::Interact(f) => f(&elements),
                Command::Finish => break,
            }
        }

        *self.interface.lock().unwrap() = None;
        INTERACT.with(|r| {
            if let Some(state) = &*r.borrow() {
                state.clear_interface();
            }
        });
    }

    pub fn invoke(&self, f: model::InvokeFn) {
        let guard = self.interface.lock().unwrap();
        if let Some(interface) = &*guard {
            let _ = interface.work.send(Command::Invoke(f));
        }
    }
}

/// Test interaction interface.
#[derive(Clone)]
pub struct Interact {
    state: Arc<State>,
}

/// Spawned test interaction.
pub struct SpawnedInteract {
    interact: Interact,
    handle: ::std::mem::ManuallyDrop<::std::thread::JoinHandle<()>>,
    ignore_panic: bool,
}

impl SpawnedInteract {
    pub fn ignore_panic(&mut self) {
        self.ignore_panic = true;
    }
}

impl Drop for SpawnedInteract {
    fn drop(&mut self) {
        self.interact.cancel();
        if unsafe { ::std::mem::ManuallyDrop::take(&mut self.handle) }
            .join()
            .is_err()
        {
            if !std::thread::panicking() && !self.ignore_panic {
                // Make sure this thread panics so we see the panic from the interact thread.
                panic!("interact thread panicked");
            }
        }
    }
}

impl Interact {
    /// Spawn a thread which runs interactions according with the test UI.
    ///
    /// The returned `SpawnedInteract` should be dropped after the test UI is run.
    ///
    /// # Panic Safety
    /// If a panic occurs in the interact thread, it will not be re-thrown (we assume that the
    /// panic handler will still show the thread's panic message when the test completes).
    pub fn spawn<F: FnOnce(&Self) + Send + 'static>(f: F) -> SpawnedInteract {
        let interact = Self::hook();
        let handle = ::std::thread::spawn(cc! { (interact) move || {
            interact.wait_for_ready();
            f(&interact);

        }});
        SpawnedInteract {
            interact,
            handle: ::std::mem::ManuallyDrop::new(handle),
            ignore_panic: false,
        }
    }

    /// Create an interaction hook for the test UI.
    ///
    /// This should be done before running the UI, and must be done on the same thread that
    /// later runs it.
    fn hook() -> Interact {
        let v = Interact {
            state: Default::default(),
        };
        {
            let state = v.state.clone();
            INTERACT.with(move |r| *r.borrow_mut() = Some(state));
        }
        v
    }

    /// Wait for the render thread to be ready for interaction.
    fn wait_for_ready(&self) {
        let mut guard = self.state.interface.lock().unwrap();
        while guard.is_none() && !self.state.cancel.load(Relaxed) {
            guard = self.state.waiting_for_interface.wait(guard).unwrap();
        }
    }

    /// Cancel an Interact (which causes `wait_for_ready` to always return).
    fn cancel(&self) {
        let _guard = self.state.interface.lock().unwrap();
        self.state.cancel.store(true, Relaxed);
        self.state.waiting_for_interface.notify_all();
    }

    /// Run the given function on the element with the given type and identity.
    ///
    /// Panics if either the id is missing or the type is incorrect.
    pub fn element<'a, 'b, T: 'b, F, R>(&self, id: &'a str, f: F) -> R
    where
        &'b T: TryFrom<&'b model::ElementType>,
        F: FnOnce(&model::ElementStyle, &T) -> R + Send + 'a,
        R: Send + 'a,
    {
        self.interact(id, move |element: &IdElement| match element {
            IdElement::Generic(e) => Some(f(&e.style, (&e.element_type).try_into().ok()?)),
            IdElement::Window(_) => None,
        })
        .expect("incorrect element type")
    }

    /// Run the given function on the window with the given identity.
    ///
    /// Panics if the id is missing or the type is incorrect.
    pub fn window<'a, F, R>(&self, id: &'a str, f: F) -> R
    where
        F: FnOnce(&model::ElementStyle, &model::Window) -> R + Send + 'a,
        R: Send + 'a,
    {
        self.interact(id, move |element| match element {
            IdElement::Window(e) => Some(f(&e.style, &e.element_type)),
            IdElement::Generic(_) => None,
        })
        .expect("incorrect element type")
    }

    fn interact<'a, 'b, F, R>(&self, id: &'a str, f: F) -> R
    where
        F: FnOnce(&IdElement<'b>) -> R + Send + 'a,
        R: Send + 'a,
    {
        let (send, recv) = std::sync::mpsc::sync_channel(0);
        {
            let f: Box<dyn FnOnce(&IdElements<'b>) + Send + 'a> = Box::new(move |elements| {
                let _ = send.send(elements.get(id).map(f));
            });

            // # Safety
            // The function is run while `'a` is still valid (we wait here for it to complete).
            let f: Box<dyn FnOnce(&IdElements) + Send + 'static> =
                unsafe { std::mem::transmute(f) };

            let guard = self.state.interface.lock().unwrap();
            let interface = guard.as_ref().expect("renderer is not running");
            let _ = interface.work.send(Command::Interact(f));
        }
        recv.recv().unwrap().expect("failed to get element")
    }
}

#[derive(Clone)]
struct UIInterface {
    work: mpsc::Sender<Command>,
}

enum Command {
    Invoke(Box<dyn FnOnce() + Send + 'static>),
    Interact(Box<dyn FnOnce(&IdElements) + Send + 'static>),
    Finish,
}

enum IdElement<'a> {
    Generic(&'a Element),
    Window(&'a model::TypedElement<model::Window>),
}

type IdElements<'a> = HashMap<String, IdElement<'a>>;

#[derive(Default)]
struct State {
    interface: Mutex<Option<UIInterface>>,
    waiting_for_interface: Condvar,
    cancel: AtomicBool,
}

impl State {
    /// Set the interface for the interaction client to use.
    pub fn set_interface(&self, interface: UIInterface) {
        let mut guard = self.interface.lock().unwrap();
        *guard = Some(interface);
        self.waiting_for_interface.notify_all();
    }

    /// Clear the UI interface.
    pub fn clear_interface(&self) {
        *self.interface.lock().unwrap() = None;
    }
}

fn id_elements<'a>(app: &'a Application) -> IdElements<'a> {
    let mut elements: IdElements<'a> = Default::default();

    let mut windows_to_visit: Vec<_> = app.windows.iter().collect();

    let mut to_visit: Vec<&'a Element> = Vec::new();
    while let Some(window) = windows_to_visit.pop() {
        if let Some(id) = &window.style.id {
            elements.insert(id.to_owned(), IdElement::Window(window));
        }
        windows_to_visit.extend(&window.element_type.children);

        if let Some(content) = &window.element_type.content {
            to_visit.push(content);
        }
    }

    while let Some(el) = to_visit.pop() {
        if let Some(id) = &el.style.id {
            elements.insert(id.to_owned(), IdElement::Generic(el));
        }

        use model::ElementType::*;
        match &el.element_type {
            Button(model::Button {
                content: Some(content),
                ..
            })
            | Scroll(model::Scroll {
                content: Some(content),
            }) => {
                to_visit.push(content);
            }
            VBox(model::VBox { items, .. }) | HBox(model::HBox { items, .. }) => {
                for item in items {
                    to_visit.push(item)
                }
            }
            _ => (),
        }
    }

    elements
}

Messung V0.5 in Prozent
C=89 H=87 G=87

¤ Dauer der Verarbeitung: 0.6 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.