xref: /aosp_15_r20/system/usb_info_tools/typec_connector_class_helper/src/typec_class_utils_tests.rs (revision c6808bf1d92d62e809b5d71fe00befd40d32d6f9)
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