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

google / alioth / 17119523075

21 Aug 2025 01:27AM UTC coverage: 11.963% (+1.6%) from 10.411%
17119523075

push

github

Lencerf
feat(virtio): support packed queue

Signed-off-by: Changyuan Lyu <changyuanl@google.com>

91 of 132 new or added lines in 5 files covered. (68.94%)

155 existing lines in 8 files now uncovered.

838 of 7005 relevant lines covered (11.96%)

17.32 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::ptr::eq as ptr_eq;
17
use std::sync::atomic::{AtomicBool, AtomicU16, AtomicU64};
18
use std::sync::mpsc::{self, TryRecvError};
19

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

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

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

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

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

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

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

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

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

115
#[rstest]
116
fn test_copy_from_reader(fixture_ram_bus: RamBus, fixture_queue: QueueReg) {
117
    let ram = fixture_ram_bus.lock_layout();
118
    let mut q = Queue::new(
119
        SplitQueue::new(&fixture_queue, &*ram, false)
120
            .unwrap()
121
            .unwrap(),
122
    );
123
    assert!(ptr_eq(q.reg(), &fixture_queue));
124

125
    let (irq_tx, irq_rx) = mpsc::channel();
126
    let irq_sender = FakeIrqSender { q_tx: irq_tx };
127

128
    let str_0 = "Hello, World!";
129
    let str_1 = "Goodbye, World!";
130
    let str_2 = "Bose-Einstein condensate";
131
    let addr_0 = DATA_ADDR;
132
    let addr_1 = addr_0 + str_0.len() as u64;
133
    let addr_2 = addr_1 + str_1.len() as u64;
134
    let addr_3 = addr_2 + str_2.len() as u64;
135

136
    let mut reader = Reader {
137
        data: &[
138
            ReaderData::Buf(str_0.as_bytes()),
139
            ReaderData::Buf(str_1.as_bytes()),
140
            ReaderData::Err(ErrorKind::WouldBlock),
141
            ReaderData::Buf(str_2.as_bytes()),
142
            ReaderData::Err(ErrorKind::Interrupted),
143
        ],
144
        pos: 0,
145
        index: 0,
146
    };
147

148
    // no writable descriptors
149
    q.handle_desc(0, &irq_sender, copy_from_reader(&mut reader))
150
        .unwrap();
151
    assert_eq!(irq_rx.try_recv(), Err(TryRecvError::Empty));
152

153
    // empty writable descripter
154
    q.add_desc(0, &[], &[(addr_0, 0)]);
155
    q.handle_desc(0, &irq_sender, copy_from_reader(&mut reader))
156
        .unwrap();
157
    assert_eq!(irq_rx.try_recv(), Ok(0));
158

159
    q.add_desc(
160
        0,
161
        &[],
162
        &[(addr_0, str_0.len() as u32), (addr_1, str_1.len() as u32)],
163
    );
164
    q.handle_desc(0, &irq_sender, copy_from_reader(&mut reader))
165
        .unwrap();
166
    assert_eq!(irq_rx.try_recv(), Ok(0));
167

168
    // no writable descriptors
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.add_desc(2, &[], &[(addr_2, str_2.len() as u32)]);
174
    // will hit ErrorKind::WouldBlock
175
    q.handle_desc(0, &irq_sender, copy_from_reader(&mut reader))
176
        .unwrap();
177
    assert_eq!(irq_rx.try_recv(), Err(TryRecvError::Empty));
178

179
    q.handle_desc(0, &irq_sender, copy_from_reader(&mut reader))
180
        .unwrap();
181
    assert_eq!(irq_rx.try_recv(), Ok(0));
182

183
    q.add_desc(3, &[], &[(addr_3, 12)]);
184

185
    // will hit ErrorKind::Interrupted
186
    assert_matches!(
187
        q.handle_desc(0, &irq_sender, copy_from_reader(&mut reader)),
188
        Err(Error::System { error, .. }) if error.kind() == ErrorKind::Interrupted
189
    );
190

191
    q.handle_desc(0, &irq_sender, copy_from_reader(&mut reader))
192
        .unwrap();
193
    assert_eq!(irq_rx.try_recv(), Err(TryRecvError::Empty));
194

195
    for (s, addr) in [(str_0, addr_0), (str_1, addr_1), (str_2, addr_2)] {
196
        let mut buf = vec![0u8; s.len()];
197
        ram.read(addr, &mut buf).unwrap();
198
        assert_eq!(String::from_utf8_lossy(buf.as_slice()), s);
199
    }
200
}
201

202
#[derive(Debug)]
203
enum WriterData<'a> {
204
    Buf(&'a mut [u8]),
205
    Err(ErrorKind),
206
}
207

208
#[derive(Debug)]
209
struct Writer<'a> {
210
    data: &'a mut [WriterData<'a>],
211
    index: usize,
212
    pos: usize,
213
}
214

215
impl<'a> Write for Writer<'a> {
216
    fn write(&mut self, _: &[u8]) -> std::io::Result<usize> {
217
        unreachable!()
218
    }
219

220
    fn flush(&mut self) -> std::io::Result<()> {
×
UNCOV
221
        Ok(())
×
222
    }
223

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

264
#[rstest]
265
fn test_copy_to_writer(fixture_ram_bus: RamBus, fixture_queue: QueueReg) {
266
    let ram = fixture_ram_bus.lock_layout();
267
    let q = SplitQueue::new(&fixture_queue, &*ram, false)
268
        .unwrap()
269
        .unwrap();
270
    let mut q = Queue::new(q);
271
    let (irq_tx, irq_rx) = mpsc::channel();
272
    let irq_sender = FakeIrqSender { q_tx: irq_tx };
273

274
    let str_0 = "Hello, World!";
275
    let str_1 = "Goodbye, World!";
276
    let str_2 = "Bose-Einstein condensate";
277
    let addr_0 = DATA_ADDR;
278
    let addr_1 = addr_0 + str_0.len() as u64;
279
    let addr_2 = addr_1 + str_1.len() as u64;
280
    let addr_3 = addr_2 + str_2.len() as u64;
281
    for (s, addr) in [(str_0, addr_0), (str_1, addr_1), (str_2, addr_2)] {
282
        ram.write(addr, s.as_bytes()).unwrap();
283
    }
284

285
    let mut buf_0 = vec![0u8; str_0.len()];
286
    let mut buf_1 = vec![0u8; str_1.len()];
287
    let mut buf_2 = vec![0u8; str_2.len()];
288
    let mut writer = Writer {
289
        data: &mut [
290
            WriterData::Buf(buf_0.as_mut_slice()),
291
            WriterData::Buf(buf_1.as_mut_slice()),
292
            WriterData::Err(ErrorKind::WouldBlock),
293
            WriterData::Buf(buf_2.as_mut_slice()),
294
            WriterData::Err(ErrorKind::Interrupted),
295
        ],
296
        pos: 0,
297
        index: 0,
298
    };
299

300
    // no readable descriptors
301
    q.handle_desc(0, &irq_sender, copy_to_writer(&mut writer))
302
        .unwrap();
303
    assert_eq!(irq_rx.try_recv(), Err(TryRecvError::Empty));
304

305
    // empty readble descripter
306
    q.add_desc(0, &[(addr_0, 0)], &[]);
307
    q.handle_desc(0, &irq_sender, copy_to_writer(&mut writer))
308
        .unwrap();
309
    assert_eq!(irq_rx.try_recv(), Ok(0));
310

311
    q.add_desc(
312
        0,
313
        &[(addr_0, str_0.len() as u32), (addr_1, str_1.len() as u32)],
314
        &[],
315
    );
316
    q.handle_desc(0, &irq_sender, copy_to_writer(&mut writer))
317
        .unwrap();
318
    assert_eq!(irq_rx.try_recv(), Ok(0));
319

320
    // no readable descriptors
321
    q.handle_desc(0, &irq_sender, copy_to_writer(&mut writer))
322
        .unwrap();
323
    assert_eq!(irq_rx.try_recv(), Err(TryRecvError::Empty));
324

325
    q.add_desc(2, &[(addr_2, str_2.len() as u32)], &[]);
326
    // will hit ErrorKind::WouldBlock
327
    q.handle_desc(0, &irq_sender, copy_to_writer(&mut writer))
328
        .unwrap();
329
    assert_eq!(irq_rx.try_recv(), Err(TryRecvError::Empty));
330

331
    q.handle_desc(0, &irq_sender, copy_to_writer(&mut writer))
332
        .unwrap();
333
    assert_eq!(irq_rx.try_recv(), Ok(0));
334

335
    q.add_desc(3, &[(addr_3, 12)], &[]);
336

337
    // will hit ErrorKind::Interrupted
338
    assert_matches!(
339
        q.handle_desc(0, &irq_sender, copy_to_writer(&mut writer)),
340
        Err(Error::System { error, .. }) if error.kind() == ErrorKind::Interrupted
341
    );
342

343
    q.handle_desc(0, &irq_sender, copy_to_writer(&mut writer))
344
        .unwrap();
345
    assert_eq!(irq_rx.try_recv(), Err(TryRecvError::Empty));
346

347
    for (buf, s) in [(buf_0, str_0), (buf_1, str_1), (buf_2, str_2)] {
348
        assert_eq!(String::from_utf8_lossy(buf.as_slice()), s)
349
    }
350
}
351

352
#[test]
353
fn test_written_bytes() {
354
    let str_0 = "Hello, World!";
355
    let str_1 = "Goodbye, World!";
356

357
    let mut buf = vec![0u8; str_0.len()];
358
    let mut chain = DescChain {
359
        index: 0,
360
        id: 0,
361
        count: 1,
362
        readable: vec![],
363
        writable: vec![IoSliceMut::new(buf.as_mut_slice())],
364
    };
365
    let reader = str_0.as_bytes();
366
    assert_matches!(
367
        copy_from_reader(reader)(&mut chain),
368
        Ok(Status::Done { len: 13 })
369
    );
370
    assert_eq!(buf.as_slice(), str_0.as_bytes());
371

372
    let mut buf = vec![];
373
    let mut chain = DescChain {
374
        index: 0,
375
        id: 1,
376
        count: 1,
377
        readable: vec![IoSlice::new(str_1.as_bytes())],
378
        writable: vec![],
379
    };
380
    assert_matches!(
381
        copy_to_writer(&mut buf)(&mut chain),
382
        Ok(Status::Done { len: 0 })
383
    );
384
    assert_eq!(buf.as_slice(), str_1.as_bytes());
385
}
386

387
#[rstest]
388
fn test_handle_deferred(fixture_ram_bus: RamBus, fixture_queue: QueueReg) {
389
    let ram = fixture_ram_bus.lock_layout();
390
    let q = SplitQueue::new(&fixture_queue, &*ram, false)
391
        .unwrap()
392
        .unwrap();
393
    let mut q = Queue::new(q);
394
    let (irq_tx, irq_rx) = mpsc::channel();
395
    let irq_sender = FakeIrqSender { q_tx: irq_tx };
396

397
    let str_0 = "Hello, World!";
398
    let str_1 = "Goodbye, World!";
399
    let str_2 = "Bose-Einstein condensate";
400
    let addr_0 = DATA_ADDR;
401
    let addr_1 = addr_0 + str_0.len() as u64;
402
    let addr_2 = addr_1 + str_1.len() as u64;
403
    for (s, addr) in [(str_0, addr_0), (str_1, addr_1), (str_2, addr_2)] {
404
        ram.write(addr, s.as_bytes()).unwrap();
405
    }
406

407
    q.add_desc(
408
        0,
409
        &[(addr_0, str_0.len() as u32), (addr_1, str_1.len() as u32)],
410
        &[],
411
    );
412
    q.add_desc(2, &[(addr_2, str_2.len() as u32)], &[]);
413

414
    let mut ids = vec![];
415
    q.handle_desc(0, &irq_sender, |chain| {
416
        ids.push(chain.id());
417
        Ok(Status::Deferred)
418
    })
419
    .unwrap();
420

421
    assert_eq!(irq_rx.try_recv(), Err(TryRecvError::Empty));
422
    assert_eq!(ids, [0, 2]);
423

424
    q.handle_deferred(0, 0, &irq_sender, |chain| {
425
        assert_eq!(chain.id, 0);
426
        assert_eq!(&*chain.readable[0], str_0.as_bytes());
427
        assert_eq!(&*chain.readable[1], str_1.as_bytes());
428
        assert_eq!(chain.writable.len(), 0);
429
        Ok(0)
430
    })
431
    .unwrap();
432

433
    assert_matches!(
434
        q.handle_deferred(1, 0, &irq_sender, |_| Ok(0)),
435
        Err(Error::InvalidDescriptor { id: 1, .. })
436
    );
437

438
    q.handle_deferred(2, 0, &irq_sender, |chain| {
439
        assert_eq!(chain.id, 2);
440
        assert_eq!(&*chain.readable[0], str_2.as_bytes());
441
        assert_eq!(chain.writable.len(), 0);
442
        Ok(0)
443
    })
444
    .unwrap();
445
}
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