test_generate_static_pref_list.py (11438B)
1 # This Source Code Form is subject to the terms of the Mozilla Public 2 # License, v. 2.0. If a copy of the MPL was not distributed with this 3 # file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 5 import sys 6 import unittest 7 from os import path 8 9 import mozpack.path as mozpath 10 import mozunit 11 import yaml 12 13 try: 14 from StringIO import StringIO 15 except ImportError: 16 from io import StringIO 17 18 sys.path.append(path.join(path.dirname(__file__), "..")) 19 from init.generate_static_pref_list import check_pref_list, generate_code 20 21 test_data_path = mozpath.abspath(mozpath.dirname(__file__)) 22 test_data_path = mozpath.join(test_data_path, "data") 23 24 # A single good input with lots of different combinations. 25 good_input = """ 26 - name: my.bool 27 type: bool 28 value: false 29 mirror: never 30 31 - name: my.int 32 type: int32_t 33 value: -123 34 mirror: once 35 do_not_use_directly: false 36 rust: false 37 38 - mirror: always 39 value: 999 40 type: uint32_t 41 name: my.uint 42 rust: true 43 44 - name: my.float # A comment. 45 type: float # A comment. 46 do_not_use_directly: true # A comment. 47 value: 0.0f # A comment. 48 mirror: once # A comment. 49 rust: true # A comment. 50 51 # A comment. 52 - name: my.string 53 type: String 54 value: foo"bar # The double quote needs escaping. 55 mirror: never 56 include: foobar.h 57 58 # A comment. 59 - name: my.string2 60 type: String 61 value: "foobar" # This string is quoted. 62 mirror: never 63 64 # A comment. 65 - name: my.atomic.bool 66 type: RelaxedAtomicBool 67 value: true 68 mirror: always 69 rust: true 70 71 # A comment. 72 - name: my.datamutex.string 73 type: DataMutexString 74 value: "foobar" # This string is quoted. 75 mirror: always 76 77 # Mirrored string-valued prefs are interesting in Rust. 78 - name: my.datamutex.string.rust 79 type: DataMutexString 80 value: "" 81 mirror: always 82 rust: true 83 84 # YAML+Python interprets `10 + 10 * 20` as a string, and so it is printed 85 # unchanged. 86 - name: my.atomic.int 87 type: ReleaseAcquireAtomicInt32 88 value: 10 + 10 * 20 89 mirror: always 90 do_not_use_directly: true # A comment. 91 92 # YAML+Python changes `0x44` to `68` because it interprets the value as an 93 # integer. 94 - name: my.atomic.uint 95 type: SequentiallyConsistentAtomicUint32 96 value: 0x44 97 mirror: once 98 99 # YAML+Python changes `.4455667` to `0.4455667` because it interprets the value 100 # as a float. 101 - name: my-dashed.atomic.float 102 type: AtomicFloat 103 value: .4455667 104 mirror: never 105 include: <math.h> 106 """ 107 108 # The corresponding code for good_input. 109 good = {} 110 111 good["static_pref_list_all_h"] = """\ 112 // This file was generated by generate_static_pref_list.py from (string input). DO NOT EDIT. 113 114 #include "mozilla/StaticPrefList_my.h" 115 #include "mozilla/StaticPrefList_my_dashed.h" 116 """ 117 118 good["static_prefs_all_h"] = """\ 119 // This file was generated by generate_static_pref_list.py from (string input). DO NOT EDIT. 120 121 #include "mozilla/StaticPrefs_my.h" 122 #include "mozilla/StaticPrefs_my_dashed.h" 123 """ 124 125 good["static_pref_list_group_h"] = { 126 "my": """\ 127 // This file was generated by generate_static_pref_list.py from (string input). DO NOT EDIT. 128 129 NEVER_PREF("my.bool", bool, false) 130 131 ONCE_PREF( 132 "my.int", 133 my_int, 134 my_int_AtStartup, 135 int32_t, -123 136 ) 137 138 ALWAYS_PREF( 139 "my.uint", 140 my_uint, 141 my_uint, 142 uint32_t, 999 143 ) 144 145 ONCE_PREF( 146 "my.float", 147 my_float, 148 my_float_AtStartup_DoNotUseDirectly, 149 float, 0.0f 150 ) 151 152 NEVER_PREF("my.string", String, "foo\\"bar") 153 154 NEVER_PREF("my.string2", String, "foobar") 155 156 ALWAYS_PREF( 157 "my.atomic.bool", 158 my_atomic_bool, 159 my_atomic_bool, 160 RelaxedAtomicBool, true 161 ) 162 163 ALWAYS_DATAMUTEX_PREF( 164 "my.datamutex.string", 165 my_datamutex_string, 166 my_datamutex_string, 167 DataMutexString, "foobar"_ns 168 ) 169 170 ALWAYS_DATAMUTEX_PREF( 171 "my.datamutex.string.rust", 172 my_datamutex_string_rust, 173 my_datamutex_string_rust, 174 DataMutexString, ""_ns 175 ) 176 177 ALWAYS_PREF( 178 "my.atomic.int", 179 my_atomic_int, 180 my_atomic_int_DoNotUseDirectly, 181 ReleaseAcquireAtomicInt32, 10 + 10 * 20 182 ) 183 184 ONCE_PREF( 185 "my.atomic.uint", 186 my_atomic_uint, 187 my_atomic_uint_AtStartup, 188 SequentiallyConsistentAtomicUint32, 68 189 ) 190 """, 191 "my_dashed": """\ 192 // This file was generated by generate_static_pref_list.py from (string input). DO NOT EDIT. 193 194 NEVER_PREF("my-dashed.atomic.float", AtomicFloat, 0.4455667) 195 """, 196 } 197 198 good["static_prefs_group_h"] = { 199 "my": """\ 200 // This file was generated by generate_static_pref_list.py from (string input). DO NOT EDIT. 201 // Include it to gain access to StaticPrefs::my_*. 202 203 #ifndef mozilla_StaticPrefs_my_h 204 #define mozilla_StaticPrefs_my_h 205 206 #include "foobar.h" 207 208 #include "mozilla/StaticPrefListBegin.h" 209 #include "mozilla/StaticPrefList_my.h" 210 #include "mozilla/StaticPrefListEnd.h" 211 212 #endif // mozilla_StaticPrefs_my_h 213 """ 214 } 215 216 good["static_prefs_c_getters_cpp"] = """\ 217 // This file was generated by generate_static_pref_list.py from (string input). DO NOT EDIT. 218 219 extern "C" uint32_t StaticPrefs_my_uint() { 220 return mozilla::StaticPrefs::my_uint(); 221 } 222 223 extern "C" float StaticPrefs_my_float_AtStartup_DoNotUseDirectly() { 224 return mozilla::StaticPrefs::my_float_AtStartup_DoNotUseDirectly(); 225 } 226 227 extern "C" bool StaticPrefs_my_atomic_bool() { 228 return mozilla::StaticPrefs::my_atomic_bool(); 229 } 230 231 extern "C" void StaticPrefs_my_datamutex_string_rust(nsACString *result) { 232 const auto preflock = mozilla::StaticPrefs::my_datamutex_string_rust(); 233 result->Append(*preflock); 234 } 235 """ 236 237 good["static_prefs_rs"] = """\ 238 // This file was generated by generate_static_pref_list.py from (string input). DO NOT EDIT. 239 240 pub use nsstring::nsCString; 241 extern "C" { 242 pub fn StaticPrefs_my_uint() -> u32; 243 pub fn StaticPrefs_my_float_AtStartup_DoNotUseDirectly() -> f32; 244 pub fn StaticPrefs_my_atomic_bool() -> bool; 245 pub fn StaticPrefs_my_datamutex_string_rust(result: *mut nsstring::nsACString); 246 } 247 248 #[macro_export] 249 macro_rules! pref { 250 ("my.uint") => (unsafe { $crate::StaticPrefs_my_uint() }); 251 ("my.float") => (unsafe { $crate::StaticPrefs_my_float_AtStartup_DoNotUseDirectly() }); 252 ("my.atomic.bool") => (unsafe { $crate::StaticPrefs_my_atomic_bool() }); 253 ("my.datamutex.string.rust") => (unsafe { let mut result = $crate::nsCString::new(); $crate::StaticPrefs_my_datamutex_string_rust(&mut *result); result }); 254 } 255 """ 256 257 # A lot of bad inputs, each with an accompanying error message. Listed in order 258 # of the relevant `error` calls within generate_static_pref_list.py. 259 bad_inputs = [ 260 ( 261 """ 262 - invalidkey: 3 263 """, 264 "invalid key `invalidkey`", 265 ), 266 ( 267 """ 268 - type: int32_t 269 """, 270 "missing `name` key", 271 ), 272 ( 273 """ 274 - name: 99 275 """, 276 "non-string `name` value `99`", 277 ), 278 ( 279 """ 280 - name: name_with_no_dot 281 """, 282 "`name` value `name_with_no_dot` lacks a '.'", 283 ), 284 ( 285 """ 286 - name: pref.is.defined.more.than.once 287 type: bool 288 value: false 289 mirror: never 290 - name: pref.is.defined.more.than.once 291 type: int32_t 292 value: 111 293 mirror: always 294 """, 295 "`pref.is.defined.more.than.once` pref is defined more than once", 296 ), 297 ( 298 """ 299 - name: your.pref 300 type: bool 301 value: false 302 mirror: never 303 - name: my.pref 304 type: bool 305 value: false 306 mirror: never 307 """, 308 "`my.pref` pref must come before `your.pref` pref", 309 ), 310 ( 311 """ 312 - name: missing.type.key 313 value: false 314 mirror: never 315 """, 316 "missing `type` key for pref `missing.type.key`", 317 ), 318 ( 319 """ 320 - name: invalid.type.value 321 type: const char* 322 value: true 323 mirror: never 324 """, 325 "invalid `type` value `const char*` for pref `invalid.type.value`", 326 ), 327 ( 328 """ 329 - name: missing.value.key 330 type: int32_t 331 mirror: once 332 """, 333 "missing `value` key for pref `missing.value.key`", 334 ), 335 ( 336 """ 337 - name: non-string.value 338 type: String 339 value: 3.45 340 mirror: once 341 """, 342 "non-string `value` value `3.45` for `String` pref `non-string.value`; add double quotes", 343 ), 344 ( 345 """ 346 - name: invalid.boolean.value 347 type: bool 348 value: true || false 349 mirror: once 350 """, 351 "invalid boolean value `true || false` for pref `invalid.boolean.value`", 352 ), 353 ( 354 """ 355 - name: missing.mirror.key 356 type: int32_t 357 value: 3 358 """, 359 "missing `mirror` key for pref `missing.mirror.key`", 360 ), 361 ( 362 """ 363 - name: invalid.mirror.value 364 type: bool 365 value: true 366 mirror: sometimes 367 """, 368 "invalid `mirror` value `sometimes` for pref `invalid.mirror.value`", 369 ), 370 ( 371 """ 372 - name: non-boolean.do_not_use_directly.value 373 type: bool 374 value: true 375 mirror: always 376 do_not_use_directly: 0 377 """, 378 "non-boolean `do_not_use_directly` value `0` for pref " 379 "`non-boolean.do_not_use_directly.value`", 380 ), 381 ( 382 """ 383 - name: do_not_use_directly.uselessly.set 384 type: int32_t 385 value: 0 386 mirror: never 387 do_not_use_directly: true 388 """, 389 "`do_not_use_directly` uselessly set with `mirror` value `never` for " 390 "pref `do_not_use_directly.uselessly.set`", 391 ), 392 ( 393 """ 394 - name: non-string.include.value 395 type: bool 396 value: true 397 mirror: always 398 include: 33 399 """, 400 "non-string `include` value `33` for pref `non-string.include.value`", 401 ), 402 ( 403 """ 404 - name: include.value.starts.with 405 type: float 406 value: M_PI 407 mirror: never 408 include: <cmath 409 """, 410 "`include` value `<cmath` starts with `<` but does not end with `>` for " 411 "pref `include.value.starts.with`", 412 ), 413 ( 414 """ 415 - name: non-boolean.rust.value 416 type: bool 417 value: true 418 mirror: always 419 rust: 1 420 """, 421 "non-boolean `rust` value `1` for pref `non-boolean.rust.value`", 422 ), 423 ( 424 """ 425 - name: rust.uselessly.set 426 type: int32_t 427 value: 0 428 mirror: never 429 rust: true 430 """, 431 "`rust` uselessly set with `mirror` value `never` for pref " 432 "`rust.uselessly.set`", 433 ), 434 ] 435 436 437 class TestGenerateStaticPrefList(unittest.TestCase): 438 """ 439 Unit tests for generate_static_pref_list.py. 440 """ 441 442 def test_good(self): 443 "Test various pieces of good input." 444 inp = StringIO(good_input) 445 pref_list = yaml.safe_load(inp) 446 check_pref_list(pref_list) 447 code = generate_code(pref_list, ["(string input)"]) 448 449 self.assertEqual(good["static_pref_list_all_h"], code["static_pref_list_all_h"]) 450 451 self.assertEqual(good["static_prefs_all_h"], code["static_prefs_all_h"]) 452 453 self.assertEqual( 454 good["static_pref_list_group_h"]["my"], 455 code["static_pref_list_group_h"]["my"], 456 ) 457 self.assertEqual( 458 good["static_pref_list_group_h"]["my_dashed"], 459 code["static_pref_list_group_h"]["my_dashed"], 460 ) 461 462 self.assertEqual( 463 good["static_prefs_group_h"]["my"], code["static_prefs_group_h"]["my"] 464 ) 465 466 self.assertEqual( 467 good["static_prefs_c_getters_cpp"], code["static_prefs_c_getters_cpp"] 468 ) 469 470 self.assertEqual(good["static_prefs_rs"], code["static_prefs_rs"]) 471 472 def test_bad(self): 473 "Test various pieces of bad input." 474 475 for input_string, expected in bad_inputs: 476 inp = StringIO(input_string) 477 try: 478 pref_list = yaml.safe_load(inp) 479 check_pref_list(pref_list) 480 generate_code(pref_list, ["(string input"]) 481 self.assertEqual(0, 1) 482 except ValueError as e: 483 self.assertEqual(str(e), expected) 484 485 486 if __name__ == "__main__": 487 mozunit.main()