Files
ELY-Browser/crates/ely_app/src/services/servo_sidecar_tests.rs
T

261 lines
8.5 KiB
Rust

use std::error::Error;
use ely_domain::UrlText;
use super::*;
#[cfg(feature = "live-site-smoke")]
use crate::services::prd_live_sites::{
LiveSiteCase, PRD_REFERENCE_SITE_CASES, PRD_TOP_SITE_CASES,
assert_prd_reference_urls_are_covered,
};
#[cfg(feature = "live-site-smoke")]
const LIVE_SITE_RENDER_ATTEMPTS: usize = 3;
#[cfg(feature = "live-site-smoke")]
const LIVE_SITE_WIDTH: u32 = 934;
#[cfg(feature = "live-site-smoke")]
const LIVE_SITE_HEIGHT: u32 = 657;
#[cfg(feature = "live-site-smoke")]
const MINIMUM_CONTENT_PIXELS: u64 = 1_000;
#[test]
fn accepts_loading_report_with_visible_content() -> Result<(), ServoSidecarError> {
let profile_id = ProfileId::new();
let snapshot = SidecarSnapshot::from_report(
report_with_state("loading", &profile_id),
&profile_id,
visible_frame(),
)?;
assert_eq!(snapshot.loaded_url(), Some("https://example.com/"));
assert_eq!(snapshot.title(), Some("Example Domain"));
assert_eq!(snapshot.render_state(), "loading");
assert_eq!(snapshot.width(), 2);
assert_eq!(snapshot.height(), 1);
assert_eq!(snapshot.non_white_pixel_count, 1);
assert_eq!(snapshot.content_pixel_count, 1);
assert_eq!(snapshot.sample_hash, 42);
Ok(())
}
#[test]
fn rejects_created_report_with_visible_content() {
let profile_id = ProfileId::new();
let result = SidecarSnapshot::from_report(
report_with_state("created", &profile_id),
&profile_id,
visible_frame(),
);
assert!(
matches!(result, Err(ServoSidecarError::IncompleteRender { state }) if state == "created")
);
}
#[test]
fn rejects_report_from_different_profile() {
let expected_profile_id = ProfileId::new();
let actual_profile_id = ProfileId::new();
let result = SidecarSnapshot::from_report(
report_with_state("loading", &actual_profile_id),
&expected_profile_id,
visible_frame(),
);
assert!(matches!(result, Err(ServoSidecarError::ProfileMismatch { expected, actual })
if expected == expected_profile_id.as_str() && actual == actual_profile_id.as_str()));
}
#[test]
fn retries_navigation_snapshots() -> Result<(), Box<dyn Error>> {
let request =
SidecarSnapshotRequest::new(UrlText::parse("https://example.com")?, ProfileId::new(), 2, 1);
assert_eq!(request.max_attempts(), SIDECAR_NAVIGATION_ATTEMPTS);
Ok(())
}
#[test]
fn keeps_page_interactions_single_attempt() -> Result<(), Box<dyn Error>> {
let request =
SidecarSnapshotRequest::new(UrlText::parse("https://example.com")?, ProfileId::new(), 2, 1)
.with_click_point(1, 1)
.with_typed_text("ely".to_string());
assert_eq!(request.max_attempts(), SIDECAR_INTERACTION_ATTEMPTS);
Ok(())
}
#[test]
fn persistent_profile_data_uses_profile_root() -> Result<(), Box<dyn Error>> {
let profile_id = ProfileId::new();
let request = SidecarSnapshotRequest::new(
UrlText::parse("https://example.com")?,
profile_id.clone(),
2,
1,
);
let root = std::env::temp_dir().join("ely-browser-profile-root-test");
assert_eq!(request.profile_data_mode_for_test(), ProfileDataMode::Persistent);
assert_eq!(request.profile_data_dir(&root)?, root.join(profile_id.as_str()).join("servo"));
Ok(())
}
#[test]
fn transient_profile_data_uses_temporary_directory_and_cleans_up() -> Result<(), Box<dyn Error>> {
let profile_id = ProfileId::new();
let request = SidecarSnapshotRequest::new(
UrlText::parse("https://example.com")?,
profile_id.clone(),
2,
1,
)
.with_profile_data_mode(ProfileDataMode::Transient);
let root = std::env::temp_dir().join("ely-browser-profile-root-test");
let profile_data_dir = request.profile_data_dir(&root)?;
assert!(profile_data_dir.starts_with(std::env::temp_dir().join("ely-browser-servo-profiles")));
assert!(profile_data_dir.to_string_lossy().contains(profile_id.as_str()));
std::fs::create_dir_all(&profile_data_dir)?;
std::fs::write(profile_data_dir.join("probe"), b"private")?;
request.cleanup_profile_data_dir(&profile_data_dir)?;
assert!(!profile_data_dir.exists());
Ok(())
}
#[cfg(feature = "live-site-smoke")]
#[test]
fn desktop_sidecar_opens_prd_top_sites() -> Result<(), Box<dyn Error>> {
assert_live_sites_render(PRD_TOP_SITE_CASES)
}
#[cfg(feature = "live-site-smoke")]
#[test]
fn desktop_sidecar_opens_prd_reference_sites() -> Result<(), Box<dyn Error>> {
assert_live_sites_render(PRD_REFERENCE_SITE_CASES)
}
#[cfg(feature = "live-site-smoke")]
#[test]
fn prd_reference_live_site_cases_cover_prd_urls() -> Result<(), Box<dyn Error>> {
assert_prd_reference_urls_are_covered()
}
#[cfg(feature = "live-site-smoke")]
fn assert_live_sites_render(cases: &[LiveSiteCase]) -> Result<(), Box<dyn Error>> {
let client = ServoSidecarClient::new()?;
for case in cases {
let snapshot = render_live_site_snapshot(&client, case)?;
let rgba_bytes = snapshot.into_rgba_bytes();
assert_eq!(
rgba_bytes.len(),
expected_rgba_byte_count(LIVE_SITE_WIDTH, LIVE_SITE_HEIGHT)?,
"{}",
case.url
);
}
Ok(())
}
#[cfg(feature = "live-site-smoke")]
fn render_live_site_snapshot(
client: &ServoSidecarClient,
case: &LiveSiteCase,
) -> Result<SidecarSnapshot, Box<dyn Error>> {
let mut last_error = String::new();
for attempt in 0..LIVE_SITE_RENDER_ATTEMPTS {
let request = SidecarSnapshotRequest::new(
UrlText::parse(case.url)?,
ProfileId::new(),
LIVE_SITE_WIDTH,
LIVE_SITE_HEIGHT,
);
match client.snapshot(request) {
Ok(snapshot) => match validate_live_site_snapshot(&snapshot, case) {
Ok(()) => return Ok(snapshot),
Err(error) => last_error = error,
},
Err(error) => last_error = error.to_string(),
}
if attempt + 1 < LIVE_SITE_RENDER_ATTEMPTS {
std::thread::sleep(std::time::Duration::from_millis(250));
}
}
Err(last_error.into())
}
#[cfg(feature = "live-site-smoke")]
fn validate_live_site_snapshot(
snapshot: &SidecarSnapshot,
case: &LiveSiteCase,
) -> Result<(), String> {
require(
snapshot.width() == LIVE_SITE_WIDTH,
format!("{} width: {}", case.url, snapshot.width()),
)?;
require(
snapshot.height() == LIVE_SITE_HEIGHT,
format!("{} height: {}", case.url, snapshot.height()),
)?;
require_render_state_is_open(snapshot.render_state(), case.url)?;
require_loaded_url_contains(snapshot, case.url)?;
require_title_contains(snapshot, case.title_fragment)?;
require(snapshot.non_white_pixel_count > 0, case.url.to_string())?;
require(
snapshot.content_pixel_count >= MINIMUM_CONTENT_PIXELS,
format!("{} content pixels: {}", case.url, snapshot.content_pixel_count),
)?;
require(snapshot.sample_hash > 0, case.url.to_string())
}
#[cfg(feature = "live-site-smoke")]
fn require_render_state_is_open(state: &str, url: &str) -> Result<(), String> {
require(matches!(state, "complete" | "loading"), format!("{url} state: {state}"))
}
#[cfg(feature = "live-site-smoke")]
fn require_loaded_url_contains(snapshot: &SidecarSnapshot, fragment: &str) -> Result<(), String> {
let loaded_url =
snapshot.loaded_url().ok_or_else(|| format!("missing loaded URL for {fragment}"))?;
require(loaded_url.contains(fragment), format!("loaded_url: {loaded_url}"))
}
#[cfg(feature = "live-site-smoke")]
fn require_title_contains(snapshot: &SidecarSnapshot, fragment: &str) -> Result<(), String> {
let title = snapshot.title().ok_or_else(|| format!("missing title containing {fragment}"))?;
require(title.contains(fragment), format!("title: {title}"))
}
#[cfg(feature = "live-site-smoke")]
fn require(condition: bool, message: String) -> Result<(), String> {
if condition { Ok(()) } else { Err(message) }
}
fn report_with_state(state: &str, profile_id: &ProfileId) -> SidecarReport {
SidecarReport {
requested_url: "https://example.com".to_string(),
profile_id: profile_id.as_str().to_string(),
loaded_url: Some("https://example.com/".to_string()),
title: Some("Example Domain".to_string()),
state: state.to_string(),
width: 2,
height: 1,
rgba_byte_count: 8,
non_white_pixel_count: 1,
content_pixel_count: 1,
sample_hash: 42,
}
}
fn visible_frame() -> Vec<u8> {
vec![0, 0, 0, 255, 255, 255, 255, 255]
}