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

google / alioth / 17186132660

24 Aug 2025 07:45AM UTC coverage: 17.88% (+4.0%) from 13.887%
17186132660

push

github

Lencerf
test: replace tempdir with tempfile

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

1336 of 7472 relevant lines covered (17.88%)

19.0 hits per line

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

92.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::collections::HashMap;
16
use std::io::{ErrorKind, IoSlice, IoSliceMut, Read, Write};
17
use std::ptr::eq as ptr_eq;
18
use std::sync::atomic::Ordering;
19
use std::sync::mpsc::{self, TryRecvError};
20

21
use assert_matches::assert_matches;
22
use rstest::rstest;
23

24
use crate::mem::mapped::RamBus;
25
use crate::virtio::Error;
26
use crate::virtio::queue::split::SplitQueue;
27
use crate::virtio::queue::{
28
    DescChain, Queue, QueueReg, Status, VirtQueue, copy_from_reader, copy_to_writer,
29
};
30
use crate::virtio::tests::{DATA_ADDR, FakeIrqSender, fixture_queues, fixture_ram_bus};
31

32
pub struct UsedDesc {
33
    pub id: u16,
34
    pub delta: u16,
35
    pub len: u32,
36
}
37

38
pub trait VirtQueueGuest<'m>: VirtQueue<'m> {
39
    fn add_desc(
40
        &mut self,
41
        index: Self::Index,
42
        ids: &[u16],
43
        readable: &[(u64, u32)],
44
        writable: &[(u64, u32)],
45
    ) -> u16;
46

47
    fn get_used(&mut self, index: Self::Index, chains: &HashMap<u16, Vec<u16>>)
48
    -> Option<UsedDesc>;
49
}
50

51
pub struct GuestQueue<'m, Q>
52
where
53
    Q: VirtQueueGuest<'m>,
54
{
55
    q: Q,
56
    avail: Q::Index,
57
    used: Q::Index,
58
    ids: Vec<bool>,
59
    chains: HashMap<u16, Vec<u16>>,
60
    next_id: u16,
61
}
62

63
impl<'m, Q> GuestQueue<'m, Q>
64
where
65
    Q: VirtQueueGuest<'m>,
66
{
67
    pub fn new(q: Q, reg: &QueueReg) -> Self {
19✔
68
        let size = reg.size.load(Ordering::Acquire);
70✔
69
        Self {
70
            q,
71
            avail: Q::INIT_INDEX,
72
            used: Q::INIT_INDEX,
73
            ids: vec![false; size as usize],
35✔
74
            chains: HashMap::new(),
19✔
75
            next_id: 0,
76
        }
77
    }
78
}
79

80
impl<'m, Q> GuestQueue<'m, Q>
81
where
82
    Q: VirtQueueGuest<'m>,
83
{
84
    pub fn add_desc(&mut self, readable: &[(u64, u32)], writable: &[(u64, u32)]) -> u16 {
52✔
85
        let mut ids = vec![];
102✔
86
        let total = readable.len() + writable.len();
202✔
87
        for _ in 0..self.ids.len() {
104✔
88
            if !self.ids[self.next_id as usize] {
150✔
89
                ids.push(self.next_id);
74✔
90
                self.ids[self.next_id as usize] = true;
74✔
91
            }
92
            self.next_id = self.next_id.wrapping_add(1) % self.ids.len() as u16;
220✔
93
            if ids.len() == total {
148✔
94
                break;
50✔
95
            }
96
        }
97
        assert_eq!(ids.len(), total);
154✔
98
        let delta = self.q.add_desc(self.avail, &ids, readable, writable);
352✔
99
        self.avail = self.q.index_add(self.avail, delta);
152✔
100
        let head_id = ids[0];
102✔
101
        self.chains.insert(head_id, ids);
202✔
102
        head_id
50✔
103
    }
104

105
    pub fn get_used(&mut self) -> Option<UsedDesc> {
32✔
106
        let used = self.q.get_used(self.used, &self.chains)?;
152✔
107
        let ids = self.chains.remove(&used.id).unwrap();
2✔
108
        for id in ids {
128✔
109
            self.ids[id as usize] = false;
4✔
110
        }
111
        self.used = self.q.index_add(self.used, used.delta);
2✔
112
        Some(used)
2✔
113
    }
114
}
115

116
#[derive(Debug)]
117
enum ReaderData<'a> {
118
    Buf(&'a [u8]),
119
    Err(ErrorKind),
120
}
121

122
#[derive(Debug)]
123
struct Reader<'a> {
124
    data: &'a [ReaderData<'a>],
125
    index: usize,
126
    pos: usize,
127
}
128

129
impl<'a> Read for Reader<'a> {
130
    fn read(&mut self, _: &mut [u8]) -> std::io::Result<usize> {
131
        unreachable!()
132
    }
133

134
    fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> std::io::Result<usize> {
13✔
135
        let mut count = 0;
25✔
136
        let mut buf_iter = bufs.iter_mut();
37✔
137
        let Some(s) = buf_iter.next() else {
23✔
138
            return Ok(0);
3✔
139
        };
140
        let mut buf = s.as_mut();
1✔
141
        loop {
×
142
            let Some(data) = self.data.get(self.index) else {
35✔
143
                break;
2✔
144
            };
145
            match data {
1✔
146
                ReaderData::Buf(data) => {
7✔
147
                    let c = buf.write(&data[self.pos..]).unwrap();
1✔
148
                    self.pos += c;
1✔
149
                    if self.pos == data.len() {
8✔
150
                        self.index += 1;
7✔
151
                        self.pos = 0;
7✔
152
                    }
153
                    count += c;
2✔
154
                    if buf.len() == 0 {
2✔
155
                        let Some(s) = buf_iter.next() else {
9✔
156
                            break;
4✔
157
                        };
158
                        buf = s.as_mut();
1✔
159
                    }
160
                }
161
                ReaderData::Err(kind) => {
5✔
162
                    if count > 0 {
5✔
163
                        break;
×
164
                    }
165
                    self.index += 1;
1✔
166
                    return Err((*kind).into());
1✔
167
                }
168
            }
169
        }
170
        Ok(count)
7✔
171
    }
172
}
173

174
#[rstest]
175
fn test_copy_from_reader(fixture_ram_bus: RamBus, fixture_queues: Box<[QueueReg]>) {
176
    let ram = fixture_ram_bus.lock_layout();
177
    let reg = &fixture_queues[0];
178
    let mut host_q = Queue::new(
179
        SplitQueue::new(reg, &*ram, false).unwrap().unwrap(),
180
        reg,
181
        &ram,
182
    );
183
    let mut guest_q = GuestQueue::new(SplitQueue::new(reg, &*ram, false).unwrap().unwrap(), reg);
184
    assert!(ptr_eq(host_q.reg(), reg));
185

186
    let (irq_tx, irq_rx) = mpsc::channel();
187
    let irq_sender = FakeIrqSender { q_tx: irq_tx };
188

189
    let str_0 = "Hello, World!";
190
    let str_1 = "Goodbye, World!";
191
    let str_2 = "Bose-Einstein condensate";
192
    let addr_0 = DATA_ADDR;
193
    let addr_1 = addr_0 + str_0.len() as u64;
194
    let addr_2 = addr_1 + str_1.len() as u64;
195
    let addr_3 = addr_2 + str_2.len() as u64;
196

197
    let mut reader = Reader {
198
        data: &[
199
            ReaderData::Buf(str_0.as_bytes()),
200
            ReaderData::Buf(str_1.as_bytes()),
201
            ReaderData::Err(ErrorKind::WouldBlock),
202
            ReaderData::Buf(str_2.as_bytes()),
203
            ReaderData::Err(ErrorKind::Interrupted),
204
        ],
205
        pos: 0,
206
        index: 0,
207
    };
208

209
    // no writable descriptors
210
    host_q
211
        .handle_desc(0, &irq_sender, copy_from_reader(&mut reader))
212
        .unwrap();
213
    assert_eq!(irq_rx.try_recv(), Err(TryRecvError::Empty));
214

215
    // empty writable descripter
216
    guest_q.add_desc(&[], &[(addr_0, 0)]);
217
    host_q
218
        .handle_desc(0, &irq_sender, copy_from_reader(&mut reader))
219
        .unwrap();
220
    assert_eq!(irq_rx.try_recv(), Ok(0));
221

222
    guest_q.add_desc(
223
        &[],
224
        &[(addr_0, str_0.len() as u32), (addr_1, str_1.len() as u32)],
225
    );
226
    host_q
227
        .handle_desc(0, &irq_sender, copy_from_reader(&mut reader))
228
        .unwrap();
229
    assert_eq!(irq_rx.try_recv(), Ok(0));
230

231
    // no writable descriptors
232
    host_q
233
        .handle_desc(0, &irq_sender, copy_from_reader(&mut reader))
234
        .unwrap();
235
    assert_eq!(irq_rx.try_recv(), Err(TryRecvError::Empty));
236

237
    guest_q.add_desc(&[], &[(addr_2, str_2.len() as u32)]);
238
    // will hit ErrorKind::WouldBlock
239
    host_q
240
        .handle_desc(0, &irq_sender, copy_from_reader(&mut reader))
241
        .unwrap();
242
    assert_eq!(irq_rx.try_recv(), Err(TryRecvError::Empty));
243

244
    host_q
245
        .handle_desc(0, &irq_sender, copy_from_reader(&mut reader))
246
        .unwrap();
247
    assert_eq!(irq_rx.try_recv(), Ok(0));
248

249
    guest_q.add_desc(&[], &[(addr_3, 12)]);
250

251
    // will hit ErrorKind::Interrupted
252
    assert_matches!(
253
        host_q.handle_desc(0, &irq_sender, copy_from_reader(&mut reader)),
254
        Err(Error::System { error, .. }) if error.kind() == ErrorKind::Interrupted
255
    );
256

257
    host_q
258
        .handle_desc(0, &irq_sender, copy_from_reader(&mut reader))
259
        .unwrap();
260
    assert_eq!(irq_rx.try_recv(), Err(TryRecvError::Empty));
261

262
    for (s, addr) in [(str_0, addr_0), (str_1, addr_1), (str_2, addr_2)] {
263
        let mut buf = vec![0u8; s.len()];
264
        ram.read(addr, &mut buf).unwrap();
265
        assert_eq!(String::from_utf8_lossy(buf.as_slice()), s);
266
    }
267
}
268

269
#[derive(Debug)]
270
enum WriterData<'a> {
271
    Buf(&'a mut [u8]),
272
    Err(ErrorKind),
273
}
274

275
#[derive(Debug)]
276
struct Writer<'a> {
277
    data: &'a mut [WriterData<'a>],
278
    index: usize,
279
    pos: usize,
280
}
281

282
impl<'a> Write for Writer<'a> {
283
    fn write(&mut self, _: &[u8]) -> std::io::Result<usize> {
284
        unreachable!()
285
    }
286

287
    fn flush(&mut self) -> std::io::Result<()> {
×
288
        Ok(())
×
289
    }
290

291
    fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> std::io::Result<usize> {
13✔
292
        let mut count = 0;
25✔
293
        let mut buf_iter = bufs.iter();
37✔
294
        let Some(s) = buf_iter.next() else {
23✔
295
            return Ok(0);
3✔
296
        };
297
        let mut buf = s.as_ref();
1✔
298
        loop {
×
299
            let Some(data) = self.data.get_mut(self.index) else {
35✔
300
                break;
2✔
301
            };
302
            match data {
1✔
303
                WriterData::Buf(data) => {
7✔
304
                    let c = buf.read(&mut data[self.pos..]).unwrap();
1✔
305
                    self.pos += c;
1✔
306
                    if self.pos == data.len() {
8✔
307
                        self.index += 1;
7✔
308
                        self.pos = 0;
7✔
309
                    }
310
                    count += c;
2✔
311
                    if buf.len() == 0 {
2✔
312
                        let Some(s) = buf_iter.next() else {
9✔
313
                            break;
4✔
314
                        };
315
                        buf = s.as_ref();
1✔
316
                    }
317
                }
318
                WriterData::Err(kind) => {
5✔
319
                    if count > 0 {
5✔
320
                        break;
×
321
                    }
322
                    self.index += 1;
1✔
323
                    return Err((*kind).into());
1✔
324
                }
325
            }
326
        }
327
        Ok(count)
7✔
328
    }
329
}
330

331
#[rstest]
332
fn test_copy_to_writer(fixture_ram_bus: RamBus, fixture_queues: Box<[QueueReg]>) {
333
    let ram = fixture_ram_bus.lock_layout();
334
    let reg = &fixture_queues[0];
335
    let mut host_q = Queue::new(
336
        SplitQueue::new(reg, &*ram, false).unwrap().unwrap(),
337
        reg,
338
        &ram,
339
    );
340
    let mut guest_q = GuestQueue::new(SplitQueue::new(reg, &*ram, false).unwrap().unwrap(), reg);
341
    let (irq_tx, irq_rx) = mpsc::channel();
342
    let irq_sender = FakeIrqSender { q_tx: irq_tx };
343

344
    let str_0 = "Hello, World!";
345
    let str_1 = "Goodbye, World!";
346
    let str_2 = "Bose-Einstein condensate";
347
    let addr_0 = DATA_ADDR;
348
    let addr_1 = addr_0 + str_0.len() as u64;
349
    let addr_2 = addr_1 + str_1.len() as u64;
350
    let addr_3 = addr_2 + str_2.len() as u64;
351
    for (s, addr) in [(str_0, addr_0), (str_1, addr_1), (str_2, addr_2)] {
352
        ram.write(addr, s.as_bytes()).unwrap();
353
    }
354

355
    let mut buf_0 = vec![0u8; str_0.len()];
356
    let mut buf_1 = vec![0u8; str_1.len()];
357
    let mut buf_2 = vec![0u8; str_2.len()];
358
    let mut writer = Writer {
359
        data: &mut [
360
            WriterData::Buf(buf_0.as_mut_slice()),
361
            WriterData::Buf(buf_1.as_mut_slice()),
362
            WriterData::Err(ErrorKind::WouldBlock),
363
            WriterData::Buf(buf_2.as_mut_slice()),
364
            WriterData::Err(ErrorKind::Interrupted),
365
        ],
366
        pos: 0,
367
        index: 0,
368
    };
369

370
    // no readable descriptors
371
    host_q
372
        .handle_desc(0, &irq_sender, copy_to_writer(&mut writer))
373
        .unwrap();
374
    assert_eq!(irq_rx.try_recv(), Err(TryRecvError::Empty));
375

376
    // empty readble descripter
377
    guest_q.add_desc(&[(addr_0, 0)], &[]);
378
    host_q
379
        .handle_desc(0, &irq_sender, copy_to_writer(&mut writer))
380
        .unwrap();
381
    assert_eq!(irq_rx.try_recv(), Ok(0));
382

383
    guest_q.add_desc(
384
        &[(addr_0, str_0.len() as u32), (addr_1, str_1.len() as u32)],
385
        &[],
386
    );
387
    host_q
388
        .handle_desc(0, &irq_sender, copy_to_writer(&mut writer))
389
        .unwrap();
390
    assert_eq!(irq_rx.try_recv(), Ok(0));
391

392
    // no readable descriptors
393
    host_q
394
        .handle_desc(0, &irq_sender, copy_to_writer(&mut writer))
395
        .unwrap();
396
    assert_eq!(irq_rx.try_recv(), Err(TryRecvError::Empty));
397

398
    guest_q.add_desc(&[(addr_2, str_2.len() as u32)], &[]);
399
    // will hit ErrorKind::WouldBlock
400
    host_q
401
        .handle_desc(0, &irq_sender, copy_to_writer(&mut writer))
402
        .unwrap();
403
    assert_eq!(irq_rx.try_recv(), Err(TryRecvError::Empty));
404

405
    host_q
406
        .handle_desc(0, &irq_sender, copy_to_writer(&mut writer))
407
        .unwrap();
408
    assert_eq!(irq_rx.try_recv(), Ok(0));
409

410
    guest_q.add_desc(&[(addr_3, 12)], &[]);
411

412
    // will hit ErrorKind::Interrupted
413
    assert_matches!(
414
        host_q.handle_desc(0, &irq_sender, copy_to_writer(&mut writer)),
415
        Err(Error::System { error, .. }) if error.kind() == ErrorKind::Interrupted
416
    );
417

418
    host_q
419
        .handle_desc(0, &irq_sender, copy_to_writer(&mut writer))
420
        .unwrap();
421
    assert_eq!(irq_rx.try_recv(), Err(TryRecvError::Empty));
422

423
    for (buf, s) in [(buf_0, str_0), (buf_1, str_1), (buf_2, str_2)] {
424
        assert_eq!(String::from_utf8_lossy(buf.as_slice()), s)
425
    }
426
}
427

428
#[test]
429
fn test_written_bytes() {
430
    let str_0 = "Hello, World!";
431
    let str_1 = "Goodbye, World!";
432

433
    let mut buf = vec![0u8; str_0.len()];
434
    let mut chain = DescChain {
435
        id: 0,
436
        delta: 1,
437
        readable: vec![],
438
        writable: vec![IoSliceMut::new(buf.as_mut_slice())],
439
    };
440
    let reader = str_0.as_bytes();
441
    assert_matches!(
442
        copy_from_reader(reader)(&mut chain),
443
        Ok(Status::Done { len: 13 })
444
    );
445
    assert_eq!(buf.as_slice(), str_0.as_bytes());
446

447
    let mut buf = vec![];
448
    let mut chain = DescChain {
449
        id: 1,
450
        delta: 1,
451
        readable: vec![IoSlice::new(str_1.as_bytes())],
452
        writable: vec![],
453
    };
454
    assert_matches!(
455
        copy_to_writer(&mut buf)(&mut chain),
456
        Ok(Status::Done { len: 0 })
457
    );
458
    assert_eq!(buf.as_slice(), str_1.as_bytes());
459
}
460

461
#[rstest]
462
fn test_handle_deferred(fixture_ram_bus: RamBus, fixture_queues: Box<[QueueReg]>) {
463
    let ram = fixture_ram_bus.lock_layout();
464
    let reg = &fixture_queues[0];
465
    let mut host_q = Queue::new(
466
        SplitQueue::new(reg, &ram, false).unwrap().unwrap(),
467
        reg,
468
        &ram,
469
    );
470
    let mut guest_q = GuestQueue::new(SplitQueue::new(reg, &ram, false).unwrap().unwrap(), reg);
471
    let (irq_tx, irq_rx) = mpsc::channel();
472
    let irq_sender = FakeIrqSender { q_tx: irq_tx };
473

474
    let str_0 = "Hello, World!";
475
    let str_1 = "Goodbye, World!";
476
    let str_2 = "Bose-Einstein condensate";
477
    let addr_0 = DATA_ADDR;
478
    let addr_1 = addr_0 + str_0.len() as u64;
479
    let addr_2 = addr_1 + str_1.len() as u64;
480
    for (s, addr) in [(str_0, addr_0), (str_1, addr_1), (str_2, addr_2)] {
481
        ram.write(addr, s.as_bytes()).unwrap();
482
    }
483

484
    guest_q.add_desc(
485
        &[(addr_0, str_0.len() as u32), (addr_1, str_1.len() as u32)],
486
        &[],
487
    );
488
    guest_q.add_desc(&[(addr_2, str_2.len() as u32)], &[]);
489

490
    let mut ids = vec![];
491
    host_q
492
        .handle_desc(0, &irq_sender, |chain| {
493
            ids.push(chain.id());
494
            Ok(Status::Deferred)
495
        })
496
        .unwrap();
497

498
    assert_eq!(irq_rx.try_recv(), Err(TryRecvError::Empty));
499
    assert_eq!(ids, [0, 2]);
500

501
    host_q
502
        .handle_deferred(0, 0, &irq_sender, |chain| {
503
            assert_eq!(chain.id, 0);
504
            assert_eq!(&*chain.readable[0], str_0.as_bytes());
505
            assert_eq!(&*chain.readable[1], str_1.as_bytes());
506
            assert_eq!(chain.writable.len(), 0);
507
            Ok(0)
508
        })
509
        .unwrap();
510

511
    assert_matches!(
512
        host_q.handle_deferred(1, 0, &irq_sender, |_| Ok(0)),
513
        Err(Error::InvalidDescriptor { id: 1, .. })
514
    );
515

516
    host_q
517
        .handle_deferred(2, 0, &irq_sender, |chain| {
518
            assert_eq!(chain.id, 2);
519
            assert_eq!(&*chain.readable[0], str_2.as_bytes());
520
            assert_eq!(chain.writable.len(), 0);
521
            Ok(0)
522
        })
523
        .unwrap();
524
}
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

© 2025 Coveralls, Inc