1use crate::installers::common::DistroFacade;
13use std::fmt;
14
15#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
21pub enum RocmChannel {
22 Legacy,
24 Stable,
26 Latest,
28}
29
30impl fmt::Display for RocmChannel {
31 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
32 match self {
33 RocmChannel::Legacy => write!(f, "legacy"),
34 RocmChannel::Stable => write!(f, "stable"),
35 RocmChannel::Latest => write!(f, "latest"),
36 }
37 }
38}
39
40impl RocmChannel {
41 pub fn from_choice(choice: u8) -> Option<Self> {
43 match choice {
44 1 => Some(RocmChannel::Legacy),
45 2 => Some(RocmChannel::Stable),
46 3 => Some(RocmChannel::Latest),
47 _ => None,
48 }
49 }
50
51 pub fn version(&self) -> &'static str {
53 match self {
54 RocmChannel::Legacy => "6.4.3",
55 RocmChannel::Stable => "7.2.3",
56 RocmChannel::Latest => "7.2.4",
57 }
58 }
59
60 pub fn dir_path(&self) -> &'static str {
62 match self {
63 RocmChannel::Legacy => "6.4.3",
64 RocmChannel::Stable => "7.2.3",
65 RocmChannel::Latest => "7.2.4",
66 }
67 }
68
69 pub fn pkg_version(&self) -> &'static str {
71 match self {
72 RocmChannel::Legacy => "6.4.60403-1",
73 RocmChannel::Stable => "7.2.3.70203-1",
74 RocmChannel::Latest => "7.2.4.70204-1",
75 }
76 }
77
78 pub fn major_minor(&self) -> &'static str {
80 match self {
81 RocmChannel::Legacy => "6.4",
82 RocmChannel::Stable => "7.2",
83 RocmChannel::Latest => "7.2",
84 }
85 }
86
87 pub fn version_from_manifest(manifest: &crate::core::manifest::Manifest) -> String {
92 manifest
93 .components
94 .iter()
95 .find(|c| c.id == "rocm")
96 .map(|c| c.version.clone())
97 .unwrap_or_else(|| RocmChannel::Latest.version().to_string())
98 }
99
100 pub fn pkg_version_from_manifest(manifest: &crate::core::manifest::Manifest) -> String {
104 let version = Self::version_from_manifest(manifest);
105 if version == "6.4.3" {
106 return "6.4.60403-1".to_string();
107 }
108
109 let parts: Vec<&str> = version.split('.').collect();
111 if parts.len() >= 3 {
112 let major: u64 = parts[0].parse().unwrap_or(7);
113 let minor: u64 = parts[1].parse().unwrap_or(2);
114 let patch: u64 = parts[2].parse().unwrap_or(0);
115 let build = format!("{major}{minor:02}{patch:02}");
116 format!("{}.{build}-1", version)
117 } else {
118 RocmChannel::Latest.pkg_version().to_string()
119 }
120 }
121}
122
123#[derive(Debug, Clone, Copy, PartialEq, Eq)]
125pub enum RocmInstallType {
126 Standard,
128 Minimal,
130 Full,
132 Custom,
134}
135
136impl fmt::Display for RocmInstallType {
137 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
138 match self {
139 RocmInstallType::Standard => write!(f, "standard"),
140 RocmInstallType::Minimal => write!(f, "minimal"),
141 RocmInstallType::Full => write!(f, "full"),
142 RocmInstallType::Custom => write!(f, "custom"),
143 }
144 }
145}
146
147#[derive(Debug, Clone)]
149pub struct RocmConfig {
150 pub channel: RocmChannel,
152 pub install_type: RocmInstallType,
154 pub force_reinstall: bool,
156 pub dry_run: bool,
158}
159
160impl Default for RocmConfig {
161 fn default() -> Self {
162 Self {
163 channel: RocmChannel::Latest,
164 install_type: RocmInstallType::Standard,
165 force_reinstall: false,
166 dry_run: false,
167 }
168 }
169}
170
171#[derive(Debug, Clone, PartialEq, Eq)]
173pub struct PackageCommand {
174 pub program: String,
176 pub args: Vec<String>,
178}
179
180impl PackageCommand {
181 pub fn to_command_string(&self) -> String {
183 if self.args.is_empty() {
184 self.program.clone()
185 } else {
186 format!("{} {}", self.program, self.args.join(" "))
187 }
188 }
189}
190
191#[derive(Debug, Clone)]
193pub struct RepoConfig {
194 pub repo_type: String,
196 pub url: String,
198 pub gpg_key_url: String,
200 pub file_path: String,
202}
203
204pub struct RocmInstaller {
206 config: RocmConfig,
207}
208
209impl RocmInstaller {
210 pub fn new(config: RocmConfig) -> Self {
212 Self { config }
213 }
214
215 pub fn with_defaults() -> Self {
217 Self::new(RocmConfig::default())
218 }
219
220 pub fn channel(&self) -> RocmChannel {
222 self.config.channel
223 }
224
225 pub fn repo_base_url(&self, distro: &DistroFacade) -> String {
240 let major_minor = self.config.channel.major_minor();
241 if distro.uses_apt() {
242 let codename = if distro.codename().is_empty() {
243 "noble"
244 } else {
245 distro.codename()
246 };
247 format!("https://repo.radeon.com/amdgpu/{major_minor}/ubuntu/{codename}")
248 } else if distro.uses_dnf() || distro.uses_yum() {
249 let rhel_version = Self::rhel_version(distro);
250 format!("https://repo.radeon.com/amdgpu/{major_minor}/rhel/{rhel_version}")
251 } else if distro.uses_zypper() {
252 format!("https://repo.radeon.com/amdgpu/{major_minor}/sle/15.7")
253 } else {
254 format!("https://repo.radeon.com/amdgpu/{major_minor}/ubuntu/noble")
256 }
257 }
258
259 pub fn rocm_core_repo_url(&self, _distro: &DistroFacade) -> String {
261 let major_minor = self.config.channel.major_minor();
262 format!("https://repo.radeon.com/rocm/apt/{major_minor}")
263 }
264
265 pub fn gpg_key_url(&self) -> &'static str {
267 "https://repo.radeon.com/rocm/rocm.gpg.key"
268 }
269
270 fn rhel_version(distro: &DistroFacade) -> &'static str {
272 let v = distro.version();
273 if v.starts_with("10") {
274 "10"
275 } else if v.starts_with("9") {
276 "9"
277 } else if v.starts_with("8") {
278 "8"
279 } else {
280 "9"
281 }
282 }
283
284 pub fn amdgpu_install_deb_url(&self) -> String {
290 let release_dir = self.config.channel.dir_path();
291 let pkg_ver = self.config.channel.pkg_version();
292 format!(
293 "https://repo.radeon.com/amdgpu-install/{release_dir}/ubuntu/noble/amdgpu-install_{pkg_ver}_all.deb"
294 )
295 }
296
297 pub fn amdgpu_install_rpm_url(&self, rhel_version: &str) -> String {
299 let release_dir = self.config.channel.dir_path();
300 let pkg_ver = self.config.channel.pkg_version();
301 format!(
302 "https://repo.radeon.com/amdgpu-install/{release_dir}/rhel/{rhel_version}/amdgpu-install-{pkg_ver}.el{rhel_version}.noarch.rpm"
303 )
304 }
305
306 pub fn amdgpu_install_sles_rpm_url(&self) -> String {
308 let release_dir = self.config.channel.dir_path();
309 let pkg_ver = self.config.channel.pkg_version();
310 format!(
311 "https://repo.radeon.com/amdgpu-install/{release_dir}/sle/15.7/amdgpu-install-{pkg_ver}.noarch.rpm"
312 )
313 }
314
315 pub fn apt_install_commands(&self) -> Vec<PackageCommand> {
317 let deb_url = self.amdgpu_install_deb_url();
318 vec![
319 PackageCommand {
321 program: "wget".to_string(),
322 args: vec!["-q".to_string(), deb_url.clone()],
323 },
324 PackageCommand {
326 program: "sudo".to_string(),
327 args: vec![
328 "dpkg".to_string(),
329 "-i".to_string(),
330 format!(
331 "amdgpu-install_{}_all.deb",
332 self.config.channel.pkg_version()
333 ),
334 ],
335 },
336 PackageCommand {
338 program: "sudo".to_string(),
339 args: vec!["apt-get".to_string(), "update".to_string()],
340 },
341 self.apt_rocm_metapackage_command(),
343 ]
344 }
345
346 fn apt_rocm_metapackage_command(&self) -> PackageCommand {
348 let metapkg = match self.config.install_type {
349 RocmInstallType::Standard => "rocm-libs",
350 RocmInstallType::Minimal => "rocm-core",
351 RocmInstallType::Full => "rocm-dev",
352 RocmInstallType::Custom => "rocm-libs",
353 };
354 PackageCommand {
355 program: "sudo".to_string(),
356 args: vec![
357 "apt-get".to_string(),
358 "install".to_string(),
359 "-y".to_string(),
360 metapkg.to_string(),
361 ],
362 }
363 }
364
365 pub fn dnf_install_commands(&self, rhel_version: &str) -> Vec<PackageCommand> {
367 let rpm_url = self.amdgpu_install_rpm_url(rhel_version);
368 vec![
369 PackageCommand {
370 program: "sudo".to_string(),
371 args: vec![
372 "dnf".to_string(),
373 "install".to_string(),
374 "-y".to_string(),
375 rpm_url,
376 ],
377 },
378 self.dnf_rocm_metapackage_command(),
379 ]
380 }
381
382 fn dnf_rocm_metapackage_command(&self) -> PackageCommand {
384 let metapkg = match self.config.install_type {
385 RocmInstallType::Standard => "rocm-libs",
386 RocmInstallType::Minimal => "rocm-core",
387 RocmInstallType::Full => "rocm-dev",
388 RocmInstallType::Custom => "rocm-libs",
389 };
390 PackageCommand {
391 program: "sudo".to_string(),
392 args: vec![
393 "dnf".to_string(),
394 "install".to_string(),
395 "-y".to_string(),
396 metapkg.to_string(),
397 ],
398 }
399 }
400
401 pub fn pacman_install_commands(&self, aur_helper: &str) -> Vec<PackageCommand> {
414 let packages = self.pacman_rocm_packages();
415 let mut args = vec![
416 "-S".to_string(),
417 "--needed".to_string(),
418 "--noconfirm".to_string(),
419 ];
420 args.extend(packages);
421
422 vec![PackageCommand {
423 program: aur_helper.to_string(),
424 args,
425 }]
426 }
427
428 pub fn pacman_rocm_packages(&self) -> Vec<String> {
430 let mut pkgs = vec![
431 "rocm-hip-sdk".to_string(),
432 "rocminfo".to_string(),
433 "rocm-smi-lib".to_string(),
434 "hipblas".to_string(),
435 "rocblas".to_string(),
436 "hipsparselt".to_string(),
437 ];
438 if self.config.channel != RocmChannel::Legacy {
440 pkgs.push("rocm-opencl-sdk".to_string());
441 pkgs.push("rccl".to_string());
442 pkgs.push("rocwmma".to_string());
446 }
447 pkgs
448 }
449
450 pub fn zypper_install_commands(&self) -> Vec<PackageCommand> {
452 let rpm_url = self.amdgpu_install_sles_rpm_url();
453 vec![
454 PackageCommand {
455 program: "sudo".to_string(),
456 args: vec![
457 "zypper".to_string(),
458 "--non-interactive".to_string(),
459 "install".to_string(),
460 rpm_url,
461 ],
462 },
463 PackageCommand {
464 program: "sudo".to_string(),
465 args: vec![
466 "zypper".to_string(),
467 "--non-interactive".to_string(),
468 "install".to_string(),
469 "-y".to_string(),
470 "rocm-libs".to_string(),
471 ],
472 },
473 ]
474 }
475
476 pub fn apt_repo_config(&self, distro: &DistroFacade) -> RepoConfig {
478 let codename = if distro.codename().is_empty() {
479 "noble"
480 } else {
481 distro.codename()
482 };
483 let major_minor = self.config.channel.major_minor();
484 RepoConfig {
485 repo_type: "apt".to_string(),
486 url: format!("deb [signed-by=/etc/apt/keyrings/rocm.gpg] https://repo.radeon.com/rocm/apt/{major_minor} {codename} main"),
487 gpg_key_url: "https://repo.radeon.com/rocm/rocm.gpg.key".to_string(),
488 file_path: "/etc/apt/sources.list.d/rocm.list".to_string(),
489 }
490 }
491
492 pub fn amdgpu_apt_repo_config(&self, distro: &DistroFacade) -> RepoConfig {
494 let codename = if distro.codename().is_empty() {
495 "noble"
496 } else {
497 distro.codename()
498 };
499 let major_minor = self.config.channel.major_minor();
500 RepoConfig {
501 repo_type: "apt".to_string(),
502 url: format!("deb [signed-by=/etc/apt/keyrings/rocm.gpg] https://repo.radeon.com/amdgpu/{major_minor}/ubuntu/{codename} {codename} main"),
503 gpg_key_url: "https://repo.radeon.com/rocm/rocm.gpg.key".to_string(),
504 file_path: "/etc/apt/sources.list.d/amdgpu.list".to_string(),
505 }
506 }
507
508 pub fn apt_pin_config(&self) -> (String, String) {
510 (
511 "Package: *\nPin: release o=repo.radeon.com\nPin-Priority: 600\n".to_string(),
512 "/etc/apt/preferences.d/rocm-pin-600".to_string(),
513 )
514 }
515
516 pub fn build_pacman_check_installed_command(package: &str) -> PackageCommand {
521 PackageCommand {
522 program: "pacman".to_string(),
523 args: vec!["-Qi".to_string(), package.to_string()],
524 }
525 }
526
527 pub fn filter_already_installed_pacman(packages: &[String]) -> Vec<String> {
533 use std::process::Command;
534
535 packages
536 .iter()
537 .filter(|pkg| {
538 let status = Command::new("pacman").args(["-Qi", pkg.as_str()]).output();
539 match status {
540 Ok(output) => !output.status.success(),
541 Err(_) => true, }
543 })
544 .cloned()
545 .collect()
546 }
547
548 pub fn is_up_to_date_output(output: &str) -> bool {
557 let lower = output.to_lowercase();
558 lower.contains("up to date -- skipping")
560 || lower.contains("up to date, skipping")
561 || lower.contains("up to date -- reinstalling")
562 || lower.contains("there is nothing to do")
563 || lower.contains("already installed")
564 || lower.contains("warning:") && lower.contains("up to date")
566 }
567}
568
569#[cfg(test)]
574mod tests {
575 use super::*;
576 use crate::platform::detection::{DistroFamily, DistroInfo, PackageManager};
577
578 fn ubuntu_distro() -> DistroFacade {
579 DistroFacade::from_info(DistroInfo {
580 id: "ubuntu".to_string(),
581 name: "Ubuntu 24.04 LTS".to_string(),
582 version: "24.04".to_string(),
583 codename: "noble".to_string(),
584 family: DistroFamily::Debian,
585 pkg_manager: PackageManager::Apt,
586 id_like: "debian".to_string(),
587 })
588 }
589
590 fn fedora_distro() -> DistroFacade {
591 DistroFacade::from_info(DistroInfo {
592 id: "fedora".to_string(),
593 name: "Fedora Linux 41".to_string(),
594 version: "41".to_string(),
595 codename: String::new(),
596 family: DistroFamily::Rhel,
597 pkg_manager: PackageManager::Dnf,
598 id_like: "fedora".to_string(),
599 })
600 }
601
602 fn rhel9_distro() -> DistroFacade {
603 DistroFacade::from_info(DistroInfo {
604 id: "rhel".to_string(),
605 name: "Red Hat Enterprise Linux 9".to_string(),
606 version: "9.3".to_string(),
607 codename: String::new(),
608 family: DistroFamily::Rhel,
609 pkg_manager: PackageManager::Dnf,
610 id_like: "rhel".to_string(),
611 })
612 }
613
614 fn suse_distro() -> DistroFacade {
615 DistroFacade::from_info(DistroInfo {
616 id: "opensuse-leap".to_string(),
617 name: "openSUSE Leap 15.7".to_string(),
618 version: "15.7".to_string(),
619 codename: String::new(),
620 family: DistroFamily::Suse,
621 pkg_manager: PackageManager::Zypper,
622 id_like: "suse".to_string(),
623 })
624 }
625
626 #[test]
629 fn test_channel_legacy_version() {
630 let ch = RocmChannel::Legacy;
631 assert_eq!(ch.version(), "6.4.3");
632 assert_eq!(ch.pkg_version(), "6.4.60403-1");
633 assert_eq!(ch.major_minor(), "6.4");
634 }
635
636 #[test]
637 fn test_channel_stable_version() {
638 let ch = RocmChannel::Stable;
639 assert_eq!(ch.version(), "7.2.3");
640 assert_eq!(ch.pkg_version(), "7.2.3.70203-1");
641 assert_eq!(ch.major_minor(), "7.2");
642 }
643
644 #[test]
645 fn test_channel_latest_version() {
646 let ch = RocmChannel::Latest;
647 assert_eq!(ch.version(), "7.2.4");
648 assert_eq!(ch.pkg_version(), "7.2.4.70204-1");
649 assert_eq!(ch.major_minor(), "7.2");
650 }
651
652 #[test]
653 fn test_channel_from_choice() {
654 assert_eq!(RocmChannel::from_choice(1), Some(RocmChannel::Legacy));
655 assert_eq!(RocmChannel::from_choice(2), Some(RocmChannel::Stable));
656 assert_eq!(RocmChannel::from_choice(3), Some(RocmChannel::Latest));
657 assert_eq!(RocmChannel::from_choice(0), None);
658 assert_eq!(RocmChannel::from_choice(4), None);
659 }
660
661 #[test]
662 fn test_pkg_version_from_manifest_latest() {
663 let manifest = crate::core::manifest::Manifest {
664 schema_version: 2,
665 sequence: 1,
666 generated_at: "2026-05-31T00:00:00Z".to_string(),
667 expires_at: None,
668 min_runtime_version: String::new(),
669 components: vec![crate::core::manifest::ManifestComponent {
670 id: "rocm".to_string(),
671 version: "7.2.4".to_string(),
672 script: "scripts/install_rocm.sh".to_string(),
673 category: crate::core::Category::Foundation,
674 validation_tier: crate::core::ValidationTier::Validated,
675 min_rocm_version: String::new(),
676 dependencies: vec![],
677 compatible_channels: vec!["latest".to_string()],
678 exclusive_group: String::new(),
679 }],
680 signature: None,
681 };
682
683 assert_eq!(
684 RocmChannel::pkg_version_from_manifest(&manifest),
685 "7.2.4.70204-1"
686 );
687 }
688
689 #[test]
690 fn test_pkg_version_from_manifest_legacy() {
691 let manifest = crate::core::manifest::Manifest {
692 schema_version: 2,
693 sequence: 1,
694 generated_at: "2026-05-31T00:00:00Z".to_string(),
695 expires_at: None,
696 min_runtime_version: String::new(),
697 components: vec![crate::core::manifest::ManifestComponent {
698 id: "rocm".to_string(),
699 version: "6.4.3".to_string(),
700 script: "scripts/install_rocm.sh".to_string(),
701 category: crate::core::Category::Foundation,
702 validation_tier: crate::core::ValidationTier::Validated,
703 min_rocm_version: String::new(),
704 dependencies: vec![],
705 compatible_channels: vec!["legacy".to_string()],
706 exclusive_group: String::new(),
707 }],
708 signature: None,
709 };
710
711 assert_eq!(
712 RocmChannel::pkg_version_from_manifest(&manifest),
713 "6.4.60403-1"
714 );
715 }
716
717 #[test]
720 fn test_apt_install_commands_latest() {
721 let installer = RocmInstaller::new(RocmConfig {
722 channel: RocmChannel::Latest,
723 install_type: RocmInstallType::Standard,
724 ..Default::default()
725 });
726 let cmds = installer.apt_install_commands();
727 assert_eq!(cmds.len(), 4);
728
729 assert_eq!(cmds[0].program, "wget");
731 assert!(cmds[0]
732 .args
733 .iter()
734 .any(|a| a.contains("7.2.4/ubuntu/noble")));
735 assert!(cmds[0].args.iter().any(|a| a.contains("7.2.4.70204-1")));
736 assert_eq!(cmds[1].program, "sudo");
737 assert!(cmds[1].args.contains(&"dpkg".to_string()));
738
739 assert_eq!(cmds[2].program, "sudo");
741 assert!(cmds[2].args.contains(&"update".to_string()));
742
743 assert_eq!(cmds[3].program, "sudo");
745 assert!(cmds[3].args.contains(&"rocm-libs".to_string()));
746 }
747
748 #[test]
749 fn test_apt_install_commands_full() {
750 let installer = RocmInstaller::new(RocmConfig {
751 channel: RocmChannel::Stable,
752 install_type: RocmInstallType::Full,
753 ..Default::default()
754 });
755 let cmds = installer.apt_install_commands();
756 let meta_cmd = &cmds[3];
757 assert!(meta_cmd.args.contains(&"rocm-dev".to_string()));
758 }
759
760 #[test]
761 fn test_dnf_install_commands() {
762 let installer = RocmInstaller::new(RocmConfig {
763 channel: RocmChannel::Latest,
764 install_type: RocmInstallType::Standard,
765 ..Default::default()
766 });
767 let cmds = installer.dnf_install_commands("9");
768 assert_eq!(cmds.len(), 2);
769
770 assert_eq!(cmds[0].program, "sudo");
772 assert!(cmds[0].args.iter().any(|a| a.contains("7.2.4/rhel/9")));
773 assert!(cmds[0].args.iter().any(|a| a.contains("7.2.4.70204-1")));
774 assert!(cmds[0].args.iter().any(|a| a.contains("el9")));
775
776 assert_eq!(cmds[1].program, "sudo");
778 assert!(cmds[1].args.contains(&"rocm-libs".to_string()));
779 }
780
781 #[test]
782 fn test_pacman_install_commands() {
783 let installer = RocmInstaller::new(RocmConfig {
784 channel: RocmChannel::Latest,
785 install_type: RocmInstallType::Standard,
786 ..Default::default()
787 });
788 let cmds = installer.pacman_install_commands("yay");
789 assert_eq!(
794 cmds.len(),
795 1,
796 "pacman install must be a single user-space yay command"
797 );
798 assert_eq!(cmds[0].program, "yay");
799 assert!(
800 !cmds.iter().any(|c| c.program == "sudo"),
801 "yay must not run under sudo"
802 );
803 assert!(cmds[0].args.contains(&"-S".to_string()));
804 assert!(cmds[0].args.contains(&"--needed".to_string()));
805 assert!(cmds[0].args.contains(&"--noconfirm".to_string()));
806 let pkgs = installer.pacman_rocm_packages();
808 assert!(pkgs.contains(&"rocm-hip-sdk".to_string()));
809 assert!(pkgs.contains(&"rocminfo".to_string()));
810 assert!(pkgs.contains(&"rocm-opencl-sdk".to_string())); assert!(pkgs.contains(&"rccl".to_string())); assert!(pkgs.contains(&"rocwmma".to_string())); for p in &pkgs {
814 assert!(cmds[0].args.contains(p), "yay args missing package {p}");
815 }
816 }
817
818 #[test]
819 fn test_pacman_legacy_no_extras() {
820 let installer = RocmInstaller::new(RocmConfig {
821 channel: RocmChannel::Legacy,
822 install_type: RocmInstallType::Standard,
823 ..Default::default()
824 });
825 let pkgs = installer.pacman_rocm_packages();
826 assert!(!pkgs.contains(&"rocm-opencl-sdk".to_string()));
827 assert!(!pkgs.contains(&"rccl".to_string()));
828 assert!(!pkgs.contains(&"rocwmma".to_string())); }
830
831 #[test]
832 fn test_zypper_install_commands() {
833 let installer = RocmInstaller::new(RocmConfig {
834 channel: RocmChannel::Latest,
835 install_type: RocmInstallType::Standard,
836 ..Default::default()
837 });
838 let cmds = installer.zypper_install_commands();
839 assert_eq!(cmds.len(), 2);
840 assert_eq!(cmds[0].program, "sudo");
841 assert!(cmds[0].args.iter().any(|a| a.contains("zypper")));
842 assert!(cmds[0].args.iter().any(|a| a.contains("7.2.4/sle/15.7")));
843 assert!(cmds[0].args.iter().any(|a| a.contains("7.2.4.70204-1")));
844 }
845
846 #[test]
849 fn test_repo_base_url_ubuntu() {
850 let installer = RocmInstaller::new(RocmConfig {
851 channel: RocmChannel::Latest,
852 ..Default::default()
853 });
854 let distro = ubuntu_distro();
855 let url = installer.repo_base_url(&distro);
856 assert_eq!(url, "https://repo.radeon.com/amdgpu/7.2/ubuntu/noble");
857 }
858
859 #[test]
860 fn test_repo_base_url_rhel() {
861 let installer = RocmInstaller::new(RocmConfig {
862 channel: RocmChannel::Latest,
863 ..Default::default()
864 });
865 let distro = rhel9_distro();
866 let url = installer.repo_base_url(&distro);
867 assert_eq!(url, "https://repo.radeon.com/amdgpu/7.2/rhel/9");
868 }
869
870 #[test]
871 fn test_repo_base_url_fedora() {
872 let installer = RocmInstaller::new(RocmConfig {
873 channel: RocmChannel::Stable,
874 ..Default::default()
875 });
876 let distro = fedora_distro();
877 let url = installer.repo_base_url(&distro);
878 assert_eq!(url, "https://repo.radeon.com/amdgpu/7.2/rhel/9");
880 }
881
882 #[test]
883 fn test_repo_base_url_suse() {
884 let installer = RocmInstaller::new(RocmConfig {
885 channel: RocmChannel::Latest,
886 ..Default::default()
887 });
888 let distro = suse_distro();
889 let url = installer.repo_base_url(&distro);
890 assert_eq!(url, "https://repo.radeon.com/amdgpu/7.2/sle/15.7");
891 }
892
893 #[test]
894 fn test_amdgpu_install_deb_url() {
895 let installer = RocmInstaller::new(RocmConfig {
896 channel: RocmChannel::Latest,
897 ..Default::default()
898 });
899 let url = installer.amdgpu_install_deb_url();
900 assert!(url.contains("7.2.4/ubuntu/noble"));
901 assert!(url.contains("7.2.4.70204-1"));
902 assert!(url.contains("ubuntu/noble"));
903 assert!(url.ends_with(".deb"));
904 }
905
906 #[test]
907 fn test_amdgpu_install_rpm_url() {
908 let installer = RocmInstaller::new(RocmConfig {
909 channel: RocmChannel::Latest,
910 ..Default::default()
911 });
912 let url = installer.amdgpu_install_rpm_url("9");
913 assert!(url.contains("7.2.4/rhel/9"));
914 assert!(url.contains("7.2.4.70204-1"));
915 assert!(url.contains("rhel/9"));
916 assert!(url.ends_with(".noarch.rpm"));
917 }
918
919 #[test]
920 fn test_apt_repo_config() {
921 let installer = RocmInstaller::new(RocmConfig {
922 channel: RocmChannel::Latest,
923 ..Default::default()
924 });
925 let distro = ubuntu_distro();
926 let repo = installer.apt_repo_config(&distro);
927 assert_eq!(repo.file_path, "/etc/apt/sources.list.d/rocm.list");
928 assert!(repo.url.contains("rocm/apt/7.2"));
929 assert!(repo.url.contains("noble"));
930 }
931
932 #[test]
933 fn test_amdgpu_apt_repo_config() {
934 let installer = RocmInstaller::new(RocmConfig {
935 channel: RocmChannel::Latest,
936 ..Default::default()
937 });
938 let distro = ubuntu_distro();
939 let repo = installer.amdgpu_apt_repo_config(&distro);
940 assert_eq!(repo.file_path, "/etc/apt/sources.list.d/amdgpu.list");
941 assert!(repo.url.contains("amdgpu/7.2"));
942 }
943
944 #[test]
945 fn test_apt_pin_config() {
946 let installer = RocmInstaller::with_defaults();
947 let (content, path) = installer.apt_pin_config();
948 assert_eq!(path, "/etc/apt/preferences.d/rocm-pin-600");
949 assert!(content.contains("Pin-Priority: 600"));
950 }
951
952 #[test]
955 fn test_all_channels_version_consistency() {
956 for choice in [1u8, 2, 3] {
957 let ch = RocmChannel::from_choice(choice).unwrap();
958 let config = RocmConfig {
959 channel: ch,
960 ..Default::default()
961 };
962 let installer = RocmInstaller::new(config);
963
964 let deb_url = installer.amdgpu_install_deb_url();
966 assert!(
967 deb_url.contains(ch.pkg_version()),
968 "deb URL for {:?} should contain pkg_version {}",
969 ch,
970 ch.pkg_version()
971 );
972
973 let distro = ubuntu_distro();
975 let repo_url = installer.repo_base_url(&distro);
976 assert!(
977 repo_url.contains(ch.major_minor()),
978 "repo URL for {:?} should contain major_minor {}",
979 ch,
980 ch.major_minor()
981 );
982 }
983 }
984
985 #[test]
986 fn test_install_type_metapackages() {
987 for (itype, expected_pkg) in [
988 (RocmInstallType::Standard, "rocm-libs"),
989 (RocmInstallType::Minimal, "rocm-core"),
990 (RocmInstallType::Full, "rocm-dev"),
991 (RocmInstallType::Custom, "rocm-libs"),
992 ] {
993 let installer = RocmInstaller::new(RocmConfig {
994 install_type: itype,
995 ..Default::default()
996 });
997 let cmds = installer.apt_install_commands();
998 let meta_cmd = &cmds[3];
999 assert!(
1000 meta_cmd.args.contains(&expected_pkg.to_string()),
1001 "Install type {:?} should use metapackage {}",
1002 itype,
1003 expected_pkg
1004 );
1005 }
1006 }
1007
1008 #[test]
1011 fn test_is_up_to_date_yay_warning() {
1012 assert!(RocmInstaller::is_up_to_date_output(
1013 "warning: rocminfo is up to date -- skipping"
1014 ));
1015 }
1016
1017 #[test]
1018 fn test_is_up_to_date_yay_warning_case_insensitive() {
1019 assert!(RocmInstaller::is_up_to_date_output(
1020 "WARNING: rocminfo is UP TO DATE -- skipping"
1021 ));
1022 }
1023
1024 #[test]
1025 fn test_is_up_to_date_nothing_to_do() {
1026 assert!(RocmInstaller::is_up_to_date_output(
1027 " there is nothing to do\n"
1028 ));
1029 }
1030
1031 #[test]
1032 fn test_is_up_to_date_already_installed() {
1033 assert!(RocmInstaller::is_up_to_date_output(
1034 "package already installed"
1035 ));
1036 }
1037
1038 #[test]
1039 fn test_is_up_to_date_normal_output_is_false() {
1040 assert!(!RocmInstaller::is_up_to_date_output(
1041 "resolving dependencies...\nlooking for conflicting packages...\n"
1042 ));
1043 }
1044
1045 #[test]
1046 fn test_is_up_to_date_error_output_is_false() {
1047 assert!(!RocmInstaller::is_up_to_date_output(
1048 "error: failed to prepare transaction\nerror: target not found: rocminfo"
1049 ));
1050 }
1051
1052 #[test]
1053 fn test_is_up_to_date_empty_output_is_false() {
1054 assert!(!RocmInstaller::is_up_to_date_output(""));
1055 }
1056
1057 #[test]
1058 fn test_is_up_to_date_pacman_skipping() {
1059 assert!(RocmInstaller::is_up_to_date_output(
1060 "warning: rocm-hip-sdk is up to date -- skipping"
1061 ));
1062 }
1063
1064 #[test]
1067 fn test_build_pacman_check_installed_command() {
1068 let cmd = RocmInstaller::build_pacman_check_installed_command("rocminfo");
1069 assert_eq!(cmd.program, "pacman");
1070 assert_eq!(cmd.args, vec!["-Qi", "rocminfo"]);
1071 }
1072
1073 #[test]
1074 fn test_build_pacman_check_installed_command_complex_pkg() {
1075 let cmd = RocmInstaller::build_pacman_check_installed_command("rocm-hip-sdk");
1076 assert_eq!(cmd.program, "pacman");
1077 assert!(cmd.args.contains(&"rocm-hip-sdk".to_string()));
1078 }
1079}