1*9e94795aSAndroid Build Coastguard Worker#!/usr/bin/env python3 2*9e94795aSAndroid Build Coastguard Worker# 3*9e94795aSAndroid Build Coastguard Worker# Copyright (C) 2020 The Android Open Source Project 4*9e94795aSAndroid Build Coastguard Worker# 5*9e94795aSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License"); 6*9e94795aSAndroid Build Coastguard Worker# you may not use this file except in compliance with the License. 7*9e94795aSAndroid Build Coastguard Worker# You may obtain a copy of the License at 8*9e94795aSAndroid Build Coastguard Worker# 9*9e94795aSAndroid Build Coastguard Worker# http://www.apache.org/licenses/LICENSE-2.0 10*9e94795aSAndroid Build Coastguard Worker# 11*9e94795aSAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software 12*9e94795aSAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS, 13*9e94795aSAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14*9e94795aSAndroid Build Coastguard Worker# See the License for the specific language governing permissions and 15*9e94795aSAndroid Build Coastguard Worker# limitations under the License. 16*9e94795aSAndroid Build Coastguard Worker 17*9e94795aSAndroid Build Coastguard Workerimport contextlib 18*9e94795aSAndroid Build Coastguard Workerimport io 19*9e94795aSAndroid Build Coastguard Workerimport unittest 20*9e94795aSAndroid Build Coastguard Worker 21*9e94795aSAndroid Build Coastguard Workerfrom unittest.mock import * 22*9e94795aSAndroid Build Coastguard Workerfrom post_process_props import * 23*9e94795aSAndroid Build Coastguard Worker 24*9e94795aSAndroid Build Coastguard Workerclass PropTestCase(unittest.TestCase): 25*9e94795aSAndroid Build Coastguard Worker def test_createFromLine(self): 26*9e94795aSAndroid Build Coastguard Worker p = Prop.from_line("# this is comment") 27*9e94795aSAndroid Build Coastguard Worker self.assertTrue(p.is_comment()) 28*9e94795aSAndroid Build Coastguard Worker self.assertEqual("", p.name) 29*9e94795aSAndroid Build Coastguard Worker self.assertEqual("", p.value) 30*9e94795aSAndroid Build Coastguard Worker self.assertFalse(p.is_optional()) 31*9e94795aSAndroid Build Coastguard Worker self.assertEqual("# this is comment", str(p)) 32*9e94795aSAndroid Build Coastguard Worker 33*9e94795aSAndroid Build Coastguard Worker for line in ["a=b", "a = b", "a= b", "a =b", " a=b "]: 34*9e94795aSAndroid Build Coastguard Worker p = Prop.from_line(line) 35*9e94795aSAndroid Build Coastguard Worker self.assertFalse(p.is_comment()) 36*9e94795aSAndroid Build Coastguard Worker self.assertEqual("a", p.name) 37*9e94795aSAndroid Build Coastguard Worker self.assertEqual("b", p.value) 38*9e94795aSAndroid Build Coastguard Worker self.assertFalse(p.is_optional()) 39*9e94795aSAndroid Build Coastguard Worker self.assertEqual("a=b", str(p)) 40*9e94795aSAndroid Build Coastguard Worker 41*9e94795aSAndroid Build Coastguard Worker for line in ["a?=b", "a ?= b", "a?= b", "a ?=b", " a?=b "]: 42*9e94795aSAndroid Build Coastguard Worker p = Prop.from_line(line) 43*9e94795aSAndroid Build Coastguard Worker self.assertFalse(p.is_comment()) 44*9e94795aSAndroid Build Coastguard Worker self.assertEqual("a", p.name) 45*9e94795aSAndroid Build Coastguard Worker self.assertEqual("b", p.value) 46*9e94795aSAndroid Build Coastguard Worker self.assertTrue(p.is_optional()) 47*9e94795aSAndroid Build Coastguard Worker self.assertEqual("a?=b", str(p)) 48*9e94795aSAndroid Build Coastguard Worker 49*9e94795aSAndroid Build Coastguard Worker def test_makeAsComment(self): 50*9e94795aSAndroid Build Coastguard Worker p = Prop.from_line("a=b") 51*9e94795aSAndroid Build Coastguard Worker p.comments.append("# a comment") 52*9e94795aSAndroid Build Coastguard Worker self.assertFalse(p.is_comment()) 53*9e94795aSAndroid Build Coastguard Worker 54*9e94795aSAndroid Build Coastguard Worker p.make_as_comment() 55*9e94795aSAndroid Build Coastguard Worker self.assertTrue(p.is_comment()) 56*9e94795aSAndroid Build Coastguard Worker self.assertEqual("# a comment\n#a=b", str(p)) 57*9e94795aSAndroid Build Coastguard Worker 58*9e94795aSAndroid Build Coastguard Workerclass PropListTestcase(unittest.TestCase): 59*9e94795aSAndroid Build Coastguard Worker def setUp(self): 60*9e94795aSAndroid Build Coastguard Worker content = """ 61*9e94795aSAndroid Build Coastguard Worker # comment 62*9e94795aSAndroid Build Coastguard Worker foo=true 63*9e94795aSAndroid Build Coastguard Worker bar=false 64*9e94795aSAndroid Build Coastguard Worker qux?=1 65*9e94795aSAndroid Build Coastguard Worker # another comment 66*9e94795aSAndroid Build Coastguard Worker foo?=false 67*9e94795aSAndroid Build Coastguard Worker """ 68*9e94795aSAndroid Build Coastguard Worker self.patcher = patch("post_process_props.open", mock_open(read_data=content)) 69*9e94795aSAndroid Build Coastguard Worker self.mock_open = self.patcher.start() 70*9e94795aSAndroid Build Coastguard Worker self.props = PropList("file") 71*9e94795aSAndroid Build Coastguard Worker 72*9e94795aSAndroid Build Coastguard Worker def tearDown(self): 73*9e94795aSAndroid Build Coastguard Worker self.patcher.stop() 74*9e94795aSAndroid Build Coastguard Worker self.props = None 75*9e94795aSAndroid Build Coastguard Worker 76*9e94795aSAndroid Build Coastguard Worker def test_readFromFile(self): 77*9e94795aSAndroid Build Coastguard Worker self.assertEqual(4, len(self.props.get_all_props())) 78*9e94795aSAndroid Build Coastguard Worker expected = [ 79*9e94795aSAndroid Build Coastguard Worker ("foo", "true", False), 80*9e94795aSAndroid Build Coastguard Worker ("bar", "false", False), 81*9e94795aSAndroid Build Coastguard Worker ("qux", "1", True), 82*9e94795aSAndroid Build Coastguard Worker ("foo", "false", True) 83*9e94795aSAndroid Build Coastguard Worker ] 84*9e94795aSAndroid Build Coastguard Worker for i,p in enumerate(self.props.get_all_props()): 85*9e94795aSAndroid Build Coastguard Worker self.assertEqual(expected[i][0], p.name) 86*9e94795aSAndroid Build Coastguard Worker self.assertEqual(expected[i][1], p.value) 87*9e94795aSAndroid Build Coastguard Worker self.assertEqual(expected[i][2], p.is_optional()) 88*9e94795aSAndroid Build Coastguard Worker self.assertFalse(p.is_comment()) 89*9e94795aSAndroid Build Coastguard Worker 90*9e94795aSAndroid Build Coastguard Worker self.assertEqual(set(["foo", "bar", "qux"]), self.props.get_all_names()) 91*9e94795aSAndroid Build Coastguard Worker 92*9e94795aSAndroid Build Coastguard Worker self.assertEqual("true", self.props.get_value("foo")) 93*9e94795aSAndroid Build Coastguard Worker self.assertEqual("false", self.props.get_value("bar")) 94*9e94795aSAndroid Build Coastguard Worker self.assertEqual("1", self.props.get_value("qux")) 95*9e94795aSAndroid Build Coastguard Worker 96*9e94795aSAndroid Build Coastguard Worker # there are two assignments for 'foo' 97*9e94795aSAndroid Build Coastguard Worker self.assertEqual(2, len(self.props.get_props("foo"))) 98*9e94795aSAndroid Build Coastguard Worker 99*9e94795aSAndroid Build Coastguard Worker def test_putNewProp(self): 100*9e94795aSAndroid Build Coastguard Worker self.props.put("new", "30") 101*9e94795aSAndroid Build Coastguard Worker 102*9e94795aSAndroid Build Coastguard Worker self.assertEqual(5, len(self.props.get_all_props())) 103*9e94795aSAndroid Build Coastguard Worker last_prop = self.props.get_all_props()[-1] 104*9e94795aSAndroid Build Coastguard Worker self.assertEqual("new", last_prop.name) 105*9e94795aSAndroid Build Coastguard Worker self.assertEqual("30", last_prop.value) 106*9e94795aSAndroid Build Coastguard Worker self.assertFalse(last_prop.is_optional()) 107*9e94795aSAndroid Build Coastguard Worker 108*9e94795aSAndroid Build Coastguard Worker def test_putExistingNonOptionalProp(self): 109*9e94795aSAndroid Build Coastguard Worker self.props.put("foo", "NewValue") 110*9e94795aSAndroid Build Coastguard Worker 111*9e94795aSAndroid Build Coastguard Worker self.assertEqual(4, len(self.props.get_all_props())) 112*9e94795aSAndroid Build Coastguard Worker foo_prop = self.props.get_props("foo")[0] 113*9e94795aSAndroid Build Coastguard Worker self.assertEqual("foo", foo_prop.name) 114*9e94795aSAndroid Build Coastguard Worker self.assertEqual("NewValue", foo_prop.value) 115*9e94795aSAndroid Build Coastguard Worker self.assertFalse(foo_prop.is_optional()) 116*9e94795aSAndroid Build Coastguard Worker self.assertEqual("# Value overridden by post_process_props.py. " + 117*9e94795aSAndroid Build Coastguard Worker "Original value: true\nfoo=NewValue", str(foo_prop)) 118*9e94795aSAndroid Build Coastguard Worker 119*9e94795aSAndroid Build Coastguard Worker def test_putExistingOptionalProp(self): 120*9e94795aSAndroid Build Coastguard Worker self.props.put("qux", "2") 121*9e94795aSAndroid Build Coastguard Worker 122*9e94795aSAndroid Build Coastguard Worker self.assertEqual(5, len(self.props.get_all_props())) 123*9e94795aSAndroid Build Coastguard Worker last_prop = self.props.get_all_props()[-1] 124*9e94795aSAndroid Build Coastguard Worker self.assertEqual("qux", last_prop.name) 125*9e94795aSAndroid Build Coastguard Worker self.assertEqual("2", last_prop.value) 126*9e94795aSAndroid Build Coastguard Worker self.assertFalse(last_prop.is_optional()) 127*9e94795aSAndroid Build Coastguard Worker self.assertEqual("# Auto-added by post_process_props.py\nqux=2", 128*9e94795aSAndroid Build Coastguard Worker str(last_prop)) 129*9e94795aSAndroid Build Coastguard Worker 130*9e94795aSAndroid Build Coastguard Worker def test_deleteNonOptionalProp(self): 131*9e94795aSAndroid Build Coastguard Worker props_to_delete = self.props.get_props("foo")[0] 132*9e94795aSAndroid Build Coastguard Worker props_to_delete.delete(reason="testing") 133*9e94795aSAndroid Build Coastguard Worker 134*9e94795aSAndroid Build Coastguard Worker self.assertEqual(3, len(self.props.get_all_props())) 135*9e94795aSAndroid Build Coastguard Worker self.assertEqual("# Removed by post_process_props.py because testing\n" + 136*9e94795aSAndroid Build Coastguard Worker "#foo=true", str(props_to_delete)) 137*9e94795aSAndroid Build Coastguard Worker 138*9e94795aSAndroid Build Coastguard Worker def test_deleteOptionalProp(self): 139*9e94795aSAndroid Build Coastguard Worker props_to_delete = self.props.get_props("qux")[0] 140*9e94795aSAndroid Build Coastguard Worker props_to_delete.delete(reason="testing") 141*9e94795aSAndroid Build Coastguard Worker 142*9e94795aSAndroid Build Coastguard Worker self.assertEqual(3, len(self.props.get_all_props())) 143*9e94795aSAndroid Build Coastguard Worker self.assertEqual("# Removed by post_process_props.py because testing\n" + 144*9e94795aSAndroid Build Coastguard Worker "#qux?=1", str(props_to_delete)) 145*9e94795aSAndroid Build Coastguard Worker 146*9e94795aSAndroid Build Coastguard Worker def test_overridingNonOptional(self): 147*9e94795aSAndroid Build Coastguard Worker props_to_be_overridden = self.props.get_props("foo")[1] 148*9e94795aSAndroid Build Coastguard Worker self.assertTrue("true", props_to_be_overridden.value) 149*9e94795aSAndroid Build Coastguard Worker 150*9e94795aSAndroid Build Coastguard Worker self.assertTrue(override_optional_props(self.props)) 151*9e94795aSAndroid Build Coastguard Worker 152*9e94795aSAndroid Build Coastguard Worker # size reduced to 3 because foo?=false was overridden by foo=true 153*9e94795aSAndroid Build Coastguard Worker self.assertEqual(3, len(self.props.get_all_props())) 154*9e94795aSAndroid Build Coastguard Worker 155*9e94795aSAndroid Build Coastguard Worker self.assertEqual(1, len(self.props.get_props("foo"))) 156*9e94795aSAndroid Build Coastguard Worker self.assertEqual("true", self.props.get_props("foo")[0].value) 157*9e94795aSAndroid Build Coastguard Worker 158*9e94795aSAndroid Build Coastguard Worker self.assertEqual("# Removed by post_process_props.py because " + 159*9e94795aSAndroid Build Coastguard Worker "overridden by foo=true\n#foo?=false", 160*9e94795aSAndroid Build Coastguard Worker str(props_to_be_overridden)) 161*9e94795aSAndroid Build Coastguard Worker 162*9e94795aSAndroid Build Coastguard Worker def test_overridingOptional(self): 163*9e94795aSAndroid Build Coastguard Worker content = """ 164*9e94795aSAndroid Build Coastguard Worker # comment 165*9e94795aSAndroid Build Coastguard Worker qux?=2 166*9e94795aSAndroid Build Coastguard Worker foo=true 167*9e94795aSAndroid Build Coastguard Worker bar=false 168*9e94795aSAndroid Build Coastguard Worker qux?=1 169*9e94795aSAndroid Build Coastguard Worker # another comment 170*9e94795aSAndroid Build Coastguard Worker foo?=false 171*9e94795aSAndroid Build Coastguard Worker """ 172*9e94795aSAndroid Build Coastguard Worker with patch('post_process_props.open', mock_open(read_data=content)) as m: 173*9e94795aSAndroid Build Coastguard Worker props = PropList("hello") 174*9e94795aSAndroid Build Coastguard Worker 175*9e94795aSAndroid Build Coastguard Worker props_to_be_overridden = props.get_props("qux")[0] 176*9e94795aSAndroid Build Coastguard Worker self.assertEqual("2", props_to_be_overridden.value) 177*9e94795aSAndroid Build Coastguard Worker 178*9e94795aSAndroid Build Coastguard Worker self.assertTrue(override_optional_props(props)) 179*9e94795aSAndroid Build Coastguard Worker 180*9e94795aSAndroid Build Coastguard Worker self.assertEqual(1, len(props.get_props("qux"))) 181*9e94795aSAndroid Build Coastguard Worker self.assertEqual("1", props.get_props("qux")[0].value) 182*9e94795aSAndroid Build Coastguard Worker # the only left optional assignment becomes non-optional 183*9e94795aSAndroid Build Coastguard Worker self.assertFalse(props.get_props("qux")[0].is_optional()) 184*9e94795aSAndroid Build Coastguard Worker 185*9e94795aSAndroid Build Coastguard Worker self.assertEqual("# Removed by post_process_props.py because " + 186*9e94795aSAndroid Build Coastguard Worker "overridden by qux?=1\n#qux?=2", 187*9e94795aSAndroid Build Coastguard Worker str(props_to_be_overridden)) 188*9e94795aSAndroid Build Coastguard Worker 189*9e94795aSAndroid Build Coastguard Worker def test_overridingDuplicated(self): 190*9e94795aSAndroid Build Coastguard Worker content = """ 191*9e94795aSAndroid Build Coastguard Worker # comment 192*9e94795aSAndroid Build Coastguard Worker foo=true 193*9e94795aSAndroid Build Coastguard Worker bar=false 194*9e94795aSAndroid Build Coastguard Worker qux?=1 195*9e94795aSAndroid Build Coastguard Worker foo=false 196*9e94795aSAndroid Build Coastguard Worker # another comment 197*9e94795aSAndroid Build Coastguard Worker foo?=false 198*9e94795aSAndroid Build Coastguard Worker """ 199*9e94795aSAndroid Build Coastguard Worker with patch("post_process_props.open", mock_open(read_data=content)) as m: 200*9e94795aSAndroid Build Coastguard Worker stderr_redirect = io.StringIO() 201*9e94795aSAndroid Build Coastguard Worker with contextlib.redirect_stderr(stderr_redirect): 202*9e94795aSAndroid Build Coastguard Worker props = PropList("hello") 203*9e94795aSAndroid Build Coastguard Worker 204*9e94795aSAndroid Build Coastguard Worker # fails due to duplicated foo=true and foo=false 205*9e94795aSAndroid Build Coastguard Worker self.assertFalse(override_optional_props(props)) 206*9e94795aSAndroid Build Coastguard Worker 207*9e94795aSAndroid Build Coastguard Worker self.assertEqual("error: found duplicate sysprop assignments:\n" + 208*9e94795aSAndroid Build Coastguard Worker "foo=true\nfoo=false\n", stderr_redirect.getvalue()) 209*9e94795aSAndroid Build Coastguard Worker 210*9e94795aSAndroid Build Coastguard Worker def test_overridingDuplicatedWithSameValue(self): 211*9e94795aSAndroid Build Coastguard Worker content = """ 212*9e94795aSAndroid Build Coastguard Worker # comment 213*9e94795aSAndroid Build Coastguard Worker foo=true 214*9e94795aSAndroid Build Coastguard Worker bar=false 215*9e94795aSAndroid Build Coastguard Worker qux?=1 216*9e94795aSAndroid Build Coastguard Worker foo=true 217*9e94795aSAndroid Build Coastguard Worker # another comment 218*9e94795aSAndroid Build Coastguard Worker foo?=false 219*9e94795aSAndroid Build Coastguard Worker """ 220*9e94795aSAndroid Build Coastguard Worker with patch("post_process_props.open", mock_open(read_data=content)) as m: 221*9e94795aSAndroid Build Coastguard Worker stderr_redirect = io.StringIO() 222*9e94795aSAndroid Build Coastguard Worker with contextlib.redirect_stderr(stderr_redirect): 223*9e94795aSAndroid Build Coastguard Worker props = PropList("hello") 224*9e94795aSAndroid Build Coastguard Worker optional_prop = props.get_props("foo")[2] # the last foo?=false one 225*9e94795aSAndroid Build Coastguard Worker 226*9e94795aSAndroid Build Coastguard Worker # we have duplicated foo=true and foo=true, but that's allowed 227*9e94795aSAndroid Build Coastguard Worker # since they have the same value 228*9e94795aSAndroid Build Coastguard Worker self.assertTrue(override_optional_props(props)) 229*9e94795aSAndroid Build Coastguard Worker 230*9e94795aSAndroid Build Coastguard Worker # foo?=false should be commented out 231*9e94795aSAndroid Build Coastguard Worker self.assertEqual("# Removed by post_process_props.py because " + 232*9e94795aSAndroid Build Coastguard Worker "overridden by foo=true\n#foo?=false", 233*9e94795aSAndroid Build Coastguard Worker str(optional_prop)) 234*9e94795aSAndroid Build Coastguard Worker 235*9e94795aSAndroid Build Coastguard Worker def test_allowDuplicates(self): 236*9e94795aSAndroid Build Coastguard Worker content = """ 237*9e94795aSAndroid Build Coastguard Worker # comment 238*9e94795aSAndroid Build Coastguard Worker foo=true 239*9e94795aSAndroid Build Coastguard Worker bar=false 240*9e94795aSAndroid Build Coastguard Worker qux?=1 241*9e94795aSAndroid Build Coastguard Worker foo=false 242*9e94795aSAndroid Build Coastguard Worker # another comment 243*9e94795aSAndroid Build Coastguard Worker foo?=false 244*9e94795aSAndroid Build Coastguard Worker """ 245*9e94795aSAndroid Build Coastguard Worker with patch("post_process_props.open", mock_open(read_data=content)) as m: 246*9e94795aSAndroid Build Coastguard Worker stderr_redirect = io.StringIO() 247*9e94795aSAndroid Build Coastguard Worker with contextlib.redirect_stderr(stderr_redirect): 248*9e94795aSAndroid Build Coastguard Worker props = PropList("hello") 249*9e94795aSAndroid Build Coastguard Worker 250*9e94795aSAndroid Build Coastguard Worker # we have duplicated foo=true and foo=false, but that's allowed 251*9e94795aSAndroid Build Coastguard Worker # because it's explicitly allowed 252*9e94795aSAndroid Build Coastguard Worker self.assertTrue(override_optional_props(props, allow_dup=True)) 253*9e94795aSAndroid Build Coastguard Worker 254*9e94795aSAndroid Build Coastguard Worker def test_validateGrfProps(self): 255*9e94795aSAndroid Build Coastguard Worker stderr_redirect = io.StringIO() 256*9e94795aSAndroid Build Coastguard Worker with contextlib.redirect_stderr(stderr_redirect): 257*9e94795aSAndroid Build Coastguard Worker props = PropList("hello") 258*9e94795aSAndroid Build Coastguard Worker props.put("ro.board.first_api_level","202504") 259*9e94795aSAndroid Build Coastguard Worker props.put("ro.build.version.codename", "REL") 260*9e94795aSAndroid Build Coastguard Worker 261*9e94795aSAndroid Build Coastguard Worker # manually set ro.board.api_level to an invalid value 262*9e94795aSAndroid Build Coastguard Worker props.put("ro.board.api_level","202404") 263*9e94795aSAndroid Build Coastguard Worker self.assertFalse(validate_grf_props(props)) 264*9e94795aSAndroid Build Coastguard Worker 265*9e94795aSAndroid Build Coastguard Worker props.get_all_props()[-1].make_as_comment() 266*9e94795aSAndroid Build Coastguard Worker # manually set ro.board.api_level to a valid value 267*9e94795aSAndroid Build Coastguard Worker props.put("ro.board.api_level","202504") 268*9e94795aSAndroid Build Coastguard Worker self.assertTrue(validate_grf_props(props)) 269*9e94795aSAndroid Build Coastguard Worker 270*9e94795aSAndroid Build Coastguard Workerif __name__ == '__main__': 271*9e94795aSAndroid Build Coastguard Worker unittest.main(verbosity=2) 272