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