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

google / alioth / 16871256879

11 Aug 2025 04:57AM UTC coverage: 10.366% (+1.6%) from 8.754%
16871256879

push

github

Lencerf
test(virtio): inject errors in queue handler tests

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

30 of 56 new or added lines in 1 file covered. (53.57%)

26 existing lines in 5 files now uncovered.

710 of 6849 relevant lines covered (10.37%)

16.03 hits per line

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

87.69
/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, Queue, 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();
4✔
40
    ram_bus
3✔
41
}
42

43
#[fixture]
44
pub fn fixture_queue() -> Queue {
45
    Queue {
46
        size: AtomicU16::new(QUEUE_SIZE),
3✔
47
        desc: AtomicU64::new(DESC_ADDR),
3✔
48
        driver: AtomicU64::new(AVAIL_ADDR),
3✔
49
        device: AtomicU64::new(USED_ADDR),
3✔
50
        enabled: AtomicBool::new(true),
3✔
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> {
11✔
73
        let mut count = 0;
21✔
74
        let mut buf_iter = bufs.iter_mut();
31✔
75
        let Some(s) = buf_iter.next() else {
21✔
NEW
76
            return Ok(0);
×
77
        };
NEW
78
        let mut buf = s.as_mut();
1✔
NEW
79
        loop {
×
80
            let Some(data) = self.data.get(self.index) else {
35✔
81
                break;
2✔
82
            };
NEW
83
            match data {
1✔
84
                ReaderData::Buf(data) => {
7✔
NEW
85
                    let c = buf.write(&data[self.pos..]).unwrap();
1✔
NEW
86
                    self.pos += c;
1✔
87
                    if self.pos == data.len() {
8✔
88
                        self.index += 1;
7✔
89
                        self.pos = 0;
7✔
90
                    }
NEW
91
                    count += c;
2✔
NEW
92
                    if buf.len() == 0 {
2✔
93
                        let Some(s) = buf_iter.next() else {
9✔
94
                            break;
4✔
95
                        };
NEW
96
                        buf = s.as_mut();
1✔
97
                    }
98
                }
99
                ReaderData::Err(kind) => {
5✔
100
                    if count > 0 {
5✔
NEW
101
                        break;
×
102
                    }
NEW
103
                    self.index += 1;
1✔
NEW
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: Queue) {
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
    q.add_desc(
145
        0,
146
        &[],
147
        &[(addr_0, str_0.len() as u32), (addr_1, str_1.len() as u32)],
148
    );
149
    q.copy_from_reader(0, &irq_sender, &mut reader).unwrap();
150
    assert_eq!(irq_rx.try_recv(), Ok(0));
151

152
    // no writable descriptors
153
    q.copy_from_reader(0, &irq_sender, &mut reader).unwrap();
154
    assert_eq!(irq_rx.try_recv(), Err(TryRecvError::Empty));
155

156
    q.add_desc(2, &[], &[(addr_2, str_2.len() as u32)]);
157
    // will hit ErrorKind::WouldBlock
158
    q.copy_from_reader(0, &irq_sender, &mut reader).unwrap();
159
    assert_eq!(irq_rx.try_recv(), Err(TryRecvError::Empty));
160

161
    q.copy_from_reader(0, &irq_sender, &mut reader).unwrap();
162
    assert_eq!(irq_rx.try_recv(), Ok(0));
163

164
    q.add_desc(3, &[], &[(addr_3, 12)]);
165

166
    // will hit ErrorKind::Interrupted
167
    assert_matches!(
168
        q.copy_from_reader(0, &irq_sender, &mut reader),
169
        Err(Error::System { error, .. }) if error.kind() == ErrorKind::Interrupted
170
    );
171

172
    assert_matches!(
173
        q.copy_from_reader(0, &irq_sender, &mut reader),
174
        Err(Error::System { error, .. }) if error.kind() == ErrorKind::UnexpectedEof
175
    );
176
    assert_eq!(irq_rx.try_recv(), Err(TryRecvError::Empty));
177

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

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

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

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

NEW
203
    fn flush(&mut self) -> std::io::Result<()> {
×
NEW
204
        Ok(())
×
205
    }
206

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

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

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

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

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

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

285
    q.add_desc(
286
        0,
287
        &[(addr_0, str_0.len() as u32), (addr_1, str_1.len() as u32)],
288
        &[],
289
    );
290
    q.copy_to_writer(0, &irq_sender, &mut writer).unwrap();
291
    assert_eq!(irq_rx.try_recv(), Ok(0));
292

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

297
    q.add_desc(2, &[(addr_2, str_2.len() as u32)], &[]);
298
    // will hit ErrorKind::WouldBlock
299
    q.copy_to_writer(0, &irq_sender, &mut writer).unwrap();
300
    assert_eq!(irq_rx.try_recv(), Err(TryRecvError::Empty));
301

302
    q.copy_to_writer(0, &irq_sender, &mut writer).unwrap();
303
    assert_eq!(irq_rx.try_recv(), Ok(0));
304

305
    q.add_desc(3, &[(addr_3, 12)], &[]);
306

307
    // will hit ErrorKind::Interrupted
308
    assert_matches!(
309
        q.copy_to_writer(0, &irq_sender, &mut writer),
310
        Err(Error::System { error, .. }) if error.kind() == ErrorKind::Interrupted
311
    );
312

313
    assert_matches!(
314
        q.copy_to_writer(0, &irq_sender, &mut writer),
315
        Err(Error::System { error, .. }) if error.kind() == ErrorKind::WriteZero
316
    );
317
    assert_eq!(irq_rx.try_recv(), Err(TryRecvError::Empty));
318

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