Skip to content

Commit c10f1d1

Browse files
committed
tests: Add a simple test for custom multiport virtio console config
Signed-off-by: Matej Hrica <[email protected]>
1 parent 0d7de11 commit c10f1d1

File tree

2 files changed

+172
-0
lines changed

2 files changed

+172
-0
lines changed

tests/test_cases/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ use test_tsi_tcp_guest_connect::TestTsiTcpGuestConnect;
1010
mod test_tsi_tcp_guest_listen;
1111
use test_tsi_tcp_guest_listen::TestTsiTcpGuestListen;
1212

13+
mod test_multiport_console;
14+
use test_multiport_console::TestMultiportConsole;
15+
1316
pub fn test_cases() -> Vec<TestCase> {
1417
// Register your test here:
1518
vec![
@@ -36,6 +39,7 @@ pub fn test_cases() -> Vec<TestCase> {
3639
"tsi-tcp-guest-listen",
3740
Box::new(TestTsiTcpGuestListen::new()),
3841
),
42+
TestCase::new("multiport-console", Box::new(TestMultiportConsole)),
3943
]
4044
}
4145

Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
use macros::{guest, host};
2+
3+
pub struct TestMultiportConsole;
4+
5+
#[host]
6+
mod host {
7+
use super::*;
8+
9+
use crate::common::setup_fs_and_enter;
10+
use crate::{krun_call, krun_call_u32};
11+
use crate::{Test, TestSetup};
12+
use krun_sys::*;
13+
use std::ffi::CString;
14+
use std::io::{BufRead, BufReader, Write};
15+
use std::os::fd::AsRawFd;
16+
use std::os::unix::net::UnixStream;
17+
use std::{mem, thread};
18+
19+
fn spawn_ping_pong_responder(stream: UnixStream) {
20+
thread::spawn(move || {
21+
let mut reader = BufReader::new(stream.try_clone().unwrap());
22+
let mut writer = stream;
23+
let mut line = String::new();
24+
while reader.read_line(&mut line).is_ok() && !line.is_empty() {
25+
let response = line.replace("PING", "PONG");
26+
writer.write_all(response.as_bytes()).unwrap();
27+
writer.flush().unwrap();
28+
line.clear();
29+
}
30+
});
31+
}
32+
33+
fn add_console_port(
34+
ctx: u32,
35+
console_id: u32,
36+
name: &str,
37+
) -> anyhow::Result<(UnixStream, UnixStream)> {
38+
let (guest, host) = UnixStream::pair()?;
39+
let name_cstring = CString::new(name)?;
40+
unsafe {
41+
krun_call!(krun_add_console_port_inout(
42+
ctx,
43+
console_id,
44+
name_cstring.as_ptr(),
45+
guest.as_raw_fd(),
46+
guest.as_raw_fd()
47+
))?;
48+
}
49+
Ok((guest, host))
50+
}
51+
52+
impl Test for TestMultiportConsole {
53+
fn start_vm(self: Box<Self>, test_setup: TestSetup) -> anyhow::Result<()> {
54+
unsafe {
55+
krun_call!(krun_set_log_level(KRUN_LOG_LEVEL_WARN))?;
56+
let ctx = krun_call_u32!(krun_create_ctx())?;
57+
58+
// Disable implicit console
59+
krun_call!(krun_disable_implicit_console(ctx))?;
60+
61+
// Add a default console with STDIN/STDOUT/STDERR
62+
krun_call!(krun_add_virtio_console_default(
63+
ctx,
64+
std::io::stdin().as_raw_fd(),
65+
std::io::stdout().as_raw_fd(),
66+
std::io::stderr().as_raw_fd()
67+
))?;
68+
69+
// Create a multiport console
70+
let console_id = krun_call_u32!(krun_add_virtio_console_multiport(ctx))?;
71+
72+
// Add 3 inout ports with different names
73+
let (port1_guest, port1_host) =
74+
add_console_port(ctx, console_id, "test-port-alpha")?;
75+
let (port2_guest, port2_host) =
76+
add_console_port(ctx, console_id, "test-port-beta")?;
77+
let (port3_guest, port3_host) =
78+
add_console_port(ctx, console_id, "test-port-gamma")?;
79+
80+
spawn_ping_pong_responder(port1_host);
81+
spawn_ping_pong_responder(port2_host);
82+
spawn_ping_pong_responder(port3_host);
83+
84+
// Keep the guest-side streams open for the VM
85+
mem::forget(port1_guest);
86+
mem::forget(port2_guest);
87+
mem::forget(port3_guest);
88+
89+
krun_call!(krun_set_vm_config(ctx, 1, 1024))?;
90+
setup_fs_and_enter(ctx, test_setup)?;
91+
}
92+
Ok(())
93+
}
94+
}
95+
}
96+
97+
#[guest]
98+
mod guest {
99+
use super::*;
100+
use crate::Test;
101+
use std::fs;
102+
use std::io::{BufRead, BufReader, Write};
103+
104+
fn test_port(port_map: &std::collections::HashMap<String, String>, name: &str, message: &str) {
105+
let device_path = format!("/dev/{}", port_map.get(name).unwrap());
106+
let mut port = fs::OpenOptions::new()
107+
.read(true)
108+
.write(true)
109+
.open(&device_path)
110+
.unwrap();
111+
112+
port.write_all(message.as_bytes()).unwrap();
113+
port.flush().unwrap();
114+
115+
let mut reader = BufReader::new(port);
116+
let mut response = String::new();
117+
reader.read_line(&mut response).unwrap();
118+
119+
let expected = message.replace("PING", "PONG").trim().to_string();
120+
assert_eq!(response.trim(), expected, "{}: wrong response", name);
121+
}
122+
123+
impl Test for TestMultiportConsole {
124+
fn in_guest(self: Box<Self>) {
125+
// List all virtio ports to find our named ports
126+
let ports_dir = "/sys/class/virtio-ports";
127+
128+
let mut port_map = std::collections::HashMap::new();
129+
130+
for entry in fs::read_dir(ports_dir).unwrap() {
131+
let entry = entry.unwrap();
132+
let port_name_path = entry.path().join("name");
133+
134+
if port_name_path.exists() {
135+
let port_name = fs::read_to_string(&port_name_path)
136+
.unwrap()
137+
.trim()
138+
.to_string();
139+
140+
if !port_name.is_empty() {
141+
let device_name = entry.file_name().to_string_lossy().to_string();
142+
port_map.insert(port_name, device_name);
143+
}
144+
}
145+
}
146+
147+
// Verify we have all three ports
148+
assert!(
149+
port_map.contains_key("test-port-alpha"),
150+
"test-port-alpha not found"
151+
);
152+
assert!(
153+
port_map.contains_key("test-port-beta"),
154+
"test-port-beta not found"
155+
);
156+
assert!(
157+
port_map.contains_key("test-port-gamma"),
158+
"test-port-gamma not found"
159+
);
160+
161+
test_port(&port_map, "test-port-alpha", "PING-ALPHA\n");
162+
test_port(&port_map, "test-port-beta", "PING-BETA\n");
163+
test_port(&port_map, "test-port-gamma", "PING-GAMMA\n");
164+
165+
println!("OK");
166+
}
167+
}
168+
}

0 commit comments

Comments
 (0)