Skip to content

Commit 1de6d02

Browse files
Gaelanvireshk
authored andcommitted
scsi: Add tests for the emulated target
The vast majority of this work was done by Gaelan Steele as part of a GSoC project [1][2]. [1] #4 [2] https://gist.github.com/Gaelan/febec4e4606e1320026a0924c3bf74d0 Co-developed-by: Erik Schilling <[email protected]> Signed-off-by: Erik Schilling <[email protected]> Signed-off-by: Gaelan Steele <[email protected]>
1 parent be1eaf3 commit 1de6d02

File tree

6 files changed

+1112
-1
lines changed

6 files changed

+1112
-1
lines changed

coverage_config_x86_64.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"coverage_score": 67.6,
2+
"coverage_score": 69.6,
33
"exclude_path": "",
44
"crate_features": ""
55
}

crates/scsi/src/scsi/emulation/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,5 @@ pub(crate) mod mode_page;
77
mod response_data;
88
pub(crate) mod target;
99

10+
#[cfg(test)]
11+
mod tests;
Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
// SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause
2+
3+
use super::{do_command_fail_lun, do_command_in_lun, null_image};
4+
use crate::scsi::{
5+
emulation::{block_device::BlockDevice, target::EmulatedTarget},
6+
sense,
7+
};
8+
9+
#[test]
10+
fn test_report_luns() {
11+
let mut target = EmulatedTarget::new();
12+
for _ in 0..5 {
13+
let dev = BlockDevice::new(null_image());
14+
target.add_lun(Box::new(dev));
15+
}
16+
17+
let select_reports = &[0x0, 0x2]; // all but well known, all
18+
19+
for &sr in select_reports {
20+
do_command_in_lun(
21+
&mut target,
22+
6,
23+
&[
24+
0xa0, // REPORT LUNS
25+
0, // reserved
26+
sr, // select report
27+
0, 0, 0, // reserved
28+
0, 0, 1, 0, // alloc length: 256
29+
0, 0,
30+
],
31+
&[],
32+
&[
33+
0, 0, 0, 40, // length: 5*8 = 40
34+
0, 0, 0, 0, // reserved
35+
0, 0, 0, 0, 0, 0, 0, 0, // LUN 0
36+
0, 1, 0, 0, 0, 0, 0, 0, // LUN 1
37+
0, 2, 0, 0, 0, 0, 0, 0, // LUN 2
38+
0, 3, 0, 0, 0, 0, 0, 0, // LUN 3
39+
0, 4, 0, 0, 0, 0, 0, 0, // LUN 4
40+
],
41+
);
42+
}
43+
}
44+
45+
#[test]
46+
fn test_report_luns_empty() {
47+
let mut target = EmulatedTarget::new();
48+
for _ in 0..5 {
49+
let dev = BlockDevice::new(null_image());
50+
target.add_lun(Box::new(dev));
51+
}
52+
53+
// well-known only and several modes explictly defined to return an empty list
54+
// for all but ceratin types of recieving LUNs
55+
let select_reports = &[0x1, 0x10, 0x11, 0x12];
56+
57+
for &sr in select_reports {
58+
do_command_in_lun(
59+
&mut target,
60+
6,
61+
&[
62+
0xa0, // REPORT LUNS
63+
0, // reserved
64+
sr, // select report
65+
0, 0, 0, // reserved
66+
0, 0, 1, 0, // alloc length: 256
67+
0, 0,
68+
],
69+
&[],
70+
&[
71+
0, 0, 0, 0, // length: 0
72+
0, 0, 0, 0, // reserved
73+
],
74+
);
75+
}
76+
}
77+
78+
#[test]
79+
fn test_request_sense() {
80+
let mut target = EmulatedTarget::new();
81+
let dev = BlockDevice::new(null_image());
82+
target.add_lun(Box::new(dev));
83+
84+
do_command_in_lun(
85+
&mut target,
86+
1,
87+
&[
88+
0x3, // REQUEST SENSE
89+
0, // fixed format sense data
90+
0, 0, // reserved
91+
255, // alloc length
92+
0, // control
93+
],
94+
&[],
95+
&sense::LOGICAL_UNIT_NOT_SUPPORTED.to_fixed_sense(),
96+
);
97+
}
98+
99+
#[test]
100+
fn test_request_sense_descriptor_format() {
101+
let mut target = EmulatedTarget::new();
102+
let dev = BlockDevice::new(null_image());
103+
target.add_lun(Box::new(dev));
104+
105+
do_command_fail_lun(
106+
&mut target,
107+
1,
108+
&[
109+
0x3, // REQUEST SENSE
110+
1, // descriptor format sense data
111+
0, 0, // reserved
112+
255, // alloc length
113+
0, // control
114+
],
115+
sense::INVALID_FIELD_IN_CDB,
116+
);
117+
}
118+
119+
#[test]
120+
fn test_inquiry() {
121+
let mut target = EmulatedTarget::new();
122+
let dev = BlockDevice::new(null_image());
123+
target.add_lun(Box::new(dev));
124+
125+
do_command_in_lun(
126+
&mut target,
127+
1,
128+
&[
129+
0x12, // INQUIRY
130+
0, // EVPD bit: 0
131+
0, // page code
132+
1, 0, // alloc length: 256
133+
0, // control
134+
],
135+
&[],
136+
// some empty comments to get rustfmt to do something vaguely sensible
137+
&[
138+
0x7f, // device not accessible, unknown type
139+
0, // features
140+
0x7, // version
141+
0x12, // response data format v2, HiSup = 1
142+
91, // addl length
143+
0, 0, 0, // unsupported features
144+
// vendor
145+
b'r', b'u', b's', b't', b'-', b'v', b'm', b'm', //
146+
// product
147+
b'v', b'h', b'o', b's', b't', b'-', b'u', b's', b'e', b'r', b'-', b's', b'c', b's',
148+
b'i', b' ', //
149+
// revision
150+
b'v', b'0', b' ', b' ', //
151+
// reserved/obselete/vendor specific
152+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
153+
// version descriptors
154+
0x0, 0xc0, // SAM-6
155+
0x05, 0xc0, // SPC-5 (no code assigned for 6 yet)
156+
0x06, 0x0, // SBC-4
157+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //
158+
// reserved
159+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
160+
],
161+
);
162+
}
163+
164+
#[test]
165+
fn test_other_command() {
166+
let mut target = EmulatedTarget::new();
167+
let dev = BlockDevice::new(null_image());
168+
target.add_lun(Box::new(dev));
169+
170+
do_command_fail_lun(
171+
&mut target,
172+
1,
173+
&[
174+
0, // TEST UNIT READY
175+
0, 0, 0, 0, // reserved
176+
0, // control
177+
],
178+
sense::LOGICAL_UNIT_NOT_SUPPORTED,
179+
);
180+
}
181+
182+
#[test]
183+
fn test_invalid_command() {
184+
let mut target = EmulatedTarget::new();
185+
let dev = BlockDevice::new(null_image());
186+
target.add_lun(Box::new(dev));
187+
188+
do_command_fail_lun(
189+
&mut target,
190+
1,
191+
&[
192+
0xff, // vendor specific
193+
0, 0, 0, 0, // reserved
194+
0, // control
195+
],
196+
sense::INVALID_COMMAND_OPERATION_CODE,
197+
);
198+
}
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
// SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause
2+
3+
//! Tests for stuff shared between commands.
4+
5+
use std::io::ErrorKind;
6+
7+
use super::{do_command_fail, test_image};
8+
use crate::scsi::{
9+
emulation::{block_device::BlockDevice, target::EmulatedTarget},
10+
sense, CmdError, Request, Target, TaskAttr,
11+
};
12+
13+
#[test]
14+
fn test_invalid_opcode() {
15+
let mut target = EmulatedTarget::new();
16+
let dev = BlockDevice::new(test_image());
17+
target.add_lun(Box::new(dev));
18+
19+
do_command_fail(
20+
&mut target,
21+
&[
22+
0xff, // vendor specific, unused by us
23+
0, 0, 0, 0, 0,
24+
],
25+
sense::INVALID_COMMAND_OPERATION_CODE,
26+
);
27+
}
28+
29+
#[test]
30+
fn test_invalid_service_action() {
31+
let mut target = EmulatedTarget::new();
32+
let dev = BlockDevice::new(test_image());
33+
target.add_lun(Box::new(dev));
34+
35+
do_command_fail(
36+
&mut target,
37+
&[
38+
0xa3, // MAINTAINANCE IN
39+
0x1f, // vendor specific, unused by us
40+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
41+
],
42+
sense::INVALID_FIELD_IN_CDB,
43+
);
44+
}
45+
46+
#[test]
47+
fn test_short_data_out_buffer() {
48+
let mut target = EmulatedTarget::new();
49+
let dev = BlockDevice::new(test_image());
50+
target.add_lun(Box::new(dev));
51+
52+
let mut data_in: &mut [u8] = &mut [];
53+
let mut data_out: &[u8] = &[0_u8; 511];
54+
55+
let res = target.execute_command(
56+
0,
57+
&mut data_out,
58+
&mut data_in,
59+
Request {
60+
id: 0,
61+
cdb: &[
62+
0x28, // READ (10)
63+
0, // flags
64+
0, 0, 0, 15, // LBA: 5
65+
0, // reserved, group #
66+
0, 1, // transfer length: 1
67+
0, // control
68+
],
69+
task_attr: TaskAttr::Simple,
70+
crn: 0,
71+
prio: 0,
72+
},
73+
);
74+
75+
if let CmdError::DataIn(e) = res.unwrap_err() {
76+
assert_eq!(e.kind(), ErrorKind::WriteZero);
77+
} else {
78+
panic!();
79+
}
80+
}
81+
82+
#[test]
83+
fn test_short_cdb() {
84+
let mut target: EmulatedTarget = EmulatedTarget::new();
85+
let dev = BlockDevice::new(test_image());
86+
target.add_lun(Box::new(dev));
87+
88+
let mut data_in: &mut [u8] = &mut [];
89+
let mut data_out: &[u8] = &[];
90+
91+
let res = target.execute_command(
92+
0,
93+
&mut data_out,
94+
&mut data_in,
95+
Request {
96+
id: 0,
97+
cdb: &[
98+
0x28, // READ (10)
99+
],
100+
task_attr: TaskAttr::Simple,
101+
crn: 0,
102+
prio: 0,
103+
},
104+
);
105+
106+
assert!(matches!(res.unwrap_err(), CmdError::CdbTooShort));
107+
}

0 commit comments

Comments
 (0)