aboutsummaryrefslogtreecommitdiff
path: root/tests/server_headless.rs
diff options
context:
space:
mode:
authorCan Celik <ogulcancelik@gmail.com>2026-09-01 15:48:22 +0300
committerGitHub <noreply@github.com>2026-09-01 15:48:22 +0300
commit207be3c771d281baae6e5fa0fb74be9a056e97a2 (patch)
treed7b037ecdf629224a048c841d11659efdc23507d /tests/server_headless.rs
parent8a6d697308162874a236c84aac0b0f8c7ac01f83 (diff)
refactor: render the shell in the client (#3487)
* feat: add opt-in client-rendered shell * feat: add client-owned shell mouse controls * feat: complete client-owned shell chrome * feat: add client-owned shell settings and notifications * feat: run client shell custom commands * feat: render popup terminals in client shell * feat: add client-owned scrollback and copy mode * fix: preserve client worktree trust defaults * feat: render startup diagnostics in client shell * feat: render onboarding in client shell * feat: render product announcements in client shell * feat: render release notes in client shell * feat: render mobile switcher in client shell * feat: render kitty graphics in client shell * fix: preserve client shell lifecycle coherence * refactor: route client shell commands through endpoint connections * refactor: make the client-rendered shell the default * refactor: remove monolithic launch mode * refactor: complete client-rendered shell cutover * perf: retain client pane surface updates * perf: scale retained pane surface updates * perf: remove remote pane input latency * test: make client shell checks platform-independent * fix: prevent client timer starvation * test: fix client shell platform gates * test: accept retained surfaces in cross-area checks * test: make client mode assertions platform-neutral * fix: badge only outdated integrations
Diffstat (limited to 'tests/server_headless.rs')
-rw-r--r--tests/server_headless.rs205
1 files changed, 2 insertions, 203 deletions
diff --git a/tests/server_headless.rs b/tests/server_headless.rs
index 5abb5ace..f6eb0771 100644
--- a/tests/server_headless.rs
+++ b/tests/server_headless.rs
@@ -1,6 +1,6 @@
//! Integration tests for headless server mode.
-mod support;
+pub mod support;
use std::fs;
use std::io::{BufRead, BufReader, Read, Write};
@@ -13,7 +13,7 @@ use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
use portable_pty::{native_pty_system, Child, CommandBuilder, MasterPty, PtySize};
use support::{
- cleanup_test_base, register_runtime_dir, register_spawned_herdr_pid,
+ cleanup_test_base, client_handshake, register_runtime_dir, register_spawned_herdr_pid,
unregister_spawned_herdr_pid, CURRENT_PROTOCOL,
};
@@ -151,207 +151,6 @@ fn ping_socket(socket_path: &Path) -> String {
response.trim().to_string()
}
-/// Sends a Hello message over the client socket and reads the Welcome response.
-/// Uses bincode v2 wire format: [u32LE length][bincode payload]
-/// bincode v2 standard config uses VarintEncoding:
-/// - Integers < 251 are encoded as a single byte
-/// - Enum variant index is encoded as u32 varint
-/// - Option discriminant is always a single byte (0=None, 1=Some)
-/// - String: length (varint) + UTF-8 bytes
-fn client_handshake(
- stream: &mut UnixStream,
- version: u32,
- cols: u16,
- rows: u16,
-) -> Result<(u32, Option<String>), String> {
- stream
- .set_read_timeout(Some(Duration::from_secs(5)))
- .map_err(|e| e.to_string())?;
-
- // Encode Hello message using bincode v2 varint format.
- // ClientMessage::Hello is variant 0.
- let hello_payload = encode_varint_enum(
- 0,
- &[
- &encode_varint_u32(version),
- &encode_varint_u16(cols),
- &encode_varint_u16(rows),
- &encode_varint_u32(8), // cell_width_px
- &encode_varint_u32(16), // cell_height_px
- &encode_varint_u32(0), // RenderEncoding::SemanticFrame
- &encode_varint_u32(0), // ClientKeybindings::Server
- &encode_varint_u32(0), // ClientLaunchMode::App
- ],
- );
- let framed = frame_message(&hello_payload);
- stream.write_all(&framed).map_err(|e| e.to_string())?;
- stream.flush().map_err(|e| e.to_string())?;
-
- // Read the framed response.
- let mut len_buf = [0u8; 4];
- stream.read_exact(&mut len_buf).map_err(|e| e.to_string())?;
- let len = u32::from_le_bytes(len_buf) as usize;
-
- if len > 2 * 1024 * 1024 {
- return Err(format!("oversized response: {len}"));
- }
-
- let mut payload = vec![0u8; len];
- stream.read_exact(&mut payload).map_err(|e| e.to_string())?;
-
- // Decode Welcome: ServerMessage variant 0 = Welcome { version: u32, error: Option<String> }
- decode_welcome(&payload)
-}
-
-/// Encode a varint u32 value according to bincode v2 VarintEncoding.
-fn encode_varint_u32(v: u32) -> Vec<u8> {
- if v < 251 {
- vec![v as u8]
- } else if v < 65536 {
- let mut buf = vec![251u8];
- buf.extend_from_slice(&(v as u16).to_le_bytes());
- buf
- } else {
- let mut buf = vec![252u8];
- buf.extend_from_slice(&v.to_le_bytes());
- buf
- }
-}
-
-/// Encode a varint u16 value.
-fn encode_varint_u16(v: u16) -> Vec<u8> {
- if v < 251 {
- vec![v as u8]
- } else {
- let mut buf = vec![251u8];
- buf.extend_from_slice(&v.to_le_bytes());
- buf
- }
-}
-
-/// Encode an enum variant with its fields.
-fn encode_varint_enum(variant_idx: u32, fields: &[&[u8]]) -> Vec<u8> {
- let mut buf = encode_varint_u32(variant_idx);
- for field in fields {
- buf.extend_from_slice(field);
- }
- buf
-}
-
-/// Frame a message with u32LE length prefix.
-fn frame_message(payload: &[u8]) -> Vec<u8> {
- let len = payload.len() as u32;
- let mut framed = len.to_le_bytes().to_vec();
- framed.extend_from_slice(payload);
- framed
-}
-
-/// Decode a varint u32 from a byte slice at the given offset.
-/// Returns (value, bytes_consumed).
-fn decode_varint_u32(payload: &[u8], offset: usize) -> Result<(u32, usize), String> {
- if offset >= payload.len() {
- return Err("payload too short for varint".into());
- }
- let first_byte = payload[offset];
- match first_byte {
- 0..=250 => Ok((first_byte as u32, 1)),
- 251 => {
- if offset + 3 > payload.len() {
- return Err("payload too short for u16 varint".into());
- }
- let v = u16::from_le_bytes(
- payload[offset + 1..offset + 3]
- .try_into()
- .map_err(|e: std::array::TryFromSliceError| e.to_string())?,
- );
- Ok((v as u32, 3))
- }
- 252 => {
- if offset + 5 > payload.len() {
- return Err("payload too short for u32 varint".into());
- }
- let v = u32::from_le_bytes(
- payload[offset + 1..offset + 5]
- .try_into()
- .map_err(|e: std::array::TryFromSliceError| e.to_string())?,
- );
- Ok((v, 5))
- }
- _ => Err(format!("unsupported varint tag: {first_byte}")),
- }
-}
-
-/// Decode a varint u16 from a byte slice at the given offset.
-#[allow(dead_code)]
-fn decode_varint_u16(payload: &[u8], offset: usize) -> Result<(u16, usize), String> {
- if offset >= payload.len() {
- return Err("payload too short for varint".into());
- }
- let first_byte = payload[offset];
- match first_byte {
- 0..=250 => Ok((first_byte as u16, 1)),
- 251 => {
- if offset + 3 > payload.len() {
- return Err("payload too short for u16 varint".into());
- }
- let v = u16::from_le_bytes(
- payload[offset + 1..offset + 3]
- .try_into()
- .map_err(|e: std::array::TryFromSliceError| e.to_string())?,
- );
- Ok((v, 3))
- }
- _ => Err(format!("unsupported varint tag for u16: {first_byte}")),
- }
-}
-
-/// Decode a ServerMessage::Welcome from bincode v2 payload.
-fn decode_welcome(payload: &[u8]) -> Result<(u32, Option<String>), String> {
- let mut offset = 0;
-
- // Variant index (should be 0 for Welcome)
- let (variant, consumed) = decode_varint_u32(payload, offset)?;
- offset += consumed;
- if variant != 0 {
- return Err(format!(
- "expected Welcome (variant 0), got variant {variant}"
- ));
- }
-
- // version: u32
- let (version, consumed) = decode_varint_u32(payload, offset)?;
- offset += consumed;
-
- // encoding: RenderEncoding
- let (_encoding, consumed) = decode_varint_u32(payload, offset)?;
- offset += consumed;
-
- // error: Option<String> — discriminant is always 1 byte
- if offset >= payload.len() {
- return Err("payload too short for Option tag".into());
- }
- let option_tag = payload[offset];
- offset += 1;
-
- let error = if option_tag == 1 {
- // Some(String) — length as varint + UTF-8 bytes
- let (str_len, consumed) = decode_varint_u32(payload, offset)?;
- offset += consumed;
- let str_len = str_len as usize;
-
- if offset + str_len > payload.len() {
- return Err("payload too short for string content".into());
- }
- let s = String::from_utf8(payload[offset..offset + str_len].to_vec())
- .map_err(|e| e.to_string())?;
- Some(s)
- } else {
- None
- };
-
- Ok((version, error))
-}
-
// ---------------------------------------------------------------------------
// Tests
// ---------------------------------------------------------------------------