1 // Copyright 2024 The Android Open Source Project
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 // http://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 //! typec_class_utils_tests
16
17 use std::path::PathBuf;
18 use std::{fs, io::Write};
19
20 use std::io::Cursor;
21
22 use tempfile::tempdir;
23
24 use crate::typec_class_utils::*;
25 use crate::usb_pd_utils::*;
26
27 /// Creates a temporary hierarchy of directories and files with content.
28 /// The dir_paths_files_content array param expects the following format:
29 /// * `[(dir_path, [array of (file_name, file_content)])]`
30 ///
31 /// ### Example
32 /// To create the following directory hierarchy:
33 /// ```
34 /// /port1
35 /// /port1/file1: content1
36 /// /port1/file2: content2
37 /// /port1/subport1
38 /// /port2/file1: content1
39 /// ```
40 /// The dir_paths_files_content parameter should receive the value:
41 /// ```
42 /// [
43 /// (
44 /// "port1",
45 /// &[("file1", "content1"), ("file2", "content2")],
46 /// ),
47 /// (
48 /// "port1/subport1",
49 /// &[("file1", "content1")],
50 /// )
51 /// ]
52 /// ```
create_temp_dir_with_dirs_files_content( dir_paths_files_content: &[(&str, &[(&str, &str)])], ) -> PathBuf53 fn create_temp_dir_with_dirs_files_content(
54 dir_paths_files_content: &[(&str, &[(&str, &str)])],
55 ) -> PathBuf {
56 let root_dir = tempdir().expect("Cannot create temporary directory");
57 for (dir_path, files_content) in dir_paths_files_content {
58 let full_dir_path = root_dir.path().join(dir_path);
59 println!("FULL: {:?}", &full_dir_path);
60 fs::create_dir(&full_dir_path).expect("Cannot create directory");
61
62 for (file_name, file_content) in files_content.iter() {
63 let mut file = fs::File::create(full_dir_path.join(file_name))
64 .expect("Cannot create temporary file");
65 writeln!(file, "{file_content}").expect("Could not write to temporary file");
66 }
67 }
68 root_dir.into_path()
69 }
70
71 /// Creates a hierarchy of directories and files similar to the one created by the Type-C Connector Class.
create_full_typec_connector_class_temp_file_hierarchy() -> PathBuf72 fn create_full_typec_connector_class_temp_file_hierarchy() -> PathBuf {
73 create_temp_dir_with_dirs_files_content(&[
74 (
75 "port0",
76 &[
77 ("uevent", "DEVTYPE=typec_port\nTYPEC_PORT=port0"),
78 ("vconn_source", "yes"),
79 ("data_role", "[host] device"),
80 ],
81 ),
82 (
83 "port0/port0-partner",
84 &[("usb_power_delivery_revision", "3.0"), ("number_of_alternate_modes", "1")],
85 ),
86 (
87 "port0/port0-partner/identity",
88 &[
89 ("id_header", "0x4c4018d1"),
90 ("product", "0x50490001"),
91 ("cert_stat", "0x0"),
92 ("product_type_vdo1", "0x45800012"),
93 ("product_type_vdo2", "0x0"),
94 ("product_type_vdo3", "0x0"),
95 ],
96 ),
97 (
98 "port0/port0-partner/port0-partner.0",
99 &[("description", "DisplayPort"), ("vdo", "0x001c0045")],
100 ),
101 (
102 "port0/port0-partner/port0-partner.0/mode1",
103 &[("description", "DisplayPort"), ("vdo", "0x001c0045")],
104 ),
105 ("port0/port0-partner/port0-partner.0/ignored", &[("ignored", "ignored content")]),
106 ("port0/port0-partner/pd0", &[("revision", "3.0")]),
107 ("port0/port0-partner/pd0/sink-capabilities", &[("uevent", "")]),
108 (
109 "port0/port0-partner/pd0/sink-capabilities/1:fixed_supply",
110 &[("operational_current", "0mA"), ("voltage", "5000mV")],
111 ),
112 ("port0/port0-partner/pd0/source-capabilities", &[("uevent", "")]),
113 (
114 "port0/port0-partner/pd0/source-capabilities/4:fixed_supply",
115 &[("maximum_current", "3000mA"), ("voltage", "20000mV")],
116 ),
117 (
118 "port0/port0-partner/pd0/source-capabilities/2:fixed_supply",
119 &[("maximum_current", "3000mA"), ("voltage", "9000mV")],
120 ),
121 ("port0/port0-cable", &[("usb_power_delivery_revision", "3.0")]),
122 (
123 "port0/port0-cable/identity",
124 &[
125 ("cert_stat", "0x290"),
126 ("id_header", "0x1800228a"),
127 ("product", "0x94880001"),
128 ("product_type_vdo1", "0x11082043"),
129 ("product_type_vdo2", "0x00000000"),
130 ("product_type_vdo3", "0x00000000"),
131 ],
132 ),
133 ("port0/port0-cable/port0-plug0", &[("number_of_alternate_modes", "2")]),
134 ("port0/port0-cable/port0-plug0/port0-plug0.0", &[("mode", "1"), ("svid", "1e4e")]),
135 (
136 "port0/port0-cable/port0-plug0/port0-plug0.0/mode1",
137 &[("active", "no"), ("vdo", "0x9031011c")],
138 ),
139 ("port0/port0-cable/port0-plug0/port0-plug0.1", &[("mode", "1"), ("svid", "8087")]),
140 (
141 "port0/port0-cable/port0-plug0/port0-plug0.1/mode1",
142 &[("active", "no"), ("vdo", "0x00030001")],
143 ),
144 ("port0/port0-cable/port0-plug0/port0-plug0.ignore", &[("ignored", "ignored content")]),
145 (
146 "port0/physical_location",
147 &[
148 ("panel", "right"),
149 ("horizontal_position", "right"),
150 ("vertical_position", "ignored"),
151 ],
152 ),
153 ("port0/usb3-port1", &[]),
154 ("port0/usb3-port1/device", &[("busnum", "3"), ("devnum", "9"), ("devpath", "1")]),
155 ("port0/usb3-port1/device/3-1.1", &[("busnum", "3"), ("devnum", "10"), ("devpath", "1.1")]),
156 ("port0/usb3-port1/device/3-1.2", &[("busnum", "3"), ("devnum", "11"), ("devpath", "1.2")]),
157 (
158 "port0/usb3-port1/device/3-1.2/3-1.2.4",
159 &[("busnum", "3"), ("devnum", "12"), ("devpath", "1.2.4")],
160 ),
161 ("port0/drm_connector", &[("connector_id", "258"), ("status", "disconnected")]),
162 ("port1", &[]),
163 ("portignored", &[("ignored", "ignored content")]),
164 ])
165 }
166
167 #[test]
test_print_dir_files()168 fn test_print_dir_files() {
169 let temp_dir_path = create_full_typec_connector_class_temp_file_hierarchy();
170 let mut outp_buffer: Cursor<Vec<u8>> = Cursor::new(Vec::new());
171 let mut outp_writer = OutputWriter::new(&mut outp_buffer, 0);
172 let expected_outp = "port0
173 data_role: [host] device
174 uevent: DEVTYPE=typec_port
175 TYPEC_PORT=port0
176 vconn_source: yes
177 ";
178
179 outp_writer
180 .print_dir_files(&temp_dir_path.join("port0"))
181 .expect("Could not execute print_dir_files");
182
183 assert_eq!(
184 &String::from_utf8(outp_buffer.into_inner()).expect("Cannot decode output buffer"),
185 expected_outp
186 );
187 }
188
189 #[test]
test_print_vdo()190 fn test_print_vdo() {
191 let temp_dir_path = create_full_typec_connector_class_temp_file_hierarchy();
192 let mut outp_buffer: Cursor<Vec<u8>> = Cursor::new(Vec::new());
193 let mut outp_writer = OutputWriter::new(&mut outp_buffer, 0);
194 let expected_outp = "id_header: 0x4c4018d1
195 USB Vendor ID: 0x18d1
196 Reserved: 0x40
197 Product Type (DFP): 0x0
198 Modal Operation Supported: 0x1
199 Product Type (UFP/Cable Plug): 0x1
200 USB Capable as a USB Device: 0x1
201 USB Capable as a USB Host: 0x0
202 ";
203
204 let _ = print_vdo(
205 &temp_dir_path.join("port0/port0-partner/identity/id_header"),
206 pd30_data::ID_HEADER_VDO,
207 &mut outp_writer,
208 );
209
210 assert_eq!(
211 &String::from_utf8(outp_buffer.into_inner()).expect("Cannot decode output buffer"),
212 expected_outp
213 );
214 }
215
216 #[test]
test_read_vdo()217 fn test_read_vdo() {
218 let temp_dir_path =
219 create_temp_dir_with_dirs_files_content(&[("identity", &[("id_header", "0x4c4018d1")])]);
220
221 let vdo =
222 read_vdo(&temp_dir_path.join("identity/id_header")).expect("read_vdo returned an error");
223 assert_eq!(vdo, 0x4c4018d1);
224 }
225
226 #[test]
test_get_pd_revision()227 fn test_get_pd_revision() {
228 let temp_dir_path = create_temp_dir_with_dirs_files_content(&[(
229 "port0-partner",
230 &[("usb_power_delivery_revision", "3.0")],
231 )]);
232 let pd_rev = get_pd_revision(&temp_dir_path.join("port0-partner"));
233 assert_eq!(pd_rev, PdRev::Pd30);
234 }
235
236 #[test]
test_parse_dirs_and_execute()237 fn test_parse_dirs_and_execute() {
238 let temp_dir_path = create_full_typec_connector_class_temp_file_hierarchy();
239 let mut outp_buffer: Cursor<Vec<u8>> = Cursor::new(Vec::new());
240 let mut outp_writer = OutputWriter::new(&mut outp_buffer, 0);
241 let expected_outp = "port0
242 data_role: [host] device
243 uevent: DEVTYPE=typec_port
244 TYPEC_PORT=port0
245 vconn_source: yes
246 port1
247 ";
248
249 let _ = parse_dirs_and_execute(&temp_dir_path, &mut outp_writer, PORT_REGEX, |path, out_wr| {
250 let _ = out_wr.print_dir_files(path);
251 });
252
253 assert_eq!(
254 &String::from_utf8(outp_buffer.into_inner()).expect("Cannot decode output buffer"),
255 expected_outp
256 )
257 }
258
259 #[test]
test_get_partner_product_type_pd30_ufp()260 fn test_get_partner_product_type_pd30_ufp() {
261 let temp_dir_path =
262 create_temp_dir_with_dirs_files_content(&[("identity", &[("id_header", "0x4c4018d1")])]);
263 let prod_type = get_partner_product_type(&temp_dir_path, PdRev::Pd30);
264
265 assert_eq!(prod_type, ProductType::Port((PortType::Ufp, PdRev::Pd30)));
266 }
267
268 #[test]
test_get_partner_product_type_pd31_drd()269 fn test_get_partner_product_type_pd31_drd() {
270 let temp_dir_path =
271 create_temp_dir_with_dirs_files_content(&[("identity", &[("id_header", "0x08800000")])]);
272 let prod_type = get_partner_product_type(&temp_dir_path, PdRev::Pd31);
273
274 assert_eq!(prod_type, ProductType::Port((PortType::Drd, PdRev::Pd31)));
275 }
276
277 #[test]
test_get_partner_product_type_other()278 fn test_get_partner_product_type_other() {
279 let temp_dir_path = create_temp_dir_with_dirs_files_content(&[]);
280 let prod_type = get_partner_product_type(&temp_dir_path, PdRev::Pd20);
281
282 assert_eq!(prod_type, ProductType::Other);
283 }
284
285 #[test]
test_print_alt_mode()286 fn test_print_alt_mode() {
287 let temp_dir_path = create_full_typec_connector_class_temp_file_hierarchy();
288 let mut outp_buffer: Cursor<Vec<u8>> = Cursor::new(Vec::new());
289 let mut outp_writer = OutputWriter::new(&mut outp_buffer, 0);
290 let expected_outp = "port0-partner.0
291 description: DisplayPort
292 vdo: 0x001c0045
293 mode1
294 description: DisplayPort
295 vdo: 0x001c0045
296 ";
297
298 print_alt_mode(
299 &temp_dir_path.join("port0").join("port0-partner").join("port0-partner.0"),
300 &mut outp_writer,
301 );
302 assert_eq!(
303 &String::from_utf8(outp_buffer.into_inner()).expect("Cannot decode output buffer"),
304 expected_outp
305 )
306 }
307
308 #[test]
test_print_pdos()309 fn test_print_pdos() {
310 let temp_dir_path = create_full_typec_connector_class_temp_file_hierarchy();
311 let mut outp_buffer: Cursor<Vec<u8>> = Cursor::new(Vec::new());
312 let mut outp_writer = OutputWriter::new(&mut outp_buffer, 0);
313 let expected_outp = "pd0
314 revision: 3.0
315 sink-capabilities
316 uevent:\x20
317 1:fixed_supply
318 operational_current: 0mA
319 voltage: 5000mV
320 source-capabilities
321 uevent:\x20
322 2:fixed_supply
323 maximum_current: 3000mA
324 voltage: 9000mV
325 4:fixed_supply
326 maximum_current: 3000mA
327 voltage: 20000mV
328 ";
329
330 print_pdos(&temp_dir_path.join("port0").join("port0-partner").join("pd0"), &mut outp_writer);
331
332 assert_eq!(
333 &String::from_utf8(outp_buffer.into_inner()).expect("Cannot decode output buffer"),
334 expected_outp
335 )
336 }
337
338 #[test]
test_get_cable_product_type()339 fn test_get_cable_product_type() {
340 let temp_dir_path = create_temp_dir_with_dirs_files_content(&[
341 ("port0-cable", &[("usb_power_delivery_revision", "3.0")]),
342 ("port0-cable/identity", &[("id_header", "0x1c6003f0")]),
343 ]);
344 let expected_outp: ProductType = ProductType::Cable((CableType::Passive, PdRev::Pd30));
345
346 let prod_type = get_cable_product_type(&temp_dir_path.join("port0-cable"));
347
348 assert_eq!(prod_type, expected_outp)
349 }
350
351 #[test]
test_full_command_output()352 fn test_full_command_output() {
353 let temp_dir_path = create_full_typec_connector_class_temp_file_hierarchy();
354 let mut outp_buffer: Cursor<Vec<u8>> = Cursor::new(Vec::new());
355 let mut outp_writer = OutputWriter::new(&mut outp_buffer, 0);
356 let expected_outp = "port0
357 data_role: [host] device
358 uevent: DEVTYPE=typec_port
359 TYPEC_PORT=port0
360 vconn_source: yes
361 port0-partner
362 number_of_alternate_modes: 1
363 usb_power_delivery_revision: 3.0
364 identity
365 id_header: 0x4c4018d1
366 USB Vendor ID: 0x18d1
367 Reserved: 0x40
368 Product Type (DFP): 0x0
369 Modal Operation Supported: 0x1
370 Product Type (UFP/Cable Plug): 0x1
371 USB Capable as a USB Device: 0x1
372 USB Capable as a USB Host: 0x0
373 cert_stat: 0x0
374 XID: 0x0
375 product: 0x50490001
376 bcdDevice: 0x1
377 USB Product ID: 0x5049
378 product_type_vdo1: 0x45800012
379 USB Highest Speed: 0x2
380 Alternate Modes: 0x2
381 Reserved: 0x20000
382 Device Capability: 0x5
383 Reserved: 0x0
384 UFP VDO Version: 0x2
385 product_type_vdo2: 0x0
386 USB3 Max Power: 0x0
387 USB3 Min Power: 0x0
388 Reserved: 0x0
389 USB4 Max Power: 0x0
390 USB4 Min Power: 0x0
391 Reserved: 0x0
392 product_type_vdo3: 0x0
393 port0-partner.0
394 description: DisplayPort
395 vdo: 0x001c0045
396 mode1
397 description: DisplayPort
398 vdo: 0x001c0045
399 pd0
400 revision: 3.0
401 sink-capabilities
402 uevent:\x20
403 1:fixed_supply
404 operational_current: 0mA
405 voltage: 5000mV
406 source-capabilities
407 uevent:\x20
408 2:fixed_supply
409 maximum_current: 3000mA
410 voltage: 9000mV
411 4:fixed_supply
412 maximum_current: 3000mA
413 voltage: 20000mV
414 port0-cable
415 usb_power_delivery_revision: 3.0
416 identity
417 id_header: 0x1800228a
418 USB Vendor ID: 0x228a
419 Reserved: 0x0
420 Product Type (DFP): 0x0
421 Modal Operation Supported: 0x0
422 Product Type (UFP/Cable Plug): 0x3
423 USB Capable as a USB Device: 0x0
424 USB Capable as a USB Host: 0x0
425 cert_stat: 0x290
426 XID: 0x290
427 product: 0x94880001
428 bcdDevice: 0x1
429 USB Product ID: 0x9488
430 product_type_vdo1: 0x11082043
431 USB Speed: 0x3
432 Reserved: 0x0
433 Vbus Current Handling: 0x2
434 Reserved: 0x0
435 Maximum Vbus Voltage: 0x0
436 Cable Termination Type: 0x0
437 Cable Latency: 0x1
438 Reserved: 0x0
439 USB Type-C Plug to USB Type: 0x2
440 Reserved: 0x0
441 VDO Version: 0x0
442 Firmware Version: 0x1
443 HW Version: 0x1
444 product_type_vdo2: 0x0
445 product_type_vdo3: 0x0
446 port0-plug0
447 number_of_alternate_modes: 2
448 port0-plug0.0
449 mode: 1
450 svid: 1e4e
451 mode1
452 active: no
453 vdo: 0x9031011c
454 port0-plug0.1
455 mode: 1
456 svid: 8087
457 mode1
458 active: no
459 vdo: 0x00030001
460 physical_location
461 panel: right
462 horizontal_position: right
463 usb_device
464 busnum: 3
465 devnum: 9
466 devpath: 1
467 usb_device
468 busnum: 3
469 devnum: 10
470 devpath: 1.1
471 usb_device
472 busnum: 3
473 devnum: 11
474 devpath: 1.2
475 usb_device
476 busnum: 3
477 devnum: 12
478 devpath: 1.2.4
479 dp_connector
480 connector_id: 258
481 status: disconnected
482 port1
483 ";
484 let _ = parse_dirs_and_execute(&temp_dir_path, &mut outp_writer, PORT_REGEX, print_port_info);
485 assert_eq!(
486 &String::from_utf8(outp_buffer.into_inner()).expect("Cannot decode output buffer"),
487 expected_outp
488 )
489 }
490