xref: /aosp_15_r20/external/toolchain-utils/afdo_redaction/redact_profile_test.py (revision 760c253c1ed00ce9abd48f8546f08516e57485fe)
1#!/usr/bin/env python3
2# -*- coding: utf-8 -*-
3# Copyright 2018 The ChromiumOS Authors
4# Use of this source code is governed by a BSD-style license that can be
5# found in the LICENSE file.
6
7"""Tests for redact_profile.py."""
8
9
10import io
11import unittest
12
13from afdo_redaction import redact_profile
14
15
16_redact_limit = redact_profile.dedup_records.__defaults__[0]
17
18
19def _redact(input_lines, summary_to=None):
20    if isinstance(input_lines, str):
21        input_lines = input_lines.splitlines()
22
23    if summary_to is None:
24        summary_to = io.StringIO()
25
26    output_to = io.StringIO()
27    redact_profile.run(
28        profile_input_file=input_lines,
29        summary_output_file=summary_to,
30        profile_output_file=output_to,
31    )
32    return output_to.getvalue()
33
34
35def _redact_with_summary(input_lines):
36    summary = io.StringIO()
37    result = _redact(input_lines, summary_to=summary)
38    return result, summary.getvalue()
39
40
41def _generate_repeated_function_body(repeats, fn_name="_some_name"):
42    # Arbitrary function body ripped from a textual AFDO profile.
43    function_header = fn_name + ":1234:185"
44    function_body = [
45        " 6: 83",
46        " 15: 126",
47        " 62832: 126",
48        " 6: _ZNK5blink10PaintLayer14GroupedMappingEv:2349",
49        "  1: 206",
50        "  1: _ZNK5blink10PaintLayer14GroupedMappersEv:2060",
51        "   1: 206",
52        " 11: _ZNK5blink10PaintLayer25GetCompositedLayerMappingEv:800",
53        "  2.1: 80",
54    ]
55
56    # Be sure to zfill this, so the functions are output in sorted order.
57    num_width = len(str(repeats))
58
59    lines = []
60    for i in range(repeats):
61        num = str(i).zfill(num_width)
62        lines.append(num + function_header)
63        lines.extend(function_body)
64    return lines
65
66
67class Tests(unittest.TestCase):
68    """All of our tests for redact_profile."""
69
70    def test_no_input_works(self):
71        self.assertEqual(_redact(""), "")
72
73    def test_single_function_works(self):
74        lines = _generate_repeated_function_body(1)
75        result_file = "\n".join(lines) + "\n"
76        self.assertEqual(_redact(lines), result_file)
77
78    def test_duplicate_of_single_function_works(self):
79        lines = _generate_repeated_function_body(2)
80        result_file = "\n".join(lines) + "\n"
81        self.assertEqual(_redact(lines), result_file)
82
83    def test_not_too_many_duplicates_of_single_function_redacts_none(self):
84        lines = _generate_repeated_function_body(_redact_limit - 1)
85        result_file = "\n".join(lines) + "\n"
86        self.assertEqual(_redact(lines), result_file)
87
88    def test_many_duplicates_of_single_function_redacts_them_all(self):
89        lines = _generate_repeated_function_body(_redact_limit)
90        self.assertEqual(_redact(lines), "")
91
92    def test_many_duplicates_of_single_function_leaves_other_functions(self):
93        kept_lines = _generate_repeated_function_body(1, fn_name="_keep_me")
94        # Something to distinguish us from the rest. Just bump a random counter.
95        kept_lines[1] += "1"
96
97        result_file = "\n".join(kept_lines) + "\n"
98
99        lines = _generate_repeated_function_body(
100            _redact_limit, fn_name="_discard_me"
101        )
102        self.assertEqual(_redact(kept_lines + lines), result_file)
103        self.assertEqual(_redact(lines + kept_lines), result_file)
104
105        more_lines = _generate_repeated_function_body(
106            _redact_limit, fn_name="_and_discard_me"
107        )
108        self.assertEqual(_redact(lines + kept_lines + more_lines), result_file)
109        self.assertEqual(_redact(lines + more_lines), "")
110
111    def test_correct_summary_is_printed_when_nothing_is_redacted(self):
112        lines = _generate_repeated_function_body(1)
113        _, summary = _redact_with_summary(lines)
114        self.assertIn("Retained 1/1 functions", summary)
115        self.assertIn("Retained 827/827 samples, total", summary)
116        # Note that top-level samples == "samples without inlining taken into
117        # account," not "sum(entry_counts)"
118        self.assertIn("Retained 335/335 top-level samples", summary)
119
120    def test_correct_summary_is_printed_when_everything_is_redacted(self):
121        lines = _generate_repeated_function_body(_redact_limit)
122        _, summary = _redact_with_summary(lines)
123        self.assertIn("Retained 0/100 functions", summary)
124        self.assertIn("Retained 0/82,700 samples, total", summary)
125        self.assertIn("Retained 0/33,500 top-level samples", summary)
126
127    def test_correct_summary_is_printed_when_most_everything_is_redacted(self):
128        kept_lines = _generate_repeated_function_body(1, fn_name="_keep_me")
129        kept_lines[1] += "1"
130
131        lines = _generate_repeated_function_body(_redact_limit)
132        _, summary = _redact_with_summary(kept_lines + lines)
133        self.assertIn("Retained 1/101 functions", summary)
134        self.assertIn("Retained 1,575/84,275 samples, total", summary)
135        self.assertIn("Retained 1,083/34,583 top-level samples", summary)
136
137
138if __name__ == "__main__":
139    unittest.main()
140