Skip to content

Commit 078c204

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

File tree

2 files changed

+219
-0
lines changed

2 files changed

+219
-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: 215 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,215 @@
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+
impl Test for TestMultiportConsole {
20+
fn start_vm(self: Box<Self>, test_setup: TestSetup) -> anyhow::Result<()> {
21+
unsafe {
22+
krun_call!(krun_set_log_level(KRUN_LOG_LEVEL_WARN))?;
23+
let ctx = krun_call_u32!(krun_create_ctx())?;
24+
25+
// Disable implicit console
26+
krun_call!(krun_disable_implicit_console(ctx))?;
27+
28+
// Add a default console with STDIN/STDOUT/STDERR
29+
krun_call!(krun_add_virtio_console_default(
30+
ctx,
31+
std::io::stdin().as_raw_fd(),
32+
std::io::stdout().as_raw_fd(),
33+
std::io::stderr().as_raw_fd()
34+
))?;
35+
36+
// Create a multiport console
37+
let console_id = krun_call_u32!(krun_add_virtio_console_multiport(ctx))?;
38+
39+
let (port1_guest, port1_host) = UnixStream::pair()?;
40+
let (port2_guest, port2_host) = UnixStream::pair()?;
41+
let (port3_guest, port3_host) = UnixStream::pair()?;
42+
43+
// Add 3 inout ports with different names
44+
let port1_name = CString::new("test-port-alpha")?;
45+
krun_call!(krun_add_console_port_inout(
46+
ctx,
47+
console_id,
48+
port1_name.as_ptr(),
49+
port1_guest.as_raw_fd(),
50+
port1_guest.as_raw_fd()
51+
))?;
52+
53+
let port2_name = CString::new("test-port-beta")?;
54+
krun_call!(krun_add_console_port_inout(
55+
ctx,
56+
console_id,
57+
port2_name.as_ptr(),
58+
port2_guest.as_raw_fd(),
59+
port2_guest.as_raw_fd()
60+
))?;
61+
62+
let port3_name = CString::new("test-port-gamma")?;
63+
krun_call!(krun_add_console_port_inout(
64+
ctx,
65+
console_id,
66+
port3_name.as_ptr(),
67+
port3_guest.as_raw_fd(),
68+
port3_guest.as_raw_fd()
69+
))?;
70+
71+
// Spawn handler threads for each port that echo back
72+
thread::spawn(move || {
73+
let mut reader = BufReader::new(port1_host.try_clone().unwrap());
74+
let mut writer = port1_host;
75+
let mut line = String::new();
76+
while reader.read_line(&mut line).is_ok() && !line.is_empty() {
77+
let response = line.replace("PING", "PONG");
78+
writer.write_all(response.as_bytes()).unwrap();
79+
writer.flush().unwrap();
80+
line.clear();
81+
}
82+
});
83+
84+
thread::spawn(move || {
85+
let mut reader = BufReader::new(port2_host.try_clone().unwrap());
86+
let mut writer = port2_host;
87+
let mut line = String::new();
88+
while reader.read_line(&mut line).is_ok() && !line.is_empty() {
89+
let response = line.replace("PING", "PONG");
90+
writer.write_all(response.as_bytes()).unwrap();
91+
writer.flush().unwrap();
92+
line.clear();
93+
}
94+
});
95+
96+
thread::spawn(move || {
97+
let mut reader = BufReader::new(port3_host.try_clone().unwrap());
98+
let mut writer = port3_host;
99+
let mut line = String::new();
100+
while reader.read_line(&mut line).is_ok() && !line.is_empty() {
101+
let response = line.replace("PING", "PONG");
102+
writer.write_all(response.as_bytes()).unwrap();
103+
writer.flush().unwrap();
104+
line.clear();
105+
}
106+
});
107+
108+
// Keep the guest-side streams open for the VM
109+
mem::forget(port1_guest);
110+
mem::forget(port2_guest);
111+
mem::forget(port3_guest);
112+
113+
krun_call!(krun_set_vm_config(ctx, 1, 1024))?;
114+
setup_fs_and_enter(ctx, test_setup)?;
115+
}
116+
Ok(())
117+
}
118+
}
119+
}
120+
121+
#[guest]
122+
mod guest {
123+
use super::*;
124+
use crate::Test;
125+
use std::fs;
126+
use std::io::{BufRead, BufReader, Write};
127+
128+
impl Test for TestMultiportConsole {
129+
fn in_guest(self: Box<Self>) {
130+
// List all virtio ports to find our named ports
131+
let ports_dir = "/sys/class/virtio-ports";
132+
133+
let mut port_map = std::collections::HashMap::new();
134+
135+
for entry in fs::read_dir(ports_dir).unwrap() {
136+
let entry = entry.unwrap();
137+
let port_name_path = entry.path().join("name");
138+
139+
if port_name_path.exists() {
140+
let port_name = fs::read_to_string(&port_name_path)
141+
.unwrap()
142+
.trim()
143+
.to_string();
144+
145+
if !port_name.is_empty() {
146+
let device_name = entry.file_name().to_string_lossy().to_string();
147+
port_map.insert(port_name, device_name);
148+
}
149+
}
150+
}
151+
152+
// Verify we have all three ports
153+
assert!(
154+
port_map.contains_key("test-port-alpha"),
155+
"test-port-alpha not found"
156+
);
157+
assert!(
158+
port_map.contains_key("test-port-beta"),
159+
"test-port-beta not found"
160+
);
161+
assert!(
162+
port_map.contains_key("test-port-gamma"),
163+
"test-port-gamma not found"
164+
);
165+
166+
// Test alpha port: write PING, read PONG
167+
let alpha_path = format!("/dev/{}", port_map.get("test-port-alpha").unwrap());
168+
let mut alpha_port = fs::OpenOptions::new()
169+
.read(true)
170+
.write(true)
171+
.open(&alpha_path)
172+
.unwrap();
173+
alpha_port.write_all(b"PING-ALPHA\n").unwrap();
174+
alpha_port.flush().unwrap();
175+
let mut reader = BufReader::new(alpha_port);
176+
let mut response = String::new();
177+
reader.read_line(&mut response).unwrap();
178+
assert_eq!(response.trim(), "PONG-ALPHA", "Alpha port: wrong response");
179+
180+
// Test beta port: write PING, read PONG
181+
let beta_path = format!("/dev/{}", port_map.get("test-port-beta").unwrap());
182+
let mut beta_port = fs::OpenOptions::new()
183+
.read(true)
184+
.write(true)
185+
.open(&beta_path)
186+
.unwrap();
187+
beta_port.write_all(b"PING-BETA\n").unwrap();
188+
beta_port.flush().unwrap();
189+
let mut reader = BufReader::new(beta_port);
190+
let mut response = String::new();
191+
reader.read_line(&mut response).unwrap();
192+
assert_eq!(response.trim(), "PONG-BETA", "Beta port: wrong response");
193+
194+
// Test gamma port: write PING, read PONG
195+
let gamma_path = format!("/dev/{}", port_map.get("test-port-gamma").unwrap());
196+
let mut gamma_port = fs::OpenOptions::new()
197+
.read(true)
198+
.write(true)
199+
.open(&gamma_path)
200+
.unwrap();
201+
gamma_port.write_all(b"PING-GAMMA\n").unwrap();
202+
gamma_port.flush().unwrap();
203+
let mut reader = BufReader::new(gamma_port);
204+
let mut response = String::new();
205+
reader.read_line(&mut response).unwrap();
206+
assert_eq!(
207+
response.trim(),
208+
"PONG-GAMMA",
209+
"Gamma port: wrong response"
210+
);
211+
212+
println!("OK");
213+
}
214+
}
215+
}

0 commit comments

Comments
 (0)