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

google / alioth / 17016642693

17 Aug 2025 04:19AM UTC coverage: 10.406% (-0.01%) from 10.419%
17016642693

Pull #273

github

web-flow
Merge ef274b67c into 3c18ccbb6
Pull Request #273: feat: virtio packed queue

50 of 111 new or added lines in 8 files covered. (45.05%)

97 existing lines in 5 files now uncovered.

713 of 6852 relevant lines covered (10.41%)

16.14 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::{QUEUE_SIZE_MAX, QueueReg, VirtQueue};
26
use crate::virtio::tests::FakeIrqSender;
27

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

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

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

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

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

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

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

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

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

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

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

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

144
    // empty writable descripter
145
    q.add_desc(0, &[], &[(addr_0, 0)]);
146
    q.copy_from_reader(0, &irq_sender, &mut reader).unwrap();
147
    assert_eq!(irq_rx.try_recv(), Ok(0));
148

149
    q.add_desc(
150
        0,
151
        &[],
152
        &[(addr_0, str_0.len() as u32), (addr_1, str_1.len() as u32)],
153
    );
154
    q.copy_from_reader(0, &irq_sender, &mut reader).unwrap();
155
    assert_eq!(irq_rx.try_recv(), Ok(0));
156

157
    // no writable descriptors
158
    q.copy_from_reader(0, &irq_sender, &mut reader).unwrap();
159
    assert_eq!(irq_rx.try_recv(), Err(TryRecvError::Empty));
160

161
    q.add_desc(2, &[], &[(addr_2, str_2.len() as u32)]);
162
    // will hit ErrorKind::WouldBlock
163
    q.copy_from_reader(0, &irq_sender, &mut reader).unwrap();
164
    assert_eq!(irq_rx.try_recv(), Err(TryRecvError::Empty));
165

166
    q.copy_from_reader(0, &irq_sender, &mut reader).unwrap();
167
    assert_eq!(irq_rx.try_recv(), Ok(0));
168

169
    q.add_desc(3, &[], &[(addr_3, 12)]);
170

171
    // will hit ErrorKind::Interrupted
172
    assert_matches!(
173
        q.copy_from_reader(0, &irq_sender, &mut reader),
174
        Err(Error::System { error, .. }) if error.kind() == ErrorKind::Interrupted
175
    );
176

177
    q.copy_from_reader(0, &irq_sender, &mut reader).unwrap();
178
    assert_eq!(irq_rx.try_recv(), Err(TryRecvError::Empty));
179

180
    for (s, addr) in [(str_0, addr_0), (str_1, addr_1), (str_2, addr_2)] {
181
        let mut buf = vec![0u8; s.len()];
182
        ram.read(addr, &mut buf).unwrap();
183
        assert_eq!(String::from_utf8_lossy(buf.as_slice()), s);
184
    }
185
}
186

187
#[derive(Debug)]
188
enum WriterData<'a> {
189
    Buf(&'a mut [u8]),
190
    Err(ErrorKind),
191
}
192

193
#[derive(Debug)]
194
struct Writer<'a> {
195
    data: &'a mut [WriterData<'a>],
196
    index: usize,
197
    pos: usize,
198
}
199

200
impl<'a> Write for Writer<'a> {
201
    fn write(&mut self, _: &[u8]) -> std::io::Result<usize> {
202
        unreachable!()
203
    }
204

UNCOV
205
    fn flush(&mut self) -> std::io::Result<()> {
×
UNCOV
206
        Ok(())
×
207
    }
208

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

249
#[rstest]
250
fn test_copy_to_writer(fixture_ram_bus: RamBus, fixture_queue: QueueReg) {
251
    let ram = fixture_ram_bus.lock_layout();
252
    let mut q = SplitQueue::new(&fixture_queue, &*ram, 0).unwrap().unwrap();
253

254
    let (irq_tx, irq_rx) = mpsc::channel();
255
    let irq_sender = FakeIrqSender { q_tx: irq_tx };
256

257
    let str_0 = "Hello, World!";
258
    let str_1 = "Goodbye, World!";
259
    let str_2 = "Bose-Einstein condensate";
260
    let addr_0 = DATA_ADDR;
261
    let addr_1 = addr_0 + str_0.len() as u64;
262
    let addr_2 = addr_1 + str_1.len() as u64;
263
    let addr_3 = addr_2 + str_2.len() as u64;
264
    for (s, addr) in [(str_0, addr_0), (str_1, addr_1), (str_2, addr_2)] {
265
        ram.write(addr, s.as_bytes()).unwrap();
266
    }
267

268
    let mut buf_0 = vec![0u8; str_0.len()];
269
    let mut buf_1 = vec![0u8; str_1.len()];
270
    let mut buf_2 = vec![0u8; str_2.len()];
271
    let mut writer = Writer {
272
        data: &mut [
273
            WriterData::Buf(buf_0.as_mut_slice()),
274
            WriterData::Buf(buf_1.as_mut_slice()),
275
            WriterData::Err(ErrorKind::WouldBlock),
276
            WriterData::Buf(buf_2.as_mut_slice()),
277
            WriterData::Err(ErrorKind::Interrupted),
278
        ],
279
        pos: 0,
280
        index: 0,
281
    };
282

283
    // no readable descriptors
284
    q.copy_to_writer(0, &irq_sender, &mut writer).unwrap();
285
    assert_eq!(irq_rx.try_recv(), Err(TryRecvError::Empty));
286

287
    // empty readble descripter
288
    q.add_desc(0, &[(addr_0, 0)], &[]);
289
    q.copy_to_writer(0, &irq_sender, &mut writer).unwrap();
290
    assert_eq!(irq_rx.try_recv(), Ok(0));
291

292
    q.add_desc(
293
        0,
294
        &[(addr_0, str_0.len() as u32), (addr_1, str_1.len() as u32)],
295
        &[],
296
    );
297
    q.copy_to_writer(0, &irq_sender, &mut writer).unwrap();
298
    assert_eq!(irq_rx.try_recv(), Ok(0));
299

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

304
    q.add_desc(2, &[(addr_2, str_2.len() as u32)], &[]);
305
    // will hit ErrorKind::WouldBlock
306
    q.copy_to_writer(0, &irq_sender, &mut writer).unwrap();
307
    assert_eq!(irq_rx.try_recv(), Err(TryRecvError::Empty));
308

309
    q.copy_to_writer(0, &irq_sender, &mut writer).unwrap();
310
    assert_eq!(irq_rx.try_recv(), Ok(0));
311

312
    q.add_desc(3, &[(addr_3, 12)], &[]);
313

314
    // will hit ErrorKind::Interrupted
315
    assert_matches!(
316
        q.copy_to_writer(0, &irq_sender, &mut writer),
317
        Err(Error::System { error, .. }) if error.kind() == ErrorKind::Interrupted
318
    );
319

320
    q.copy_to_writer(0, &irq_sender, &mut writer).unwrap();
321
    assert_eq!(irq_rx.try_recv(), Err(TryRecvError::Empty));
322

323
    for (buf, s) in [(buf_0, str_0), (buf_1, str_1), (buf_2, str_2)] {
324
        assert_eq!(String::from_utf8_lossy(buf.as_slice()), s)
325
    }
326
}
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