1 /* 2 * Copyright (C) 2024 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #[cfg(test)] 18 mod tests { 19 use alloctop::*; 20 use std::io::Write; 21 use tempfile::NamedTempFile; 22 23 /// Tests the handling of missing input files. 24 /// 25 /// This test verifies that the `run` function correctly handles cases where 26 /// the input file specified does not exist. It ensures that an error is returned 27 /// and that the error message indicates an issue with file reading. 28 #[test] test_missing_file_handling()29 fn test_missing_file_handling() { 30 // Define a non-existent file path 31 let non_existent_file = "/this/file/should/not/exist.txt"; 32 33 // Attempt to run the alloctop logic with the non-existent file 34 let result = run( 35 usize::MAX, // max_lines (not relevant for this test) 36 Some(SortBy::Size), // sort_by (not relevant for this test) 37 0, // min_size (not relevant for this test) 38 false, // use_tree (not relevant for this test) 39 non_existent_file, 40 ); 41 42 // Assert that the result is an error 43 assert!(result.is_err()); 44 45 // Optionally, check the error message to ensure it's related to file access 46 if let Err(msg) = result { 47 assert!( 48 msg.contains("Error reading or parsing allocinfo"), 49 "Error message should indicate an issue with file reading" 50 ); 51 } 52 } 53 54 /// Tests parsing an empty allocinfo file. 55 /// 56 /// This test verifies that the `parse_allocinfo` function correctly handles 57 /// an empty input file. It ensures that an empty vector of `AllocInfo` is returned. 58 #[test] test_parse_allocinfo_empty_file()59 fn test_parse_allocinfo_empty_file() { 60 let mut temp_file = NamedTempFile::new().unwrap(); 61 writeln!(temp_file).unwrap(); 62 63 let result = parse_allocinfo(temp_file.path().to_str().unwrap()).unwrap(); 64 65 assert_eq!(result.len(), 0); 66 } 67 68 /// Tests parsing a valid allocinfo file. 69 /// 70 /// This test verifies that the `parse_allocinfo` function correctly parses 71 /// a valid allocinfo file with multiple valid lines. It checks that the correct 72 /// number of `AllocInfo` entries are parsed and that the values within each entry 73 /// are correct. 74 #[test] test_parse_allocinfo_valid_file()75 fn test_parse_allocinfo_valid_file() { 76 let mut temp_file = NamedTempFile::new().unwrap(); 77 writeln!( 78 temp_file, 79 "allocinfo - version: 1.0 80 # <size> <calls> <tag info> 81 1024 5 /some/tag/path 82 2048 2 /another/tag 83 512 10 /third/tag/here" 84 ) 85 .unwrap(); 86 87 let result = parse_allocinfo(temp_file.path().to_str().unwrap()).unwrap(); 88 89 assert_eq!(result.len(), 3); 90 assert_eq!( 91 result[0], 92 AllocInfo { size: 1024, calls: 5, tag: "/some/tag/path".to_string() } 93 ); 94 assert_eq!(result[1], AllocInfo { size: 2048, calls: 2, tag: "/another/tag".to_string() }); 95 assert_eq!( 96 result[2], 97 AllocInfo { size: 512, calls: 10, tag: "/third/tag/here".to_string() } 98 ); 99 } 100 101 /// Tests parsing an allocinfo file with invalid lines. 102 /// 103 /// This test verifies that the `parse_allocinfo` function correctly handles 104 /// invalid lines within the input file. It checks that valid lines are parsed 105 /// correctly and invalid lines are skipped, with potentially incomplete data if possible. 106 #[test] test_parse_allocinfo_invalid_lines()107 fn test_parse_allocinfo_invalid_lines() { 108 let mut temp_file = NamedTempFile::new().unwrap(); 109 writeln!( 110 temp_file, 111 "allocinfo - version: 1.0 112 # <size> <calls> <tag info> 113 1024 5 /some/tag/path 114 invalid line 115 512 abc /third/tag/here" 116 ) 117 .unwrap(); 118 119 let result = parse_allocinfo(temp_file.path().to_str().unwrap()).unwrap(); 120 121 assert_eq!(result.len(), 2); 122 assert_eq!( 123 result[0], 124 AllocInfo { size: 1024, calls: 5, tag: "/some/tag/path".to_string() } 125 ); 126 assert_eq!( 127 result[1], 128 AllocInfo { size: 512, calls: 0, tag: "/third/tag/here".to_string() } 129 ); 130 } 131 132 /// Tests parsing a missing allocinfo file. 133 /// 134 /// This test verifies that the `parse_allocinfo` function correctly handles 135 /// cases where the input file does not exist. It ensures that an error is returned. 136 #[test] test_parse_allocinfo_missing_file()137 fn test_parse_allocinfo_missing_file() { 138 let result = parse_allocinfo("nonexistent_file.txt"); 139 assert!(result.is_err()); 140 } 141 142 /// Tests parsing an allocinfo file with comments. 143 /// 144 /// This test verifies that the `parse_allocinfo` function correctly handles 145 /// comment lines within the input file. It ensures that comment lines are ignored 146 /// and valid lines are parsed correctly. 147 #[test] test_parse_allocinfo_comments()148 fn test_parse_allocinfo_comments() { 149 let mut temp_file = NamedTempFile::new().unwrap(); 150 writeln!( 151 temp_file, 152 "allocinfo - version: 1.0 153 # <size> <calls> <tag info># This is a comment 154 1024 5 /some/tag/path 155 # Another comment 156 512 10 /third/tag/here" 157 ) 158 .unwrap(); 159 160 let result = parse_allocinfo(temp_file.path().to_str().unwrap()).unwrap(); 161 162 assert_eq!(result.len(), 2); 163 assert_eq!( 164 result[0], 165 AllocInfo { size: 1024, calls: 5, tag: "/some/tag/path".to_string() } 166 ); 167 assert_eq!( 168 result[1], 169 AllocInfo { size: 512, calls: 10, tag: "/third/tag/here".to_string() } 170 ); 171 } 172 173 /// Tests sorting allocinfo data by size. 174 /// 175 /// This test verifies that the `sort_allocinfo` function correctly sorts 176 /// a vector of `AllocInfo` entries by size in descending order. 177 #[test] test_sort_allocinfo_by_size()178 fn test_sort_allocinfo_by_size() { 179 let mut data = vec![ 180 AllocInfo { size: 1024, calls: 5, tag: "/tag1".to_string() }, 181 AllocInfo { size: 512, calls: 10, tag: "/tag2".to_string() }, 182 AllocInfo { size: 2048, calls: 2, tag: "/tag3".to_string() }, 183 ]; 184 185 sort_allocinfo(&mut data, SortBy::Size); 186 187 assert_eq!(data[0].size, 2048); 188 assert_eq!(data[1].size, 1024); 189 assert_eq!(data[2].size, 512); 190 } 191 192 /// Tests sorting allocinfo data by number of calls. 193 /// 194 /// This test verifies that the `sort_allocinfo` function correctly sorts 195 /// a vector of `AllocInfo` entries by the number of calls in descending order. 196 #[test] test_sort_allocinfo_by_calls()197 fn test_sort_allocinfo_by_calls() { 198 let mut data = vec![ 199 AllocInfo { size: 1024, calls: 5, tag: "/tag1".to_string() }, 200 AllocInfo { size: 512, calls: 10, tag: "/tag2".to_string() }, 201 AllocInfo { size: 2048, calls: 2, tag: "/tag3".to_string() }, 202 ]; 203 204 sort_allocinfo(&mut data, SortBy::Calls); 205 206 assert_eq!(data[0].calls, 10); 207 assert_eq!(data[1].calls, 5); 208 assert_eq!(data[2].calls, 2); 209 } 210 211 /// Tests sorting allocinfo data by tag. 212 /// 213 /// This test verifies that the `sort_allocinfo` function correctly sorts 214 /// a vector of `AllocInfo` entries by tag in ascending order. 215 #[test] test_sort_allocinfo_by_tag()216 fn test_sort_allocinfo_by_tag() { 217 let mut data = vec![ 218 AllocInfo { size: 1024, calls: 5, tag: "/tag2".to_string() }, 219 AllocInfo { size: 512, calls: 10, tag: "/tag3".to_string() }, 220 AllocInfo { size: 2048, calls: 2, tag: "/tag1".to_string() }, 221 ]; 222 223 sort_allocinfo(&mut data, SortBy::Tag); 224 225 assert_eq!(data[0].tag, "/tag1"); 226 assert_eq!(data[1].tag, "/tag2"); 227 assert_eq!(data[2].tag, "/tag3"); 228 } 229 230 /// Tests aggregating allocinfo data into a tree structure. 231 /// 232 /// This test verifies that the `aggregate_tree` function correctly aggregates 233 /// allocation data into a tree structure based on the tags. It checks that the 234 /// total size and calls are correctly summed for each node in the tree. 235 #[test] test_aggregate_tree()236 fn test_aggregate_tree() { 237 let data = vec![ 238 AllocInfo { size: 100, calls: 10, tag: "/A/B/C".to_string() }, 239 AllocInfo { size: 200, calls: 20, tag: "/A/B".to_string() }, 240 AllocInfo { size: 300, calls: 30, tag: "/A/X".to_string() }, 241 AllocInfo { size: 50, calls: 5, tag: "/A/B/C".to_string() }, 242 ]; 243 244 let result = aggregate_tree(&data); 245 246 assert_eq!(result.get("/A"), Some(&(650, 65))); 247 assert_eq!(result.get("/A/B"), Some(&(350, 35))); 248 assert_eq!(result.get("/A/X"), Some(&(300, 30))); 249 assert_eq!(result.get("/A/B/C"), Some(&(150, 15))); 250 assert_eq!(result.get("/A/Y"), None); 251 } 252 253 /// Tests aggregating allocinfo data globally. 254 /// 255 /// This test verifies that the `aggregate_global` function correctly aggregates 256 /// allocation data into a single `AllocInfo` entry representing the total size 257 /// and number of calls across all entries. 258 #[test] test_aggregate_global()259 fn test_aggregate_global() { 260 let data = vec![ 261 AllocInfo { size: 100, calls: 10, tag: "/A/B/C".to_string() }, 262 AllocInfo { size: 200, calls: 20, tag: "/A/B".to_string() }, 263 AllocInfo { size: 300, calls: 30, tag: "/A/X".to_string() }, 264 ]; 265 266 let result = aggregate_global(&data); 267 268 assert_eq!(result.size, 600); 269 assert_eq!(result.calls, 60); 270 } 271 } 272