Jiyong Park | d721e87 | 2020-06-22 17:30:57 +0900 | [diff] [blame] | 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 | |
| 17 | import contextlib |
| 18 | import io |
| 19 | import unittest |
| 20 | |
| 21 | from unittest.mock import * |
| 22 | from post_process_props import * |
| 23 | |
| 24 | class 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()) |
Justin Yun | 07ceaa7 | 2021-04-02 16:29:06 +0900 | [diff] [blame] | 56 | self.assertEqual("# a comment\n#a=b", str(p)) |
Jiyong Park | d721e87 | 2020-06-22 17:30:57 +0900 | [diff] [blame] | 57 | |
| 58 | class 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") |
Jiyong Park | 24d9cad | 2020-06-30 11:41:23 +0900 | [diff] [blame] | 224 | optional_prop = props.get_props("foo")[2] # the last foo?=false one |
Jiyong Park | d721e87 | 2020-06-22 17:30:57 +0900 | [diff] [blame] | 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 | |
Jiyong Park | 24d9cad | 2020-06-30 11:41:23 +0900 | [diff] [blame] | 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 | |
Jiyong Park | 0b4fccb | 2020-06-26 17:38:00 +0900 | [diff] [blame] | 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 | |
Justin Yun | 07ceaa7 | 2021-04-02 16:29:06 +0900 | [diff] [blame] | 254 | def test_validateGrfProps(self): |
| 255 | stderr_redirect = io.StringIO() |
| 256 | with contextlib.redirect_stderr(stderr_redirect): |
| 257 | props = PropList("hello") |
Justin Yun | 23d5243 | 2023-11-10 16:31:04 +0900 | [diff] [blame] | 258 | props.put("ro.board.first_api_level","202504") |
Justin Yun | 870ea2e | 2023-04-06 16:28:12 +0900 | [diff] [blame] | 259 | props.put("ro.build.version.codename", "REL") |
Justin Yun | 07ceaa7 | 2021-04-02 16:29:06 +0900 | [diff] [blame] | 260 | |
Justin Yun | 07ceaa7 | 2021-04-02 16:29:06 +0900 | [diff] [blame] | 261 | # manually set ro.board.api_level to an invalid value |
Justin Yun | 23d5243 | 2023-11-10 16:31:04 +0900 | [diff] [blame] | 262 | props.put("ro.board.api_level","202404") |
| 263 | self.assertFalse(validate_grf_props(props)) |
Justin Yun | 07ceaa7 | 2021-04-02 16:29:06 +0900 | [diff] [blame] | 264 | |
| 265 | props.get_all_props()[-1].make_as_comment() |
| 266 | # manually set ro.board.api_level to a valid value |
Justin Yun | 23d5243 | 2023-11-10 16:31:04 +0900 | [diff] [blame] | 267 | props.put("ro.board.api_level","202504") |
| 268 | self.assertTrue(validate_grf_props(props)) |
Justin Yun | 870ea2e | 2023-04-06 16:28:12 +0900 | [diff] [blame] | 269 | |
Jiyong Park | d721e87 | 2020-06-22 17:30:57 +0900 | [diff] [blame] | 270 | if __name__ == '__main__': |
| 271 | unittest.main(verbosity=2) |