• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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