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