Skip to main content

java_manager/
manager.rs

1// Copyright 2026 TaimWay
2//
3// @file: manager.rs
4//
5// Licensed under the Apache License, Version 2.0 (the "License");
6// you may not use this file except in compliance with the License.
7// You may obtain a copy of the License at
8//
9// http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing, software
12// distributed under the License is distributed on an "AS IS" BASIS,
13// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14// See the License for the specific language governing permissions and
15// limitations under the License.
16
17use std::collections::HashMap;
18
19use crate::errors::Result;
20use crate::info::JavaInfo;
21
22/// Manages multiple Java installations and provides convenient access methods.
23///
24/// The `JavaManager` struct provides functionality to:
25/// - Store and organize multiple Java installations
26/// - Filter installations by various criteria
27/// - Set default Java installations
28/// - Execute commands with specific Java versions
29///
30/// # Examples
31///
32/// ```rust
33/// use java_manager::{JavaManager, JavaInfo};
34///
35/// fn main() -> java_manager::Result<()> {
36///     // Create a manager and discover all Java installations
37///     let mut manager = JavaManager::new();
38///     manager.discover_installations()?;
39///
40///     // List all installations
41///     println!("Found {} Java installations:", manager.len());
42///     for java in manager.list() {
43///         println!("  - {}", java);
44///     }
45///
46///     // Get Java by version
47///     if let Some(java_11) = manager.get_by_version(11) {
48///         println!("Java 11: {}", java_11);
49///     }
50///
51///     Ok(())
52/// }
53/// ```
54pub struct JavaManager {
55    /// Vector of Java installations
56    java_installations: Vec<JavaInfo>,
57    /// Default Java installation index
58    default_index: Option<usize>,
59    /// Map of version to installation indices for quick lookup
60    version_map: HashMap<u32, Vec<usize>>,
61}
62
63impl JavaManager {
64    /// Creates a new empty `JavaManager`.
65    ///
66    /// # Returns
67    ///
68    /// A new `JavaManager` instance
69    ///
70    /// # Examples
71    ///
72    /// ```rust
73    /// use java_manager::JavaManager;
74    ///
75    /// let manager = JavaManager::new();
76    /// assert_eq!(manager.len(), 0);
77    /// ```
78    pub fn new() -> Self {
79        JavaManager {
80            java_installations: Vec::new(),
81            default_index: None,
82            version_map: HashMap::new(),
83        }
84    }
85
86    /// Discovers and adds all Java installations on the system.
87    ///
88    /// # Returns
89    ///
90    /// - `Ok(())` if discovery succeeds
91    /// - `Err(JavaLocatorError)` if an error occurs during discovery
92    ///
93    /// # Examples
94    ///
95    /// ```rust
96    /// use java_manager::JavaManager;
97    ///
98    /// fn main() -> java_manager::Result<()> {
99    ///     let mut manager = JavaManager::new();
100    ///     manager.discover_installations()?;
101    ///     println!("Discovered {} Java installations", manager.len());
102    ///     Ok(())
103    /// }
104    /// ```
105    pub fn discover_installations(&mut self) -> Result<()> {
106        let installations = crate::local::find_all_java_installations()?;
107        
108        for installation in installations {
109            self.add(installation);
110        }
111        
112        // Set the first installation as default if any exist
113        if !self.java_installations.is_empty() {
114            self.default_index = Some(0);
115        }
116        
117        Ok(())
118    }
119
120    /// Adds a Java installation to the manager.
121    ///
122    /// # Arguments
123    ///
124    /// * `java_info` - Java installation information to add
125    ///
126    /// # Examples
127    ///
128    /// ```rust
129    /// use java_manager::{JavaManager, JavaInfo};
130    ///
131    /// let mut manager = JavaManager::new();
132    /// let java_info = JavaInfo::new("java", "/usr/bin/java", "11.0.12", "64-bit", "OpenJDK");
133    /// manager.add(java_info);
134    /// assert_eq!(manager.len(), 1);
135    /// ```
136    pub fn add(&mut self, java_info: JavaInfo) {
137        let index = self.java_installations.len();
138        self.java_installations.push(java_info.clone());
139        
140        // Update version map for quick lookup
141        if let Some(version) = java_info.get_major_version() {
142            self.version_map
143                .entry(version)
144                .or_insert_with(Vec::new)
145                .push(index);
146        }
147        
148        // Set as default if this is the first installation
149        if self.default_index.is_none() {
150            self.default_index = Some(index);
151        }
152    }
153
154    /// Gets a Java installation by index.
155    ///
156    /// # Arguments
157    ///
158    /// * `index` - Index of the Java installation to retrieve
159    ///
160    /// # Returns
161    ///
162    /// - `Some(&JavaInfo)` if the index is valid
163    /// - `None` if the index is out of bounds
164    ///
165    /// # Examples
166    ///
167    /// ```rust
168    /// use java_manager::JavaManager;
169    ///
170    /// let manager = JavaManager::new();
171    /// // Add some installations first...
172    /// // let java = manager.get(0);
173    /// ```
174    pub fn get(&self, index: usize) -> Option<&JavaInfo> {
175        self.java_installations.get(index)
176    }
177
178    /// Gets a Java installation by major version.
179    ///
180    /// If multiple installations have the same version, returns the first one.
181    ///
182    /// # Arguments
183    ///
184    /// * `version` - Major version to look for (e.g., 8, 11, 17)
185    ///
186    /// # Returns
187    ///
188    /// - `Some(&JavaInfo)` if a matching installation is found
189    /// - `None` if no installation with the specified version exists
190    ///
191    /// # Examples
192    ///
193    /// ```rust
194    /// use java_manager::JavaManager;
195    ///
196    /// let manager = JavaManager::new();
197    /// // Discover installations first...
198    /// // if let Some(java_11) = manager.get_by_version(11) {
199    /// //     println!("Found Java 11: {}", java_11);
200    /// // }
201    /// ```
202    pub fn get_by_version(&self, version: u32) -> Option<&JavaInfo> {
203        self.version_map
204            .get(&version)
205            .and_then(|indices| indices.first())
206            .and_then(|&index| self.get(index))
207    }
208
209    /// Gets all Java installations of a specific major version.
210    ///
211    /// # Arguments
212    ///
213    /// * `version` - Major version to look for
214    ///
215    /// # Returns
216    ///
217    /// Vector of references to Java installations with the specified version
218    ///
219    /// # Examples
220    ///
221    /// ```rust
222    /// use java_manager::JavaManager;
223    ///
224    /// let manager = JavaManager::new();
225    /// // Discover installations first...
226    /// // let java_11_installations = manager.get_all_by_version(11);
227    /// // println!("Found {} Java 11 installations", java_11_installations.len());
228    /// ```
229    pub fn get_all_by_version(&self, version: u32) -> Vec<&JavaInfo> {
230        self.version_map
231            .get(&version)
232            .map(|indices| {
233                indices.iter()
234                    .filter_map(|&index| self.get(index))
235                    .collect()
236            })
237            .unwrap_or_default()
238    }
239
240    /// Gets the default Java installation.
241    ///
242    /// # Returns
243    ///
244    /// - `Some(&JavaInfo)` if a default installation is set
245    /// - `None` if no installations exist or no default is set
246    ///
247    /// # Examples
248    ///
249    /// ```rust
250    /// use java_manager::JavaManager;
251    ///
252    /// let manager = JavaManager::new();
253    /// // Discover installations first...
254    /// // if let Some(default_java) = manager.get_default() {
255    /// //     println!("Default Java: {}", default_java);
256    /// // }
257    /// ```
258    pub fn get_default(&self) -> Option<&JavaInfo> {
259        self.default_index.and_then(|index| self.get(index))
260    }
261
262    /// Sets the default Java installation by index.
263    ///
264    /// # Arguments
265    ///
266    /// * `index` - Index of the Java installation to set as default
267    ///
268    /// # Returns
269    ///
270    /// - `true` if the index is valid and default was set
271    /// - `false` if the index is out of bounds
272    ///
273    /// # Examples
274    ///
275    /// ```rust
276    /// use java_manager::JavaManager;
277    ///
278    /// let mut manager = JavaManager::new();
279    /// // Add installations first...
280    /// // let success = manager.set_default(0);
281    /// ```
282    pub fn set_default(&mut self, index: usize) -> bool {
283        if index < self.java_installations.len() {
284            self.default_index = Some(index);
285            true
286        } else {
287            false
288        }
289    }
290
291    /// Sets the default Java installation by version.
292    ///
293    /// If multiple installations have the same version, sets the first one as default.
294    ///
295    /// # Arguments
296    ///
297    /// * `version` - Major version to set as default
298    ///
299    /// # Returns
300    ///
301    /// - `true` if a matching installation was found and set as default
302    /// - `false` if no installation with the specified version exists
303    ///
304    /// # Examples
305    ///
306    /// ```rust
307    /// use java_manager::JavaManager;
308    ///
309    /// let mut manager = JavaManager::new();
310    /// // Discover installations first...
311    /// // let success = manager.set_default_by_version(11);
312    /// ```
313    pub fn set_default_by_version(&mut self, version: u32) -> bool {
314        if let Some(&index) = self.version_map
315            .get(&version)
316            .and_then(|indices| indices.first())
317        {
318            self.default_index = Some(index);
319            true
320        } else {
321            false
322        }
323    }
324
325    /// Returns a reference to all Java installations.
326    ///
327    /// # Returns
328    ///
329    /// Reference to vector of all Java installations
330    ///
331    /// # Examples
332    ///
333    /// ```rust
334    /// use java_manager::JavaManager;
335    ///
336    /// let manager = JavaManager::new();
337    /// // Discover installations first...
338    /// // let all_java = manager.list();
339    /// // for java in all_java {
340    /// //     println!("{}", java);
341    /// // }
342    /// ```
343    pub fn list(&self) -> &Vec<JavaInfo> {
344        &self.java_installations
345    }
346
347    /// Returns the number of Java installations in the manager.
348    ///
349    /// # Returns
350    ///
351    /// Number of Java installations
352    ///
353    /// # Examples
354    ///
355    /// ```rust
356    /// use java_manager::JavaManager;
357    ///
358    /// let manager = JavaManager::new();
359    /// assert_eq!(manager.len(), 0);
360    /// ```
361    pub fn len(&self) -> usize {
362        self.java_installations.len()
363    }
364
365    /// Checks if the manager has any Java installations.
366    ///
367    /// # Returns
368    ///
369    /// - `true` if there are no Java installations
370    /// - `false` if there is at least one Java installation
371    ///
372    /// # Examples
373    ///
374    /// ```rust
375    /// use java_manager::JavaManager;
376    ///
377    /// let manager = JavaManager::new();
378    /// assert!(manager.is_empty());
379    /// ```
380    pub fn is_empty(&self) -> bool {
381        self.java_installations.is_empty()
382    }
383
384    /// Filters Java installations by supplier/vendor.
385    ///
386    /// # Arguments
387    ///
388    /// * `supplier` - Supplier name to filter by (case-insensitive)
389    ///
390    /// # Returns
391    ///
392    /// Vector of references to Java installations from the specified supplier
393    ///
394    /// # Examples
395    ///
396    /// ```rust
397    /// use java_manager::JavaManager;
398    ///
399    /// let manager = JavaManager::new();
400    /// // Discover installations first...
401    /// // let openjdk_installations = manager.filter_by_supplier("OpenJDK");
402    /// ```
403    pub fn filter_by_supplier(&self, supplier: &str) -> Vec<&JavaInfo> {
404        let supplier_lower = supplier.to_lowercase();
405        self.java_installations
406            .iter()
407            .filter(|info| info.suppliers.to_lowercase().contains(&supplier_lower))
408            .collect()
409    }
410
411    /// Filters Java installations by architecture.
412    ///
413    /// # Arguments
414    ///
415    /// * `architecture` - Architecture to filter by (e.g., "64-bit", "32-bit")
416    ///
417    /// # Returns
418    ///
419    /// Vector of references to Java installations with the specified architecture
420    ///
421    /// # Examples
422    ///
423    /// ```rust
424    /// use java_manager::JavaManager;
425    ///
426    /// let manager = JavaManager::new();
427    /// // Discover installations first...
428    /// // let x64_installations = manager.filter_by_architecture("64-bit");
429    /// ```
430    pub fn filter_by_architecture(&self, architecture: &str) -> Vec<&JavaInfo> {
431        self.java_installations
432            .iter()
433            .filter(|info| info.architecture == architecture)
434            .collect()
435    }
436
437    /// Executes a Java command using the default Java installation.
438    ///
439    /// # Arguments
440    ///
441    /// * `args` - Command-line arguments to pass to Java
442    ///
443    /// # Returns
444    ///
445    /// - `Ok(String)` - Command output as a string
446    /// - `Err(std::io::Error)` - If the command fails to execute or no default Java is set
447    ///
448    /// # Examples
449    ///
450    /// ```rust
451    /// use java_manager::JavaManager;
452    ///
453    /// fn main() -> std::io::Result<()> {
454    ///     let mut manager = JavaManager::new();
455    ///     manager.discover_installations()?;
456    ///     
457    ///     let output = manager.execute_default(&["-version"])?;
458    ///     println!("Output:\n{}", output);
459    ///     Ok(())
460    /// }
461    /// ```
462    pub fn execute_default(&self, args: &[&str]) -> std::io::Result<String> {
463        self.get_default()
464            .ok_or_else(|| std::io::Error::new(
465                std::io::ErrorKind::NotFound,
466                "No default Java installation set"
467            ))?
468            .execute_with_output(args)
469    }
470
471    /// Executes a Java command using a specific Java version.
472    ///
473    /// # Arguments
474    ///
475    /// * `version` - Major version of Java to use
476    /// * `args` - Command-line arguments to pass to Java
477    ///
478    /// # Returns
479    ///
480    /// - `Ok(String)` - Command output as a string
481    /// - `Err(std::io::Error)` - If the command fails to execute or the version is not found
482    ///
483    /// # Examples
484    ///
485    /// ```rust
486    /// use java_manager::JavaManager;
487    ///
488    /// fn main() -> std::io::Result<()> {
489    ///     let mut manager = JavaManager::new();
490    ///     manager.discover_installations()?;
491    ///     
492    ///     let output = manager.execute_with_version(11, &["-version"])?;
493    ///     println!("Java 11 output:\n{}", output);
494    ///     Ok(())
495    /// }
496    /// ```
497    pub fn execute_with_version(&self, version: u32, args: &[&str]) -> std::io::Result<String> {
498        self.get_by_version(version)
499            .ok_or_else(|| std::io::Error::new(
500                std::io::ErrorKind::NotFound,
501                format!("Java version {} not found", version)
502            ))?
503            .execute_with_output(args)
504    }
505
506    /// Returns a summary of Java installations by version.
507    ///
508    /// # Returns
509    ///
510    /// HashMap mapping major versions to counts of installations
511    ///
512    /// # Examples
513    ///
514    /// ```rust
515    /// use java_manager::JavaManager;
516    ///
517    /// let manager = JavaManager::new();
518    /// // Discover installations first...
519    /// // let summary = manager.get_version_summary();
520    /// // for (version, count) in summary {
521    /// //     println!("Java {}: {} installations", version, count);
522    /// // }
523    /// ```
524    pub fn get_version_summary(&self) -> HashMap<u32, usize> {
525        let mut summary = HashMap::new();
526        
527        for info in &self.java_installations {
528            if let Some(version) = info.get_major_version() {
529                *summary.entry(version).or_insert(0) += 1;
530            }
531        }
532        
533        summary
534    }
535
536    /// Clears all Java installations from the manager.
537    ///
538    /// # Examples
539    ///
540    /// ```rust
541    /// use java_manager::JavaManager;
542    ///
543    /// let mut manager = JavaManager::new();
544    /// // Add some installations...
545    /// manager.clear();
546    /// assert!(manager.is_empty());
547    /// ```
548    pub fn clear(&mut self) {
549        self.java_installations.clear();
550        self.version_map.clear();
551        self.default_index = None;
552    }
553}
554
555impl Default for JavaManager {
556    /// Creates a new `JavaManager` with default settings.
557    ///
558    /// # Returns
559    ///
560    /// A new `JavaManager` instance
561    fn default() -> Self {
562        Self::new()
563    }
564}
565
566#[cfg(test)]
567mod tests {
568    use super::*;
569
570    /// Tests creating a new JavaManager
571    #[test]
572    fn test_new_manager() {
573        let manager = JavaManager::new();
574        assert!(manager.is_empty());
575        assert_eq!(manager.len(), 0);
576        assert!(manager.get_default().is_none());
577    }
578
579    /// Tests adding Java installations to the manager
580    #[test]
581    fn test_add_java() {
582        let mut manager = JavaManager::new();
583        
584        let java1 = JavaInfo::new("java", "/usr/bin/java1", "11.0.12", "64-bit", "OpenJDK");
585        let java2 = JavaInfo::new("java", "/usr/bin/java2", "1.8.0_312", "64-bit", "Oracle");
586        
587        manager.add(java1.clone());
588        manager.add(java2.clone());
589        
590        assert_eq!(manager.len(), 2);
591        assert!(!manager.is_empty());
592        
593        // First added should be default
594        assert!(manager.get_default().is_some());
595        assert_eq!(manager.get_default().unwrap().path, java1.path);
596    }
597
598    /// Tests getting Java installations by index
599    #[test]
600    fn test_get_by_index() {
601        let mut manager = JavaManager::new();
602        
603        let java1 = JavaInfo::new("java", "/usr/bin/java1", "11.0.12", "64-bit", "OpenJDK");
604        let java2 = JavaInfo::new("java", "/usr/bin/java2", "1.8.0_312", "64-bit", "Oracle");
605        
606        manager.add(java1);
607        manager.add(java2);
608        
609        assert!(manager.get(0).is_some());
610        assert!(manager.get(1).is_some());
611        assert!(manager.get(2).is_none());
612    }
613
614    /// Tests getting Java installations by version
615    #[test]
616    fn test_get_by_version() {
617        let mut manager = JavaManager::new();
618        
619        let java11 = JavaInfo::new("java", "/usr/bin/java11", "11.0.12", "64-bit", "OpenJDK");
620        let java8 = JavaInfo::new("java", "/usr/bin/java8", "1.8.0_312", "64-bit", "Oracle");
621        
622        manager.add(java11);
623        manager.add(java8);
624        
625        // Test getting Java 11
626        let java_11 = manager.get_by_version(11);
627        assert!(java_11.is_some());
628        assert_eq!(java_11.unwrap().get_major_version(), Some(11));
629        
630        // Test getting Java 8
631        let java_8 = manager.get_by_version(8);
632        assert!(java_8.is_some());
633        assert_eq!(java_8.unwrap().get_major_version(), Some(8));
634        
635        // Test getting non-existent version
636        let java_17 = manager.get_by_version(17);
637        assert!(java_17.is_none());
638    }
639
640    /// Tests getting all Java installations by version
641    #[test]
642    fn test_get_all_by_version() {
643        let mut manager = JavaManager::new();
644        
645        // Add multiple Java 11 installations
646        let java11_1 = JavaInfo::new("java", "/usr/bin/java11_1", "11.0.12", "64-bit", "OpenJDK");
647        let java11_2 = JavaInfo::new("java", "/usr/bin/java11_2", "11.0.13", "64-bit", "Oracle");
648        let java8 = JavaInfo::new("java", "/usr/bin/java8", "1.8.0_312", "64-bit", "OpenJDK");
649        
650        manager.add(java11_1);
651        manager.add(java11_2);
652        manager.add(java8);
653        
654        // Get all Java 11 installations
655        let java11_installations = manager.get_all_by_version(11);
656        assert_eq!(java11_installations.len(), 2);
657        
658        // Get all Java 8 installations
659        let java8_installations = manager.get_all_by_version(8);
660        assert_eq!(java8_installations.len(), 1);
661        
662        // Get all Java 17 installations (none)
663        let java17_installations = manager.get_all_by_version(17);
664        assert_eq!(java17_installations.len(), 0);
665    }
666
667    /// Tests setting and getting default Java installation
668    #[test]
669    fn test_set_default() {
670        let mut manager = JavaManager::new();
671        
672        let java1 = JavaInfo::new("java", "/usr/bin/java1", "11.0.12", "64-bit", "OpenJDK");
673        let java2 = JavaInfo::new("java", "/usr/bin/java2", "1.8.0_312", "64-bit", "Oracle");
674        
675        manager.add(java1.clone());
676        manager.add(java2.clone());
677        
678        // First added should be default
679        assert_eq!(manager.get_default().unwrap().path, java1.path);
680        
681        // Set second as default
682        assert!(manager.set_default(1));
683        assert_eq!(manager.get_default().unwrap().path, java2.path);
684        
685        // Try to set invalid index
686        assert!(!manager.set_default(5));
687        assert_eq!(manager.get_default().unwrap().path, java2.path); // Should remain unchanged
688    }
689
690    /// Tests setting default by version
691    #[test]
692    fn test_set_default_by_version() {
693        let mut manager = JavaManager::new();
694        
695        let java11 = JavaInfo::new("java", "/usr/bin/java11", "11.0.12", "64-bit", "OpenJDK");
696        let java8 = JavaInfo::new("java", "/usr/bin/java8", "1.8.0_312", "64-bit", "Oracle");
697        
698        manager.add(java11.clone());
699        manager.add(java8.clone());
700        
701        // Set default to Java 8
702        assert!(manager.set_default_by_version(8));
703        assert_eq!(manager.get_default().unwrap().path, java8.path);
704        
705        // Set default to Java 11
706        assert!(manager.set_default_by_version(11));
707        assert_eq!(manager.get_default().unwrap().path, java11.path);
708        
709        // Try to set non-existent version
710        assert!(!manager.set_default_by_version(17));
711        assert_eq!(manager.get_default().unwrap().path, java11.path); // Should remain unchanged
712    }
713
714    /// Tests filtering Java installations by supplier
715    #[test]
716    fn test_filter_by_supplier() {
717        let mut manager = JavaManager::new();
718        
719        let java1 = JavaInfo::new("java", "/usr/bin/java1", "11.0.12", "64-bit", "OpenJDK");
720        let java2 = JavaInfo::new("java", "/usr/bin/java2", "11.0.13", "64-bit", "Oracle");
721        let java3 = JavaInfo::new("java", "/usr/bin/java3", "1.8.0_312", "64-bit", "OpenJDK");
722        
723        manager.add(java1);
724        manager.add(java2);
725        manager.add(java3);
726        
727        // Filter by OpenJDK (case-insensitive)
728        let openjdk_installations = manager.filter_by_supplier("OpenJDK");
729        assert_eq!(openjdk_installations.len(), 2);
730        
731        // Filter by Oracle
732        let oracle_installations = manager.filter_by_supplier("Oracle");
733        assert_eq!(oracle_installations.len(), 1);
734        
735        // Filter by non-existent supplier
736        let ibm_installations = manager.filter_by_supplier("IBM");
737        assert_eq!(ibm_installations.len(), 0);
738    }
739
740    /// Tests filtering Java installations by architecture
741    #[test]
742    fn test_filter_by_architecture() {
743        let mut manager = JavaManager::new();
744        
745        let java64 = JavaInfo::new("java", "/usr/bin/java64", "11.0.12", "64-bit", "OpenJDK");
746        let java32 = JavaInfo::new("java", "/usr/bin/java32", "1.8.0_312", "32-bit", "Oracle");
747        let java64_2 = JavaInfo::new("java", "/usr/bin/java64_2", "17.0.1", "64-bit", "OpenJDK");
748        
749        manager.add(java64);
750        manager.add(java32);
751        manager.add(java64_2);
752        
753        // Filter 64-bit installations
754        let x64_installations = manager.filter_by_architecture("64-bit");
755        assert_eq!(x64_installations.len(), 2);
756        
757        // Filter 32-bit installations
758        let x86_installations = manager.filter_by_architecture("32-bit");
759        assert_eq!(x86_installations.len(), 1);
760        
761        // Filter non-existent architecture
762        let unknown_installations = manager.filter_by_architecture("Unknown");
763        assert_eq!(unknown_installations.len(), 0);
764    }
765
766    /// Tests getting version summary
767    #[test]
768    fn test_get_version_summary() {
769        let mut manager = JavaManager::new();
770        
771        // Add multiple versions
772        let java11_1 = JavaInfo::new("java", "/usr/bin/java11_1", "11.0.12", "64-bit", "OpenJDK");
773        let java11_2 = JavaInfo::new("java", "/usr/bin/java11_2", "11.0.13", "64-bit", "Oracle");
774        let java8 = JavaInfo::new("java", "/usr/bin/java8", "1.8.0_312", "64-bit", "OpenJDK");
775        let java17 = JavaInfo::new("java", "/usr/bin/java17", "17.0.1", "64-bit", "OpenJDK");
776        let java_invalid = JavaInfo::new("java", "/usr/bin/java_invalid", "invalid", "64-bit", "Unknown");
777        
778        manager.add(java11_1);
779        manager.add(java11_2);
780        manager.add(java8);
781        manager.add(java17);
782        manager.add(java_invalid);
783        
784        let summary = manager.get_version_summary();
785        
786        assert_eq!(summary.get(&11), Some(&2)); // Two Java 11 installations
787        assert_eq!(summary.get(&8), Some(&1));  // One Java 8 installation
788        assert_eq!(summary.get(&17), Some(&1)); // One Java 17 installation
789        assert_eq!(summary.get(&99), None);     // No Java 99 installations
790    }
791
792    /// Tests clearing all installations
793    #[test]
794    fn test_clear() {
795        let mut manager = JavaManager::new();
796        
797        let java1 = JavaInfo::new("java", "/usr/bin/java1", "11.0.12", "64-bit", "OpenJDK");
798        let java2 = JavaInfo::new("java", "/usr/bin/java2", "1.8.0_312", "64-bit", "Oracle");
799        
800        manager.add(java1);
801        manager.add(java2);
802        
803        assert_eq!(manager.len(), 2);
804        assert!(!manager.is_empty());
805        
806        manager.clear();
807        
808        assert_eq!(manager.len(), 0);
809        assert!(manager.is_empty());
810        assert!(manager.get_default().is_none());
811    }
812
813    /// Tests the list method
814    #[test]
815    fn test_list() {
816        let mut manager = JavaManager::new();
817        
818        let java1 = JavaInfo::new("java", "/usr/bin/java1", "11.0.12", "64-bit", "OpenJDK");
819        let java2 = JavaInfo::new("java", "/usr/bin/java2", "1.8.0_312", "64-bit", "Oracle");
820        
821        manager.add(java1);
822        manager.add(java2);
823        
824        let list = manager.list();
825        assert_eq!(list.len(), 2);
826        
827        // Verify we can iterate over the list
828        for java in list {
829            assert!(java.is_valid() || !std::path::Path::new(&java.path).exists());
830        }
831    }
832
833    /// Tests the Default trait implementation
834    #[test]
835    fn test_default() {
836        let manager = JavaManager::default();
837        assert!(manager.is_empty());
838        assert_eq!(manager.len(), 0);
839    }
840
841    /// Tests discovering installations (if Java is available)
842    #[test]
843    fn test_discover_installations() {
844        let mut manager = JavaManager::new();
845        let result = manager.discover_installations();
846        
847        // Discovery might succeed or fail depending on whether Java is installed
848        if result.is_ok() {
849            // If discovery succeeded, there should be at least one installation
850            // (unless the system has no Java at all)
851            println!("Discovered {} Java installations", manager.len());
852            
853            if !manager.is_empty() {
854                assert!(manager.get_default().is_some());
855                
856                // List all discovered installations
857                for (i, java) in manager.list().iter().enumerate() {
858                    println!("{}. {}", i + 1, java);
859                }
860            }
861        } else {
862            println!("Discovery failed (Java may not be installed)");
863        }
864    }
865}