blob: 2addefcb6f2463c85adff26d71261b3b0899c17c [file] [log] [blame]
Jiyong Parkd721e872020-06-22 17:30:57 +09001#!/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())
Justin Yun07ceaa72021-04-02 16:29:06 +090056 self.assertEqual("# a comment\n#a=b", str(p))
Jiyong Parkd721e872020-06-22 17:30:57 +090057
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")
Jiyong Park24d9cad2020-06-30 11:41:23 +0900224 optional_prop = props.get_props("foo")[2] # the last foo?=false one
Jiyong Parkd721e872020-06-22 17:30:57 +0900225
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 Park24d9cad2020-06-30 11:41:23 +0900230 # 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 Park0b4fccb2020-06-26 17:38:00 +0900235 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 Yun07ceaa72021-04-02 16:29:06 +0900254 def test_validateGrfProps(self):
255 stderr_redirect = io.StringIO()
256 with contextlib.redirect_stderr(stderr_redirect):
257 props = PropList("hello")
Justin Yun23d52432023-11-10 16:31:04 +0900258 props.put("ro.board.first_api_level","202504")
Justin Yun870ea2e2023-04-06 16:28:12 +0900259 props.put("ro.build.version.codename", "REL")
Justin Yun07ceaa72021-04-02 16:29:06 +0900260
Justin Yun07ceaa72021-04-02 16:29:06 +0900261 # manually set ro.board.api_level to an invalid value
Justin Yun23d52432023-11-10 16:31:04 +0900262 props.put("ro.board.api_level","202404")
263 self.assertFalse(validate_grf_props(props))
Justin Yun07ceaa72021-04-02 16:29:06 +0900264
265 props.get_all_props()[-1].make_as_comment()
266 # manually set ro.board.api_level to a valid value
Justin Yun23d52432023-11-10 16:31:04 +0900267 props.put("ro.board.api_level","202504")
268 self.assertTrue(validate_grf_props(props))
Justin Yun870ea2e2023-04-06 16:28:12 +0900269
Jiyong Parkd721e872020-06-22 17:30:57 +0900270if __name__ == '__main__':
271 unittest.main(verbosity=2)