• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

google / alioth / 17114675795

21 Aug 2025 01:36AM UTC coverage: 10.398% (-0.01%) from 10.411%
17114675795

Pull #273

github

web-flow
Merge b894c4553 into 7925c9625
Pull Request #273: feat: virtio packed queue

54 of 117 new or added lines in 9 files covered. (46.15%)

123 existing lines in 6 files now uncovered.

713 of 6857 relevant lines covered (10.4%)

16.1 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

90.77
/alioth/src/virtio/queue/queue_test.rs
1
// Copyright 2025 Google LLC
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
//     https://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14

15
use std::io::{ErrorKind, IoSlice, IoSliceMut, Read, Write};
16
use std::sync::atomic::{AtomicBool, AtomicU16, AtomicU64};
17
use std::sync::mpsc::{self, TryRecvError};
18

19
use assert_matches::assert_matches;
20
use rstest::{fixture, rstest};
21

22
use crate::mem::mapped::{ArcMemPages, RamBus};
23
use crate::virtio::Error;
24
use crate::virtio::queue::split::SplitQueue;
25
use crate::virtio::queue::{
26
    DescChain, QUEUE_SIZE_MAX, QueueReg, VirtQueue, copy_from_reader, copy_to_writer,
27
};
28
use crate::virtio::tests::FakeIrqSender;
29

30
pub const MEM_SIZE: usize = 2 << 20;
31
pub const QUEUE_SIZE: u16 = QUEUE_SIZE_MAX;
32
pub const DESC_ADDR: u64 = 0x1000;
33
pub const AVAIL_ADDR: u64 = 0x2000;
34
pub const USED_ADDR: u64 = 0x3000;
35
pub const DATA_ADDR: u64 = 0x4000;
36

37
#[fixture]
38
pub fn fixture_ram_bus() -> RamBus {
39
    let host_pages = ArcMemPages::from_anonymous(MEM_SIZE, None, None).unwrap();
1✔
40
    let ram_bus = RamBus::new();
1✔
41
    ram_bus.add(0, host_pages).unwrap();
4✔
42
    ram_bus
3✔
43
}
44

45
#[fixture]
46
pub fn fixture_queue() -> QueueReg {
47
    QueueReg {
48
        size: AtomicU16::new(QUEUE_SIZE),
3✔
49
        desc: AtomicU64::new(DESC_ADDR),
3✔
50
        driver: AtomicU64::new(AVAIL_ADDR),
3✔
51
        device: AtomicU64::new(USED_ADDR),
3✔
52
        enabled: AtomicBool::new(true),
3✔
53
    }
54
}
55

56
#[derive(Debug)]
57
enum ReaderData<'a> {
58
    Buf(&'a [u8]),
59
    Err(ErrorKind),
60
}
61

62
#[derive(Debug)]
63
struct Reader<'a> {
64
    data: &'a [ReaderData<'a>],
65
    index: usize,
66
    pos: usize,
67
}
68

69
impl<'a> Read for Reader<'a> {
70
    fn read(&mut self, _: &mut [u8]) -> std::io::Result<usize> {
71
        unreachable!()
72
    }
73

74
    fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> std::io::Result<usize> {
13✔
75
        let mut count = 0;
25✔
76
        let mut buf_iter = bufs.iter_mut();
37✔
77
        let Some(s) = buf_iter.next() else {
23✔
78
            return Ok(0);
3✔
79
        };
UNCOV
80
        let mut buf = s.as_mut();
1✔
81
        loop {
×
82
            let Some(data) = self.data.get(self.index) else {
35✔
83
                break;
2✔
84
            };
UNCOV
85
            match data {
1✔
86
                ReaderData::Buf(data) => {
7✔
UNCOV
87
                    let c = buf.write(&data[self.pos..]).unwrap();
1✔
88
                    self.pos += c;
1✔
89
                    if self.pos == data.len() {
8✔
90
                        self.index += 1;
7✔
91
                        self.pos = 0;
7✔
92
                    }
UNCOV
93
                    count += c;
2✔
94
                    if buf.len() == 0 {
2✔
95
                        let Some(s) = buf_iter.next() else {
9✔
96
                            break;
4✔
97
                        };
UNCOV
98
                        buf = s.as_mut();
1✔
99
                    }
100
                }
101
                ReaderData::Err(kind) => {
5✔
102
                    if count > 0 {
5✔
UNCOV
103
                        break;
×
104
                    }
UNCOV
105
                    self.index += 1;
1✔
106
                    return Err((*kind).into());
1✔
107
                }
108
            }
109
        }
110
        Ok(count)
7✔
111
    }
112
}
113

114
#[rstest]
115
fn test_copy_from_reader(fixture_ram_bus: RamBus, fixture_queue: QueueReg) {
116
    let ram = fixture_ram_bus.lock_layout();
117
    let mut q = SplitQueue::new(&fixture_queue, &*ram, 0).unwrap().unwrap();
118

119
    let (irq_tx, irq_rx) = mpsc::channel();
120
    let irq_sender = FakeIrqSender { q_tx: irq_tx };
121

122
    let str_0 = "Hello, World!";
123
    let str_1 = "Goodbye, World!";
124
    let str_2 = "Bose-Einstein condensate";
125
    let addr_0 = DATA_ADDR;
126
    let addr_1 = addr_0 + str_0.len() as u64;
127
    let addr_2 = addr_1 + str_1.len() as u64;
128
    let addr_3 = addr_2 + str_2.len() as u64;
129

130
    let mut reader = Reader {
131
        data: &[
132
            ReaderData::Buf(str_0.as_bytes()),
133
            ReaderData::Buf(str_1.as_bytes()),
134
            ReaderData::Err(ErrorKind::WouldBlock),
135
            ReaderData::Buf(str_2.as_bytes()),
136
            ReaderData::Err(ErrorKind::Interrupted),
137
        ],
138
        pos: 0,
139
        index: 0,
140
    };
141

142
    // no writable descriptors
143
    q.handle_desc(0, &irq_sender, copy_from_reader(&mut reader))
144
        .unwrap();
145
    assert_eq!(irq_rx.try_recv(), Err(TryRecvError::Empty));
146

147
    // empty writable descripter
148
    q.add_desc(0, &[], &[(addr_0, 0)]);
149
    q.handle_desc(0, &irq_sender, copy_from_reader(&mut reader))
150
        .unwrap();
151
    assert_eq!(irq_rx.try_recv(), Ok(0));
152

153
    q.add_desc(
154
        0,
155
        &[],
156
        &[(addr_0, str_0.len() as u32), (addr_1, str_1.len() as u32)],
157
    );
158
    q.handle_desc(0, &irq_sender, copy_from_reader(&mut reader))
159
        .unwrap();
160
    assert_eq!(irq_rx.try_recv(), Ok(0));
161

162
    // no writable descriptors
163
    q.handle_desc(0, &irq_sender, copy_from_reader(&mut reader))
164
        .unwrap();
165
    assert_eq!(irq_rx.try_recv(), Err(TryRecvError::Empty));
166

167
    q.add_desc(2, &[], &[(addr_2, str_2.len() as u32)]);
168
    // will hit ErrorKind::WouldBlock
169
    q.handle_desc(0, &irq_sender, copy_from_reader(&mut reader))
170
        .unwrap();
171
    assert_eq!(irq_rx.try_recv(), Err(TryRecvError::Empty));
172

173
    q.handle_desc(0, &irq_sender, copy_from_reader(&mut reader))
174
        .unwrap();
175
    assert_eq!(irq_rx.try_recv(), Ok(0));
176

177
    q.add_desc(3, &[], &[(addr_3, 12)]);
178

179
    // will hit ErrorKind::Interrupted
180
    assert_matches!(
181
        q.handle_desc(0, &irq_sender, copy_from_reader(&mut reader)),
182
        Err(Error::System { error, .. }) if error.kind() == ErrorKind::Interrupted
183
    );
184

185
    q.handle_desc(0, &irq_sender, copy_from_reader(&mut reader))
186
        .unwrap();
187
    assert_eq!(irq_rx.try_recv(), Err(TryRecvError::Empty));
188

189
    for (s, addr) in [(str_0, addr_0), (str_1, addr_1), (str_2, addr_2)] {
190
        let mut buf = vec![0u8; s.len()];
191
        ram.read(addr, &mut buf).unwrap();
192
        assert_eq!(String::from_utf8_lossy(buf.as_slice()), s);
193
    }
194
}
195

196
#[derive(Debug)]
197
enum WriterData<'a> {
198
    Buf(&'a mut [u8]),
199
    Err(ErrorKind),
200
}
201

202
#[derive(Debug)]
203
struct Writer<'a> {
204
    data: &'a mut [WriterData<'a>],
205
    index: usize,
206
    pos: usize,
207
}
208

209
impl<'a> Write for Writer<'a> {
210
    fn write(&mut self, _: &[u8]) -> std::io::Result<usize> {
211
        unreachable!()
212
    }
213

UNCOV
214
    fn flush(&mut self) -> std::io::Result<()> {
×
UNCOV
215
        Ok(())
×
216
    }
217

218
    fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> std::io::Result<usize> {
13✔
219
        let mut count = 0;
25✔
220
        let mut buf_iter = bufs.iter();
37✔
221
        let Some(s) = buf_iter.next() else {
23✔
222
            return Ok(0);
3✔
223
        };
UNCOV
224
        let mut buf = s.as_ref();
1✔
UNCOV
225
        loop {
×
226
            let Some(data) = self.data.get_mut(self.index) else {
35✔
227
                break;
2✔
228
            };
UNCOV
229
            match data {
1✔
230
                WriterData::Buf(data) => {
7✔
231
                    let c = buf.read(&mut data[self.pos..]).unwrap();
1✔
UNCOV
232
                    self.pos += c;
1✔
233
                    if self.pos == data.len() {
8✔
234
                        self.index += 1;
7✔
235
                        self.pos = 0;
7✔
236
                    }
237
                    count += c;
2✔
238
                    if buf.len() == 0 {
2✔
239
                        let Some(s) = buf_iter.next() else {
9✔
240
                            break;
4✔
241
                        };
UNCOV
242
                        buf = s.as_ref();
1✔
243
                    }
244
                }
245
                WriterData::Err(kind) => {
5✔
246
                    if count > 0 {
5✔
UNCOV
247
                        break;
×
248
                    }
UNCOV
249
                    self.index += 1;
1✔
UNCOV
250
                    return Err((*kind).into());
1✔
251
                }
252
            }
253
        }
254
        Ok(count)
7✔
255
    }
256
}
257

258
#[rstest]
259
fn test_copy_to_writer(fixture_ram_bus: RamBus, fixture_queue: QueueReg) {
260
    let ram = fixture_ram_bus.lock_layout();
261
    let mut q = SplitQueue::new(&fixture_queue, &*ram, 0).unwrap().unwrap();
262

263
    let (irq_tx, irq_rx) = mpsc::channel();
264
    let irq_sender = FakeIrqSender { q_tx: irq_tx };
265

266
    let str_0 = "Hello, World!";
267
    let str_1 = "Goodbye, World!";
268
    let str_2 = "Bose-Einstein condensate";
269
    let addr_0 = DATA_ADDR;
270
    let addr_1 = addr_0 + str_0.len() as u64;
271
    let addr_2 = addr_1 + str_1.len() as u64;
272
    let addr_3 = addr_2 + str_2.len() as u64;
273
    for (s, addr) in [(str_0, addr_0), (str_1, addr_1), (str_2, addr_2)] {
274
        ram.write(addr, s.as_bytes()).unwrap();
275
    }
276

277
    let mut buf_0 = vec![0u8; str_0.len()];
278
    let mut buf_1 = vec![0u8; str_1.len()];
279
    let mut buf_2 = vec![0u8; str_2.len()];
280
    let mut writer = Writer {
281
        data: &mut [
282
            WriterData::Buf(buf_0.as_mut_slice()),
283
            WriterData::Buf(buf_1.as_mut_slice()),
284
            WriterData::Err(ErrorKind::WouldBlock),
285
            WriterData::Buf(buf_2.as_mut_slice()),
286
            WriterData::Err(ErrorKind::Interrupted),
287
        ],
288
        pos: 0,
289
        index: 0,
290
    };
291

292
    // no readable descriptors
293
    q.handle_desc(0, &irq_sender, copy_to_writer(&mut writer))
294
        .unwrap();
295
    assert_eq!(irq_rx.try_recv(), Err(TryRecvError::Empty));
296

297
    // empty readble descripter
298
    q.add_desc(0, &[(addr_0, 0)], &[]);
299
    q.handle_desc(0, &irq_sender, copy_to_writer(&mut writer))
300
        .unwrap();
301
    assert_eq!(irq_rx.try_recv(), Ok(0));
302

303
    q.add_desc(
304
        0,
305
        &[(addr_0, str_0.len() as u32), (addr_1, str_1.len() as u32)],
306
        &[],
307
    );
308
    q.handle_desc(0, &irq_sender, copy_to_writer(&mut writer))
309
        .unwrap();
310
    assert_eq!(irq_rx.try_recv(), Ok(0));
311

312
    // no readable descriptors
313
    q.handle_desc(0, &irq_sender, copy_to_writer(&mut writer))
314
        .unwrap();
315
    assert_eq!(irq_rx.try_recv(), Err(TryRecvError::Empty));
316

317
    q.add_desc(2, &[(addr_2, str_2.len() as u32)], &[]);
318
    // will hit ErrorKind::WouldBlock
319
    q.handle_desc(0, &irq_sender, copy_to_writer(&mut writer))
320
        .unwrap();
321
    assert_eq!(irq_rx.try_recv(), Err(TryRecvError::Empty));
322

323
    q.handle_desc(0, &irq_sender, copy_to_writer(&mut writer))
324
        .unwrap();
325
    assert_eq!(irq_rx.try_recv(), Ok(0));
326

327
    q.add_desc(3, &[(addr_3, 12)], &[]);
328

329
    // will hit ErrorKind::Interrupted
330
    assert_matches!(
331
        q.handle_desc(0, &irq_sender, copy_to_writer(&mut writer)),
332
        Err(Error::System { error, .. }) if error.kind() == ErrorKind::Interrupted
333
    );
334

335
    q.handle_desc(0, &irq_sender, copy_to_writer(&mut writer))
336
        .unwrap();
337
    assert_eq!(irq_rx.try_recv(), Err(TryRecvError::Empty));
338

339
    for (buf, s) in [(buf_0, str_0), (buf_1, str_1), (buf_2, str_2)] {
340
        assert_eq!(String::from_utf8_lossy(buf.as_slice()), s)
341
    }
342
}
343

344
#[test]
345
fn test_written_bytes() {
346
    let str_0 = "Hello, World!";
347
    let str_1 = "Goodbye, World!";
348

349
    let mut buf = vec![0u8; str_0.len()];
350
    let mut chain = DescChain {
351
        id: 0,
352
        readable: vec![],
353
        writable: vec![IoSliceMut::new(buf.as_mut_slice())],
354
    };
355
    let reader = str_0.as_bytes();
356
    assert_matches!(copy_from_reader(reader)(&mut chain), Ok(Some(13)));
357
    assert_eq!(buf.as_slice(), str_0.as_bytes());
358

359
    let mut buf = vec![];
360
    let mut chain = DescChain {
361
        id: 1,
362
        readable: vec![IoSlice::new(str_1.as_bytes())],
363
        writable: vec![],
364
    };
365
    assert_matches!(copy_to_writer(&mut buf)(&mut chain), Ok(Some(0)));
366
    assert_eq!(buf.as_slice(), str_1.as_bytes());
367
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc