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

google / alioth / 17185429117

24 Aug 2025 06:43AM UTC coverage: 13.709% (-0.2%) from 13.887%
17185429117

Pull #277

github

web-flow
Merge c83084111 into 861f19073
Pull Request #277: feat: Unix domain socket based vsock device

110 of 554 new or added lines in 18 files covered. (19.86%)

4 existing lines in 4 files now uncovered.

1019 of 7433 relevant lines covered (13.71%)

17.31 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_queue, 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 {
14✔
68
        let size = reg.size.load(Ordering::Acquire);
52✔
69
        Self {
70
            q,
71
            avail: Q::INIT_INDEX,
72
            used: Q::INIT_INDEX,
73
            ids: vec![false; size as usize],
26✔
74
            chains: HashMap::new(),
14✔
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 {
34✔
85
        let mut ids = vec![];
66✔
86
        let total = readable.len() + writable.len();
130✔
87
        for _ in 0..self.ids.len() {
68✔
88
            if !self.ids[self.next_id as usize] {
90✔
89
                ids.push(self.next_id);
44✔
90
                self.ids[self.next_id as usize] = true;
44✔
91
            }
92
            self.next_id = self.next_id.wrapping_add(1) % self.ids.len() as u16;
130✔
93
            if ids.len() == total {
88✔
94
                break;
32✔
95
            }
96
        }
97
        assert_eq!(ids.len(), total);
100✔
98
        let delta = self.q.add_desc(self.avail, &ids, readable, writable);
226✔
99
        self.avail = self.q.index_add(self.avail, delta);
99✔
100
        let head_id = ids[0];
67✔
101
        self.chains.insert(head_id, ids);
131✔
102
        head_id
32✔
103
    }
104

105
    pub fn get_used(&mut self) -> Option<UsedDesc> {
14✔
106
        let used = self.q.get_used(self.used, &self.chains)?;
62✔
NEW
107
        let ids = self.chains.remove(&used.id).unwrap();
2✔
108
        for id in ids {
50✔
NEW
109
            self.ids[id as usize] = false;
4✔
110
        }
NEW
111
        self.used = self.q.index_add(self.used, used.delta);
2✔
NEW
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_queue: QueueReg) {
176
    let ram = fixture_ram_bus.lock_layout();
177
    let mut host_q = Queue::new(
178
        SplitQueue::new(&fixture_queue, &*ram, false)
179
            .unwrap()
180
            .unwrap(),
181
        &fixture_queue,
182
        &ram,
183
    );
184
    let mut guest_q = GuestQueue::new(
185
        SplitQueue::new(&fixture_queue, &*ram, false)
186
            .unwrap()
187
            .unwrap(),
188
        &fixture_queue,
189
    );
190
    assert!(ptr_eq(host_q.reg(), &fixture_queue));
191

192
    let (irq_tx, irq_rx) = mpsc::channel();
193
    let irq_sender = FakeIrqSender { q_tx: irq_tx };
194

195
    let str_0 = "Hello, World!";
196
    let str_1 = "Goodbye, World!";
197
    let str_2 = "Bose-Einstein condensate";
198
    let addr_0 = DATA_ADDR;
199
    let addr_1 = addr_0 + str_0.len() as u64;
200
    let addr_2 = addr_1 + str_1.len() as u64;
201
    let addr_3 = addr_2 + str_2.len() as u64;
202

203
    let mut reader = Reader {
204
        data: &[
205
            ReaderData::Buf(str_0.as_bytes()),
206
            ReaderData::Buf(str_1.as_bytes()),
207
            ReaderData::Err(ErrorKind::WouldBlock),
208
            ReaderData::Buf(str_2.as_bytes()),
209
            ReaderData::Err(ErrorKind::Interrupted),
210
        ],
211
        pos: 0,
212
        index: 0,
213
    };
214

215
    // no writable descriptors
216
    host_q
217
        .handle_desc(0, &irq_sender, copy_from_reader(&mut reader))
218
        .unwrap();
219
    assert_eq!(irq_rx.try_recv(), Err(TryRecvError::Empty));
220

221
    // empty writable descripter
222
    guest_q.add_desc(&[], &[(addr_0, 0)]);
223
    host_q
224
        .handle_desc(0, &irq_sender, copy_from_reader(&mut reader))
225
        .unwrap();
226
    assert_eq!(irq_rx.try_recv(), Ok(0));
227

228
    guest_q.add_desc(
229
        &[],
230
        &[(addr_0, str_0.len() as u32), (addr_1, str_1.len() as u32)],
231
    );
232
    host_q
233
        .handle_desc(0, &irq_sender, copy_from_reader(&mut reader))
234
        .unwrap();
235
    assert_eq!(irq_rx.try_recv(), Ok(0));
236

237
    // no writable descriptors
238
    host_q
239
        .handle_desc(0, &irq_sender, copy_from_reader(&mut reader))
240
        .unwrap();
241
    assert_eq!(irq_rx.try_recv(), Err(TryRecvError::Empty));
242

243
    guest_q.add_desc(&[], &[(addr_2, str_2.len() as u32)]);
244
    // will hit ErrorKind::WouldBlock
245
    host_q
246
        .handle_desc(0, &irq_sender, copy_from_reader(&mut reader))
247
        .unwrap();
248
    assert_eq!(irq_rx.try_recv(), Err(TryRecvError::Empty));
249

250
    host_q
251
        .handle_desc(0, &irq_sender, copy_from_reader(&mut reader))
252
        .unwrap();
253
    assert_eq!(irq_rx.try_recv(), Ok(0));
254

255
    guest_q.add_desc(&[], &[(addr_3, 12)]);
256

257
    // will hit ErrorKind::Interrupted
258
    assert_matches!(
259
        host_q.handle_desc(0, &irq_sender, copy_from_reader(&mut reader)),
260
        Err(Error::System { error, .. }) if error.kind() == ErrorKind::Interrupted
261
    );
262

263
    host_q
264
        .handle_desc(0, &irq_sender, copy_from_reader(&mut reader))
265
        .unwrap();
266
    assert_eq!(irq_rx.try_recv(), Err(TryRecvError::Empty));
267

268
    for (s, addr) in [(str_0, addr_0), (str_1, addr_1), (str_2, addr_2)] {
269
        let mut buf = vec![0u8; s.len()];
270
        ram.read(addr, &mut buf).unwrap();
271
        assert_eq!(String::from_utf8_lossy(buf.as_slice()), s);
272
    }
273
}
274

275
#[derive(Debug)]
276
enum WriterData<'a> {
277
    Buf(&'a mut [u8]),
278
    Err(ErrorKind),
279
}
280

281
#[derive(Debug)]
282
struct Writer<'a> {
283
    data: &'a mut [WriterData<'a>],
284
    index: usize,
285
    pos: usize,
286
}
287

288
impl<'a> Write for Writer<'a> {
289
    fn write(&mut self, _: &[u8]) -> std::io::Result<usize> {
290
        unreachable!()
291
    }
292

293
    fn flush(&mut self) -> std::io::Result<()> {
×
294
        Ok(())
×
295
    }
296

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

337
#[rstest]
338
fn test_copy_to_writer(fixture_ram_bus: RamBus, fixture_queue: QueueReg) {
339
    let ram = fixture_ram_bus.lock_layout();
340
    let mut host_q = Queue::new(
341
        SplitQueue::new(&fixture_queue, &*ram, false)
342
            .unwrap()
343
            .unwrap(),
344
        &fixture_queue,
345
        &ram,
346
    );
347
    let mut guest_q = GuestQueue::new(
348
        SplitQueue::new(&fixture_queue, &*ram, false)
349
            .unwrap()
350
            .unwrap(),
351
        &fixture_queue,
352
    );
353
    let (irq_tx, irq_rx) = mpsc::channel();
354
    let irq_sender = FakeIrqSender { q_tx: irq_tx };
355

356
    let str_0 = "Hello, World!";
357
    let str_1 = "Goodbye, World!";
358
    let str_2 = "Bose-Einstein condensate";
359
    let addr_0 = DATA_ADDR;
360
    let addr_1 = addr_0 + str_0.len() as u64;
361
    let addr_2 = addr_1 + str_1.len() as u64;
362
    let addr_3 = addr_2 + str_2.len() as u64;
363
    for (s, addr) in [(str_0, addr_0), (str_1, addr_1), (str_2, addr_2)] {
364
        ram.write(addr, s.as_bytes()).unwrap();
365
    }
366

367
    let mut buf_0 = vec![0u8; str_0.len()];
368
    let mut buf_1 = vec![0u8; str_1.len()];
369
    let mut buf_2 = vec![0u8; str_2.len()];
370
    let mut writer = Writer {
371
        data: &mut [
372
            WriterData::Buf(buf_0.as_mut_slice()),
373
            WriterData::Buf(buf_1.as_mut_slice()),
374
            WriterData::Err(ErrorKind::WouldBlock),
375
            WriterData::Buf(buf_2.as_mut_slice()),
376
            WriterData::Err(ErrorKind::Interrupted),
377
        ],
378
        pos: 0,
379
        index: 0,
380
    };
381

382
    // no readable descriptors
383
    host_q
384
        .handle_desc(0, &irq_sender, copy_to_writer(&mut writer))
385
        .unwrap();
386
    assert_eq!(irq_rx.try_recv(), Err(TryRecvError::Empty));
387

388
    // empty readble descripter
389
    guest_q.add_desc(&[(addr_0, 0)], &[]);
390
    host_q
391
        .handle_desc(0, &irq_sender, copy_to_writer(&mut writer))
392
        .unwrap();
393
    assert_eq!(irq_rx.try_recv(), Ok(0));
394

395
    guest_q.add_desc(
396
        &[(addr_0, str_0.len() as u32), (addr_1, str_1.len() as u32)],
397
        &[],
398
    );
399
    host_q
400
        .handle_desc(0, &irq_sender, copy_to_writer(&mut writer))
401
        .unwrap();
402
    assert_eq!(irq_rx.try_recv(), Ok(0));
403

404
    // no readable descriptors
405
    host_q
406
        .handle_desc(0, &irq_sender, copy_to_writer(&mut writer))
407
        .unwrap();
408
    assert_eq!(irq_rx.try_recv(), Err(TryRecvError::Empty));
409

410
    guest_q.add_desc(&[(addr_2, str_2.len() as u32)], &[]);
411
    // will hit ErrorKind::WouldBlock
412
    host_q
413
        .handle_desc(0, &irq_sender, copy_to_writer(&mut writer))
414
        .unwrap();
415
    assert_eq!(irq_rx.try_recv(), Err(TryRecvError::Empty));
416

417
    host_q
418
        .handle_desc(0, &irq_sender, copy_to_writer(&mut writer))
419
        .unwrap();
420
    assert_eq!(irq_rx.try_recv(), Ok(0));
421

422
    guest_q.add_desc(&[(addr_3, 12)], &[]);
423

424
    // will hit ErrorKind::Interrupted
425
    assert_matches!(
426
        host_q.handle_desc(0, &irq_sender, copy_to_writer(&mut writer)),
427
        Err(Error::System { error, .. }) if error.kind() == ErrorKind::Interrupted
428
    );
429

430
    host_q
431
        .handle_desc(0, &irq_sender, copy_to_writer(&mut writer))
432
        .unwrap();
433
    assert_eq!(irq_rx.try_recv(), Err(TryRecvError::Empty));
434

435
    for (buf, s) in [(buf_0, str_0), (buf_1, str_1), (buf_2, str_2)] {
436
        assert_eq!(String::from_utf8_lossy(buf.as_slice()), s)
437
    }
438
}
439

440
#[test]
441
fn test_written_bytes() {
442
    let str_0 = "Hello, World!";
443
    let str_1 = "Goodbye, World!";
444

445
    let mut buf = vec![0u8; str_0.len()];
446
    let mut chain = DescChain {
447
        id: 0,
448
        delta: 1,
449
        readable: vec![],
450
        writable: vec![IoSliceMut::new(buf.as_mut_slice())],
451
    };
452
    let reader = str_0.as_bytes();
453
    assert_matches!(
454
        copy_from_reader(reader)(&mut chain),
455
        Ok(Status::Done { len: 13 })
456
    );
457
    assert_eq!(buf.as_slice(), str_0.as_bytes());
458

459
    let mut buf = vec![];
460
    let mut chain = DescChain {
461
        id: 1,
462
        delta: 1,
463
        readable: vec![IoSlice::new(str_1.as_bytes())],
464
        writable: vec![],
465
    };
466
    assert_matches!(
467
        copy_to_writer(&mut buf)(&mut chain),
468
        Ok(Status::Done { len: 0 })
469
    );
470
    assert_eq!(buf.as_slice(), str_1.as_bytes());
471
}
472

473
#[rstest]
474
fn test_handle_deferred(fixture_ram_bus: RamBus, fixture_queue: QueueReg) {
475
    let ram = fixture_ram_bus.lock_layout();
476
    let mut host_q = Queue::new(
477
        SplitQueue::new(&fixture_queue, &ram, false)
478
            .unwrap()
479
            .unwrap(),
480
        &fixture_queue,
481
        &ram,
482
    );
483
    let mut guest_q = GuestQueue::new(
484
        SplitQueue::new(&fixture_queue, &ram, false)
485
            .unwrap()
486
            .unwrap(),
487
        &fixture_queue,
488
    );
489
    let (irq_tx, irq_rx) = mpsc::channel();
490
    let irq_sender = FakeIrqSender { q_tx: irq_tx };
491

492
    let str_0 = "Hello, World!";
493
    let str_1 = "Goodbye, World!";
494
    let str_2 = "Bose-Einstein condensate";
495
    let addr_0 = DATA_ADDR;
496
    let addr_1 = addr_0 + str_0.len() as u64;
497
    let addr_2 = addr_1 + str_1.len() as u64;
498
    for (s, addr) in [(str_0, addr_0), (str_1, addr_1), (str_2, addr_2)] {
499
        ram.write(addr, s.as_bytes()).unwrap();
500
    }
501

502
    guest_q.add_desc(
503
        &[(addr_0, str_0.len() as u32), (addr_1, str_1.len() as u32)],
504
        &[],
505
    );
506
    guest_q.add_desc(&[(addr_2, str_2.len() as u32)], &[]);
507

508
    let mut ids = vec![];
509
    host_q
510
        .handle_desc(0, &irq_sender, |chain| {
511
            ids.push(chain.id());
512
            Ok(Status::Deferred)
513
        })
514
        .unwrap();
515

516
    assert_eq!(irq_rx.try_recv(), Err(TryRecvError::Empty));
517
    assert_eq!(ids, [0, 2]);
518

519
    host_q
520
        .handle_deferred(0, 0, &irq_sender, |chain| {
521
            assert_eq!(chain.id, 0);
522
            assert_eq!(&*chain.readable[0], str_0.as_bytes());
523
            assert_eq!(&*chain.readable[1], str_1.as_bytes());
524
            assert_eq!(chain.writable.len(), 0);
525
            Ok(0)
526
        })
527
        .unwrap();
528

529
    assert_matches!(
530
        host_q.handle_deferred(1, 0, &irq_sender, |_| Ok(0)),
531
        Err(Error::InvalidDescriptor { id: 1, .. })
532
    );
533

534
    host_q
535
        .handle_deferred(2, 0, &irq_sender, |chain| {
536
            assert_eq!(chain.id, 2);
537
            assert_eq!(&*chain.readable[0], str_2.as_bytes());
538
            assert_eq!(chain.writable.len(), 0);
539
            Ok(0)
540
        })
541
        .unwrap();
542
}
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