mirror of
https://github.com/servo/servo.git
synced 2025-06-06 16:45:39 +00:00
libservo: Add a basic WebView
API test (#36791)
This should allow us to start unit testing the `WebView` API. Testing: This is a test. Signed-off-by: Martin Robinson <mrobinson@igalia.com>
This commit is contained in:
parent
9bc16482a3
commit
37c680dae4
4 changed files with 126 additions and 70 deletions
|
@ -141,3 +141,12 @@ libservo = { path = ".", features = ["tracing"] }
|
||||||
rustls = { version = "0.23", default-features = false, features = ["aws-lc-rs"] }
|
rustls = { version = "0.23", default-features = false, features = ["aws-lc-rs"] }
|
||||||
tracing = { workspace = true }
|
tracing = { workspace = true }
|
||||||
winit = "0.30.8"
|
winit = "0.30.8"
|
||||||
|
|
||||||
|
|
||||||
|
[[test]]
|
||||||
|
name = "webview"
|
||||||
|
harness = false
|
||||||
|
|
||||||
|
[[test]]
|
||||||
|
name = "servo"
|
||||||
|
harness = false
|
||||||
|
|
|
@ -3,18 +3,52 @@
|
||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
use std::sync::Arc;
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
use std::sync::{Arc, OnceLock};
|
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use anyhow::Error;
|
use anyhow::Error;
|
||||||
use compositing_traits::rendering_context::{RenderingContext, SoftwareRenderingContext};
|
use compositing_traits::rendering_context::{RenderingContext, SoftwareRenderingContext};
|
||||||
use crossbeam_channel::{Receiver, Sender, unbounded};
|
|
||||||
use dpi::PhysicalSize;
|
use dpi::PhysicalSize;
|
||||||
use embedder_traits::EventLoopWaker;
|
use embedder_traits::EventLoopWaker;
|
||||||
use parking_lot::Mutex;
|
|
||||||
use servo::{Servo, ServoBuilder};
|
use servo::{Servo, ServoBuilder};
|
||||||
|
|
||||||
|
macro_rules! run_api_tests {
|
||||||
|
($($test_function:ident), +) => {
|
||||||
|
let mut failed = false;
|
||||||
|
|
||||||
|
// Be sure that `servo_test` is dropped before exiting early.
|
||||||
|
{
|
||||||
|
let servo_test = ServoTest::new();
|
||||||
|
$(
|
||||||
|
common::run_test($test_function, stringify!($test_function), &servo_test, &mut failed);
|
||||||
|
)+
|
||||||
|
}
|
||||||
|
|
||||||
|
if failed {
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) use run_api_tests;
|
||||||
|
|
||||||
|
pub(crate) fn run_test(
|
||||||
|
test_function: fn(&ServoTest) -> Result<(), Error>,
|
||||||
|
test_name: &str,
|
||||||
|
servo_test: &ServoTest,
|
||||||
|
failed: &mut bool,
|
||||||
|
) {
|
||||||
|
match test_function(servo_test) {
|
||||||
|
Ok(_) => println!(" ✅ {test_name}"),
|
||||||
|
Err(error) => {
|
||||||
|
*failed = true;
|
||||||
|
println!(" ❌ {test_name}");
|
||||||
|
println!("{}", format!("\n{error:?}").replace("\n", "\n "));
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct ServoTest {
|
pub struct ServoTest {
|
||||||
servo: Servo,
|
servo: Servo,
|
||||||
}
|
}
|
||||||
|
@ -30,7 +64,7 @@ impl Drop for ServoTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ServoTest {
|
impl ServoTest {
|
||||||
fn new() -> Self {
|
pub(crate) fn new() -> Self {
|
||||||
let rendering_context = Rc::new(
|
let rendering_context = Rc::new(
|
||||||
SoftwareRenderingContext::new(PhysicalSize {
|
SoftwareRenderingContext::new(PhysicalSize {
|
||||||
width: 500,
|
width: 500,
|
||||||
|
@ -63,58 +97,28 @@ impl ServoTest {
|
||||||
&self.servo
|
&self.servo
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Run a Servo test. All tests are run in a `ServoTestThread` and serially. Currently
|
/// Spin the Servo event loop until one of:
|
||||||
/// Servo does not support launching concurrent instances, in order to ensure
|
/// - The given callback returns `Ok(false)`.
|
||||||
/// isolation and allow for more than a single test per instance.
|
/// - The given callback returns an `Error`, in which case the `Error` will be returned.
|
||||||
pub fn run(
|
/// - Servo has indicated that shut down is complete and we cannot spin the event loop
|
||||||
test_function: impl FnOnce(&ServoTest) -> Result<(), anyhow::Error> + Send + Sync + 'static,
|
/// any longer.
|
||||||
) {
|
// The dead code exception here is because not all test suites that use `common` also
|
||||||
static SERVO_TEST_THREAD: Mutex<OnceLock<ServoTestThread>> = Mutex::new(OnceLock::new());
|
// use `spin()`.
|
||||||
let test_thread = SERVO_TEST_THREAD.lock();
|
#[allow(dead_code)]
|
||||||
test_thread
|
pub fn spin(&self, callback: impl Fn() -> Result<bool, Error> + 'static) -> Result<(), Error> {
|
||||||
.get_or_init(ServoTestThread::new)
|
let mut keep_going = true;
|
||||||
.run_test(Box::new(test_function));
|
while keep_going {
|
||||||
}
|
std::thread::sleep(Duration::from_millis(1));
|
||||||
}
|
if !self.servo.spin_event_loop() {
|
||||||
|
return Ok(());
|
||||||
type TestFunction =
|
}
|
||||||
Box<dyn FnOnce(&ServoTest) -> Result<(), anyhow::Error> + Send + Sync + 'static>;
|
let result = callback();
|
||||||
|
match result {
|
||||||
struct ServoTestThread {
|
Ok(result) => keep_going = result,
|
||||||
test_function_sender: Sender<TestFunction>,
|
Err(error) => return Err(error),
|
||||||
result_receiver: Receiver<Result<(), Error>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ServoTestThread {
|
|
||||||
fn new() -> Self {
|
|
||||||
let (result_sender, result_receiver) = unbounded();
|
|
||||||
let (test_function_sender, test_function_receiver) = unbounded();
|
|
||||||
|
|
||||||
// Defined here rather than at the end of this method in order to take advantage
|
|
||||||
// of Rust type inference.
|
|
||||||
let thread = Self {
|
|
||||||
test_function_sender,
|
|
||||||
result_receiver,
|
|
||||||
};
|
|
||||||
|
|
||||||
let _ = std::thread::spawn(move || {
|
|
||||||
let servo_test = ServoTest::new();
|
|
||||||
while let Ok(incoming_test_function) = test_function_receiver.recv() {
|
|
||||||
let _ = result_sender.send(incoming_test_function(&servo_test));
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
thread
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run_test(&self, test_function: TestFunction) {
|
|
||||||
let _ = self.test_function_sender.send(Box::new(test_function));
|
|
||||||
let result = self
|
|
||||||
.result_receiver
|
|
||||||
.recv()
|
|
||||||
.expect("Servo test thread should always return a result.");
|
|
||||||
if let Err(result) = result {
|
|
||||||
unreachable!("{result}");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
//! Servo API unit tests.
|
//! Servo API unit tests.
|
||||||
//!
|
//!
|
||||||
//! Since all Servo tests must rust serially on the same thread, it is important
|
//! Since all Servo tests must run serially on the same thread, it is important
|
||||||
//! that tests never panic. In order to ensure this, use `anyhow::ensure!` instead
|
//! that tests never panic. In order to ensure this, use `anyhow::ensure!` instead
|
||||||
//! of `assert!` for test assertions. `ensure!` will produce a `Result::Err` in
|
//! of `assert!` for test assertions. `ensure!` will produce a `Result::Err` in
|
||||||
//! place of panicking.
|
//! place of panicking.
|
||||||
|
@ -12,21 +12,15 @@
|
||||||
mod common;
|
mod common;
|
||||||
|
|
||||||
use anyhow::ensure;
|
use anyhow::ensure;
|
||||||
use common::*;
|
use common::{ServoTest, run_api_tests};
|
||||||
use servo::WebViewBuilder;
|
|
||||||
|
|
||||||
#[test]
|
fn test_simple_servo_is_not_animating_by_default(
|
||||||
fn test_simple_servo_is_not_animating_by_default() {
|
servo_test: &ServoTest,
|
||||||
ServoTest::run(|servo_test| {
|
) -> Result<(), anyhow::Error> {
|
||||||
ensure!(!servo_test.servo().animating());
|
ensure!(!servo_test.servo().animating());
|
||||||
Ok(())
|
Ok(())
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
fn main() {
|
||||||
fn test_simple_servo_construct_webview() {
|
run_api_tests!(test_simple_servo_is_not_animating_by_default);
|
||||||
ServoTest::run(|servo_test| {
|
|
||||||
WebViewBuilder::new(servo_test.servo()).build();
|
|
||||||
Ok(())
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
49
components/servo/tests/webview.rs
Normal file
49
components/servo/tests/webview.rs
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
/* 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 https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
//! WebView API unit tests.
|
||||||
|
//!
|
||||||
|
//! Since all Servo tests must run serially on the same thread, it is important
|
||||||
|
//! that tests never panic. In order to ensure this, use `anyhow::ensure!` instead
|
||||||
|
//! of `assert!` for test assertions. `ensure!` will produce a `Result::Err` in
|
||||||
|
//! place of panicking.
|
||||||
|
|
||||||
|
mod common;
|
||||||
|
|
||||||
|
use std::cell::Cell;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use anyhow::ensure;
|
||||||
|
use common::{ServoTest, run_api_tests};
|
||||||
|
use servo::{WebViewBuilder, WebViewDelegate};
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
struct WebViewDelegateImpl {
|
||||||
|
url_changed: Cell<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WebViewDelegate for WebViewDelegateImpl {
|
||||||
|
fn notify_url_changed(&self, _webview: servo::WebView, _url: url::Url) {
|
||||||
|
self.url_changed.set(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_create_webview(servo_test: &ServoTest) -> Result<(), anyhow::Error> {
|
||||||
|
let delegate = Rc::new(WebViewDelegateImpl::default());
|
||||||
|
let webview = WebViewBuilder::new(servo_test.servo())
|
||||||
|
.delegate(delegate.clone())
|
||||||
|
.build();
|
||||||
|
|
||||||
|
servo_test.spin(move || Ok(!delegate.url_changed.get()))?;
|
||||||
|
|
||||||
|
let url = webview.url();
|
||||||
|
ensure!(url.is_some());
|
||||||
|
ensure!(url.unwrap().to_string() == "about:blank");
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
run_api_tests!(test_create_webview);
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue