| #!/usr/bin/env python3 |
| # -*- coding: utf-8 -*- |
| # Copyright 2020 The Chromium OS Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| """Tests for remove_cold_functions.""" |
| |
| from __future__ import print_function |
| |
| import io |
| from unittest.mock import patch |
| import unittest |
| |
| from afdo_redaction import remove_cold_functions |
| |
| |
| def _construct_profile(indices=None): |
| real_world_profile_functions = [ |
| """SomeFunction1:24150:300 |
| 2: 75 |
| 3: 23850 |
| 39: 225 |
| """, |
| """SomeFunction2:8925:225 |
| 0: 225 |
| 0.2: 150 |
| 0.1: SomeFunction2:6300 |
| 3: 6300 |
| 0.2: SomeFunction2:150 |
| 3: 75 |
| """, |
| """SomeFunction3:7500:75 |
| 0: 75 |
| 0.2: 75 |
| 0.1: SomeFunction3:6600 |
| 1: 6600 |
| 0.2: SomeFunction3:75 |
| 1: 75 |
| """, |
| """LargerFunction4:51450:0 |
| 1: 0 |
| 3: 0 |
| 3.1: 7350 |
| 4: 7350 |
| 7: 7350 |
| 8: 7350 |
| 9: 7350 |
| 12: 0 |
| 15: 0 |
| 13: AnotherFunction5:0 |
| 3: 0 |
| 3.1: 0 |
| 3.2: 0 |
| 4: 0 |
| 5: 0 |
| 6: 0 |
| 7: 0 |
| 8: 0 |
| 9: 0 |
| """, |
| """SomeFakeFunction5:7500:75 |
| 0: 75 |
| 0.2: 75 |
| 0.1: SomeFakeFunction5:6600 |
| 1: 6600 |
| 0.2: SomeFakeFunction5:75 |
| 1: 75 |
| """, |
| ] |
| |
| ret = [] |
| if not indices: |
| for x in real_world_profile_functions: |
| ret += x.strip().splitlines() |
| return ret |
| |
| ret = [] |
| for i in indices: |
| ret += real_world_profile_functions[i].strip().splitlines() |
| return ret |
| |
| |
| def _run_test(input_lines, goal, cwp_file=None, benchmark_file=None): |
| input_buf = io.StringIO('\n'.join(input_lines)) |
| output_buf = io.StringIO() |
| remove_cold_functions.run(input_buf, output_buf, goal, cwp_file, |
| benchmark_file) |
| return output_buf.getvalue().splitlines() |
| |
| |
| class Test(unittest.TestCase): |
| """Test functions in remove_cold_functions.py""" |
| |
| def test_empty_profile(self): |
| self.assertEqual(_run_test([], 0), []) |
| |
| def test_remove_all_functions_fail(self): |
| input_profile_lines = _construct_profile() |
| with self.assertRaises(Exception) as context: |
| _run_test(input_profile_lines, 0) |
| self.assertEqual( |
| str(context.exception), |
| "It's invalid to remove all functions in the profile") |
| |
| def test_remove_cold_functions_work(self): |
| input_profile_lines = _construct_profile() |
| # To make sure the cold functions are removed in order |
| expected_profile_lines = { |
| 5: input_profile_lines, |
| # Entry 4 wins the tie breaker because the name is smaller |
| # alphabetically. |
| 4: _construct_profile([0, 1, 3, 4]), |
| 3: _construct_profile([0, 1, 3]), |
| 2: _construct_profile([0, 3]), |
| 1: _construct_profile([3]), |
| } |
| |
| for num in expected_profile_lines: |
| self.assertCountEqual( |
| _run_test(input_profile_lines, num), expected_profile_lines[num]) |
| |
| def test_analyze_cwp_and_benchmark_work(self): |
| input_profile_lines = _construct_profile() |
| cwp_profile = _construct_profile([0, 1, 3, 4]) |
| benchmark_profile = _construct_profile([1, 2, 3, 4]) |
| cwp_buf = io.StringIO('\n'.join(cwp_profile)) |
| benchmark_buf = io.StringIO('\n'.join(benchmark_profile)) |
| with patch('sys.stderr', new=io.StringIO()) as fake_output: |
| _run_test(input_profile_lines, 3, cwp_buf, benchmark_buf) |
| |
| output = fake_output.getvalue() |
| self.assertIn('Retained 3/5 (60.0%) functions in the profile', output) |
| self.assertIn( |
| 'Retained 1/1 (100.0%) functions only appear in the CWP profile', |
| output) |
| self.assertIn( |
| 'Retained 0/1 (0.0%) functions only appear in the benchmark profile', |
| output) |
| self.assertIn( |
| 'Retained 2/3 (66.7%) functions appear in both CWP and benchmark' |
| ' profiles', output) |
| |
| |
| if __name__ == '__main__': |
| unittest.main() |