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