xref: /aosp_15_r20/tools/platform-compat/build/process-compat-config-test.py (revision b7bfe76a3376ed14ad4000514e7f5aa4f0ee949d)
1*b7bfe76aSAndroid Build Coastguard Worker#!/usr/bin/env python3
2*b7bfe76aSAndroid Build Coastguard Worker#
3*b7bfe76aSAndroid Build Coastguard Worker# Copyright (C) 2019 The Android Open Source Project
4*b7bfe76aSAndroid Build Coastguard Worker#
5*b7bfe76aSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License");
6*b7bfe76aSAndroid Build Coastguard Worker# you may not use this file except in compliance with the License.
7*b7bfe76aSAndroid Build Coastguard Worker# You may obtain a copy of the License at
8*b7bfe76aSAndroid Build Coastguard Worker#
9*b7bfe76aSAndroid Build Coastguard Worker#      http://www.apache.org/licenses/LICENSE-2.0
10*b7bfe76aSAndroid Build Coastguard Worker#
11*b7bfe76aSAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software
12*b7bfe76aSAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS,
13*b7bfe76aSAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14*b7bfe76aSAndroid Build Coastguard Worker# See the License for the specific language governing permissions and
15*b7bfe76aSAndroid Build Coastguard Worker# limitations under the License.
16*b7bfe76aSAndroid Build Coastguard Worker#
17*b7bfe76aSAndroid Build Coastguard Worker"""Unit tests for process_compat_config.py."""
18*b7bfe76aSAndroid Build Coastguard Worker
19*b7bfe76aSAndroid Build Coastguard Workerimport difflib
20*b7bfe76aSAndroid Build Coastguard Workerimport io
21*b7bfe76aSAndroid Build Coastguard Workerimport unittest
22*b7bfe76aSAndroid Build Coastguard Workerimport xml.dom.minidom
23*b7bfe76aSAndroid Build Coastguard Workerfrom inspect import currentframe, getframeinfo
24*b7bfe76aSAndroid Build Coastguard Worker
25*b7bfe76aSAndroid Build Coastguard Workerimport process_compat_config
26*b7bfe76aSAndroid Build Coastguard Worker
27*b7bfe76aSAndroid Build Coastguard Workerdef here():
28*b7bfe76aSAndroid Build Coastguard Worker    f = currentframe().f_back
29*b7bfe76aSAndroid Build Coastguard Worker    return "%s:%d" % (getframeinfo(f).filename, f.f_lineno)
30*b7bfe76aSAndroid Build Coastguard Worker
31*b7bfe76aSAndroid Build Coastguard Workerclass ProcessCompatConfigTest(unittest.TestCase):
32*b7bfe76aSAndroid Build Coastguard Worker
33*b7bfe76aSAndroid Build Coastguard Worker    def setUp(self):
34*b7bfe76aSAndroid Build Coastguard Worker        self.merger = process_compat_config.ConfigMerger(detect_conflicts = True)
35*b7bfe76aSAndroid Build Coastguard Worker        self.stderr = io.StringIO()
36*b7bfe76aSAndroid Build Coastguard Worker        self.merger.write_errors_to = self.stderr
37*b7bfe76aSAndroid Build Coastguard Worker        self.xml = io.BytesIO()
38*b7bfe76aSAndroid Build Coastguard Worker
39*b7bfe76aSAndroid Build Coastguard Worker    def remove_white_space_text_nodes(self, node):
40*b7bfe76aSAndroid Build Coastguard Worker        remove = []
41*b7bfe76aSAndroid Build Coastguard Worker        # Find any child nodes that are just white space, and add them to a list
42*b7bfe76aSAndroid Build Coastguard Worker        # to remove. Do not remove the child while iterating as that prevents
43*b7bfe76aSAndroid Build Coastguard Worker        # the following node from being seen.
44*b7bfe76aSAndroid Build Coastguard Worker        for child in node.childNodes:
45*b7bfe76aSAndroid Build Coastguard Worker            if child.nodeType == node.ELEMENT_NODE:
46*b7bfe76aSAndroid Build Coastguard Worker                self.remove_white_space_text_nodes(child)
47*b7bfe76aSAndroid Build Coastguard Worker            elif child.nodeType == node.TEXT_NODE:
48*b7bfe76aSAndroid Build Coastguard Worker                if str.isspace(child.data):
49*b7bfe76aSAndroid Build Coastguard Worker                    remove.append(child)
50*b7bfe76aSAndroid Build Coastguard Worker        # Remove any child nodes that were just white space.
51*b7bfe76aSAndroid Build Coastguard Worker        for child in remove:
52*b7bfe76aSAndroid Build Coastguard Worker            node.removeChild(child)
53*b7bfe76aSAndroid Build Coastguard Worker            child.unlink()
54*b7bfe76aSAndroid Build Coastguard Worker
55*b7bfe76aSAndroid Build Coastguard Worker    def parse_xml(self, text):
56*b7bfe76aSAndroid Build Coastguard Worker        node = xml.dom.minidom.parseString(text)
57*b7bfe76aSAndroid Build Coastguard Worker        # Remove any white space text nodes as they are irrelevant.
58*b7bfe76aSAndroid Build Coastguard Worker        self.remove_white_space_text_nodes(node)
59*b7bfe76aSAndroid Build Coastguard Worker        return node.toprettyxml()
60*b7bfe76aSAndroid Build Coastguard Worker
61*b7bfe76aSAndroid Build Coastguard Worker    def assert_same_xml(self, got, expected):
62*b7bfe76aSAndroid Build Coastguard Worker        got = self.parse_xml(got)
63*b7bfe76aSAndroid Build Coastguard Worker        expected = self.parse_xml(expected)
64*b7bfe76aSAndroid Build Coastguard Worker        diffs = [diff for diff in difflib.ndiff(got.split('\n'), expected.split('\n')) if not diff.startswith(" ")]
65*b7bfe76aSAndroid Build Coastguard Worker        self.assertEqual("", "\n".join(diffs), msg="Got unexpected diffs in XML")
66*b7bfe76aSAndroid Build Coastguard Worker
67*b7bfe76aSAndroid Build Coastguard Worker    def test_no_config_to_merge(self):
68*b7bfe76aSAndroid Build Coastguard Worker        self.merger.write(self.xml)
69*b7bfe76aSAndroid Build Coastguard Worker        self.assert_same_xml(self.xml.getvalue(), "<config />")
70*b7bfe76aSAndroid Build Coastguard Worker
71*b7bfe76aSAndroid Build Coastguard Worker    def test_merge_one_file(self):
72*b7bfe76aSAndroid Build Coastguard Worker        self.merger.merge(io.BytesIO(b'<config><compat-change id="1234" name="TEST_CHANGE" /></config>'), here())
73*b7bfe76aSAndroid Build Coastguard Worker        self.merger.write(self.xml)
74*b7bfe76aSAndroid Build Coastguard Worker        self.assert_same_xml(self.xml.getvalue(), '<config><compat-change id="1234" name="TEST_CHANGE" /></config>')
75*b7bfe76aSAndroid Build Coastguard Worker
76*b7bfe76aSAndroid Build Coastguard Worker    def test_merge_two_files(self):
77*b7bfe76aSAndroid Build Coastguard Worker        self.merger.merge(io.BytesIO(b'<config><compat-change id="1234" name="TEST_CHANGE" /></config>'), here())
78*b7bfe76aSAndroid Build Coastguard Worker        self.merger.merge(io.BytesIO(b'<config><compat-change id="1235" name="TEST_CHANGE2" /></config>'), here())
79*b7bfe76aSAndroid Build Coastguard Worker        self.merger.write(self.xml)
80*b7bfe76aSAndroid Build Coastguard Worker        self.assert_same_xml(self.xml.getvalue(),
81*b7bfe76aSAndroid Build Coastguard Worker            '<config><compat-change id="1234" name="TEST_CHANGE" /><compat-change id="1235" name="TEST_CHANGE2" /></config>')
82*b7bfe76aSAndroid Build Coastguard Worker
83*b7bfe76aSAndroid Build Coastguard Worker    def test_merge_two_files_metadata(self):
84*b7bfe76aSAndroid Build Coastguard Worker        self.merger.merge(io.BytesIO(
85*b7bfe76aSAndroid Build Coastguard Worker            b'<config><compat-change id="1234" name="TEST_CHANGE"><meta-data definedIn="some.Class" sourcePosition="some.java:1" />'
86*b7bfe76aSAndroid Build Coastguard Worker            b'</compat-change></config>'), here())
87*b7bfe76aSAndroid Build Coastguard Worker        self.merger.merge(io.BytesIO(
88*b7bfe76aSAndroid Build Coastguard Worker            b'<config><compat-change id="1235" name="TEST_CHANGE2"><meta-data definedIn="other.Class" sourcePosition="other.java:2" />'
89*b7bfe76aSAndroid Build Coastguard Worker            b'</compat-change></config>'), here())
90*b7bfe76aSAndroid Build Coastguard Worker        self.merger.write(self.xml)
91*b7bfe76aSAndroid Build Coastguard Worker        self.assert_same_xml(self.xml.getvalue(), b'<config>'
92*b7bfe76aSAndroid Build Coastguard Worker            b'<compat-change id="1234" name="TEST_CHANGE"><meta-data definedIn="some.Class" sourcePosition="some.java:1" /></compat-change>'
93*b7bfe76aSAndroid Build Coastguard Worker            b'<compat-change id="1235" name="TEST_CHANGE2"><meta-data definedIn="other.Class" sourcePosition="other.java:2" /></compat-change>'
94*b7bfe76aSAndroid Build Coastguard Worker            b'</config>')
95*b7bfe76aSAndroid Build Coastguard Worker
96*b7bfe76aSAndroid Build Coastguard Worker    def test_write_device_config_metadata_stripped(self):
97*b7bfe76aSAndroid Build Coastguard Worker        self.merger.merge(io.BytesIO(
98*b7bfe76aSAndroid Build Coastguard Worker            b'<config><compat-change id="1234" name="TEST_CHANGE"><meta-data definedIn="some.Class" sourcePosition="file.java:1"/>'
99*b7bfe76aSAndroid Build Coastguard Worker            b'</compat-change></config>'), here())
100*b7bfe76aSAndroid Build Coastguard Worker        self.merger.write_device_config(self.xml)
101*b7bfe76aSAndroid Build Coastguard Worker        self.assert_same_xml(self.xml.getvalue(), b'<config>'
102*b7bfe76aSAndroid Build Coastguard Worker            b'<compat-change id="1234" name="TEST_CHANGE" />'
103*b7bfe76aSAndroid Build Coastguard Worker            b'</config>')
104*b7bfe76aSAndroid Build Coastguard Worker
105*b7bfe76aSAndroid Build Coastguard Worker    def test_merge_two_files_duplicate_id(self):
106*b7bfe76aSAndroid Build Coastguard Worker        self.merger.merge(io.BytesIO(b'<config><compat-change id="1234" name="TEST_CHANGE" /></config>'), here())
107*b7bfe76aSAndroid Build Coastguard Worker        self.merger.merge(io.BytesIO(b'<config><compat-change id="1234" name="TEST_CHANGE2" /></config>'), here())
108*b7bfe76aSAndroid Build Coastguard Worker        self.assertIn(r'ERROR: Duplicate definitions for compat change with ID 1234', self.stderr.getvalue())
109*b7bfe76aSAndroid Build Coastguard Worker        with self.assertRaisesRegex(Exception, ' 1 .*error'):
110*b7bfe76aSAndroid Build Coastguard Worker            self.merger.write(self.xml)
111*b7bfe76aSAndroid Build Coastguard Worker
112*b7bfe76aSAndroid Build Coastguard Worker    def test_merge_two_files_duplicate_name(self):
113*b7bfe76aSAndroid Build Coastguard Worker        self.merger.merge(io.BytesIO(b'<config><compat-change id="1234" name="TEST_CHANGE" /></config>'), here())
114*b7bfe76aSAndroid Build Coastguard Worker        self.merger.merge(io.BytesIO(b'<config><compat-change id="1235" name="TEST_CHANGE" /></config>'), here())
115*b7bfe76aSAndroid Build Coastguard Worker        self.assertIn(r'ERROR: Duplicate definitions for compat change with name TEST_CHANGE', self.stderr.getvalue())
116*b7bfe76aSAndroid Build Coastguard Worker        with self.assertRaisesRegex(Exception, ' 1 .*error'):
117*b7bfe76aSAndroid Build Coastguard Worker            self.merger.write(self.xml)
118*b7bfe76aSAndroid Build Coastguard Worker
119*b7bfe76aSAndroid Build Coastguard Worker    def test_merge_two_files_duplicate_id_allow_duplicates(self):
120*b7bfe76aSAndroid Build Coastguard Worker        self.merger = process_compat_config.ConfigMerger(detect_conflicts = False)
121*b7bfe76aSAndroid Build Coastguard Worker        self.merger.merge(io.BytesIO(b'<config><compat-change id="1234" name="TEST_CHANGE" /></config>'), here())
122*b7bfe76aSAndroid Build Coastguard Worker        self.merger.merge(io.BytesIO(b'<config><compat-change id="1234" name="TEST_CHANGE2" /></config>'), here())
123*b7bfe76aSAndroid Build Coastguard Worker        self.merger.write(self.xml)
124*b7bfe76aSAndroid Build Coastguard Worker
125*b7bfe76aSAndroid Build Coastguard Worker    def test_merge_two_files_duplicate_name_allow_duplicates(self):
126*b7bfe76aSAndroid Build Coastguard Worker        self.merger = process_compat_config.ConfigMerger(detect_conflicts = False)
127*b7bfe76aSAndroid Build Coastguard Worker        self.merger.merge(io.BytesIO(b'<config><compat-change id="1234" name="TEST_CHANGE" /></config>'), here())
128*b7bfe76aSAndroid Build Coastguard Worker        self.merger.merge(io.BytesIO(b'<config><compat-change id="1235" name="TEST_CHANGE" /></config>'), here())
129*b7bfe76aSAndroid Build Coastguard Worker        self.merger.write(self.xml)
130*b7bfe76aSAndroid Build Coastguard Worker
131*b7bfe76aSAndroid Build Coastguard Workerif __name__ == '__main__':
132*b7bfe76aSAndroid Build Coastguard Worker  unittest.main(verbosity=2)
133