# This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. import sys import unittest from os import path import mozpack.path as mozpath import mozunit import yaml try: from StringIO import StringIO except ImportError: from io import StringIO sys.path.append(path.join(path.dirname(__file__), "..")) from init.generate_static_pref_list import generate_code test_data_path = mozpath.abspath(mozpath.dirname(__file__)) test_data_path = mozpath.join(test_data_path, "data") # A single good input with lots of different combinations. good_input = """ - name: my.bool type: bool value: false mirror: never - name: my.int type: int32_t value: -123 mirror: once do_not_use_directly: false rust: false - mirror: always value: 999 type: uint32_t name: my.uint rust: true - name: my.float # A comment. type: float # A comment. do_not_use_directly: true # A comment. value: 0.0f # A comment. mirror: once # A comment. rust: true # A comment. # A comment. - name: my.string type: String value: foo"bar # The double quote needs escaping. mirror: never include: foobar.h # A comment. - name: my.string2 type: String value: "foobar" # This string is quoted. mirror: never # A comment. - name: my.atomic.bool type: RelaxedAtomicBool value: true mirror: always rust: true # A comment. - name: my.datamutex.string type: DataMutexString value: "foobar" # This string is quoted. mirror: always # Mirrored string-valued prefs are interesting in Rust. - name: my.datamutex.string.rust type: DataMutexString value: "" mirror: always rust: true # YAML+Python interprets `10 + 10 * 20` as a string, and so it is printed # unchanged. - name: my.atomic.int type: ReleaseAcquireAtomicInt32 value: 10 + 10 * 20 mirror: always do_not_use_directly: true # A comment. # YAML+Python changes `0x44` to `68` because it interprets the value as an # integer. - name: my.atomic.uint type: SequentiallyConsistentAtomicUint32 value: 0x44 mirror: once # YAML+Python changes `.4455667` to `0.4455667` because it interprets the value # as a float. - name: my-dashed.atomic.float type: AtomicFloat value: .4455667 mirror: never include: """ # The corresponding code for good_input. good = {} good[ "static_pref_list_all_h" ] = """\ // This file was generated by generate_static_pref_list.py from (string input). DO NOT EDIT. #include "mozilla/StaticPrefList_my.h" #include "mozilla/StaticPrefList_my_dashed.h" """ good[ "static_prefs_all_h" ] = """\ // This file was generated by generate_static_pref_list.py from (string input). DO NOT EDIT. #include "mozilla/StaticPrefs_my.h" #include "mozilla/StaticPrefs_my_dashed.h" """ good["static_pref_list_group_h"] = { "my": """\ // This file was generated by generate_static_pref_list.py from (string input). DO NOT EDIT. NEVER_PREF("my.bool", bool, false) ONCE_PREF( "my.int", my_int, my_int_AtStartup, int32_t, -123 ) ALWAYS_PREF( "my.uint", my_uint, my_uint, uint32_t, 999 ) ONCE_PREF( "my.float", my_float, my_float_AtStartup_DoNotUseDirectly, float, 0.0f ) NEVER_PREF("my.string", String, "foo\\"bar") NEVER_PREF("my.string2", String, "foobar") ALWAYS_PREF( "my.atomic.bool", my_atomic_bool, my_atomic_bool, RelaxedAtomicBool, true ) ALWAYS_DATAMUTEX_PREF( "my.datamutex.string", my_datamutex_string, my_datamutex_string, DataMutexString, "foobar"_ns ) ALWAYS_DATAMUTEX_PREF( "my.datamutex.string.rust", my_datamutex_string_rust, my_datamutex_string_rust, DataMutexString, ""_ns ) ALWAYS_PREF( "my.atomic.int", my_atomic_int, my_atomic_int_DoNotUseDirectly, ReleaseAcquireAtomicInt32, 10 + 10 * 20 ) ONCE_PREF( "my.atomic.uint", my_atomic_uint, my_atomic_uint_AtStartup, SequentiallyConsistentAtomicUint32, 68 ) """, "my_dashed": """\ // This file was generated by generate_static_pref_list.py from (string input). DO NOT EDIT. NEVER_PREF("my-dashed.atomic.float", AtomicFloat, 0.4455667) """, } good["static_prefs_group_h"] = { "my": """\ // This file was generated by generate_static_pref_list.py from (string input). DO NOT EDIT. // Include it to gain access to StaticPrefs::my_*. #ifndef mozilla_StaticPrefs_my_h #define mozilla_StaticPrefs_my_h #include "foobar.h" #include "mozilla/StaticPrefListBegin.h" #include "mozilla/StaticPrefList_my.h" #include "mozilla/StaticPrefListEnd.h" #endif // mozilla_StaticPrefs_my_h """ } good[ "static_prefs_c_getters_cpp" ] = """\ // This file was generated by generate_static_pref_list.py from (string input). DO NOT EDIT. extern "C" uint32_t StaticPrefs_my_uint() { return mozilla::StaticPrefs::my_uint(); } extern "C" float StaticPrefs_my_float_AtStartup_DoNotUseDirectly() { return mozilla::StaticPrefs::my_float_AtStartup_DoNotUseDirectly(); } extern "C" bool StaticPrefs_my_atomic_bool() { return mozilla::StaticPrefs::my_atomic_bool(); } extern "C" void StaticPrefs_my_datamutex_string_rust(nsACString *result) { const auto preflock = mozilla::StaticPrefs::my_datamutex_string_rust(); result->Append(*preflock); } """ good[ "static_prefs_rs" ] = """\ // This file was generated by generate_static_pref_list.py from (string input). DO NOT EDIT. pub use nsstring::nsCString; extern "C" { pub fn StaticPrefs_my_uint() -> u32; pub fn StaticPrefs_my_float_AtStartup_DoNotUseDirectly() -> f32; pub fn StaticPrefs_my_atomic_bool() -> bool; pub fn StaticPrefs_my_datamutex_string_rust(result: *mut nsstring::nsACString); } #[macro_export] macro_rules! pref { ("my.uint") => (unsafe { $crate::StaticPrefs_my_uint() }); ("my.float") => (unsafe { $crate::StaticPrefs_my_float_AtStartup_DoNotUseDirectly() }); ("my.atomic.bool") => (unsafe { $crate::StaticPrefs_my_atomic_bool() }); ("my.datamutex.string.rust") => (unsafe { let mut result = $crate::nsCString::new(); $crate::StaticPrefs_my_datamutex_string_rust(&mut *result); result }); } """ # A lot of bad inputs, each with an accompanying error message. Listed in order # of the relevant `error` calls within generate_static_pref_list.py. bad_inputs = [ ( """ - invalidkey: 3 """, "invalid key `invalidkey`", ), ( """ - type: int32_t """, "missing `name` key", ), ( """ - name: 99 """, "non-string `name` value `99`", ), ( """ - name: name_with_no_dot """, "`name` value `name_with_no_dot` lacks a '.'", ), ( """ - name: pref.is.defined.more.than.once type: bool value: false mirror: never - name: pref.is.defined.more.than.once type: int32_t value: 111 mirror: always """, "`pref.is.defined.more.than.once` pref is defined more than once", ), ( """ - name: your.pref type: bool value: false mirror: never - name: my.pref type: bool value: false mirror: never """, "`my.pref` pref must come before `your.pref` pref", ), ( """ - name: missing.type.key value: false mirror: never """, "missing `type` key for pref `missing.type.key`", ), ( """ - name: invalid.type.value type: const char* value: true mirror: never """, "invalid `type` value `const char*` for pref `invalid.type.value`", ), ( """ - name: missing.value.key type: int32_t mirror: once """, "missing `value` key for pref `missing.value.key`", ), ( """ - name: non-string.value type: String value: 3.45 mirror: once """, "non-string `value` value `3.45` for `String` pref `non-string.value`; add double quotes", ), ( """ - name: invalid.boolean.value type: bool value: true || false mirror: once """, "invalid boolean value `true || false` for pref `invalid.boolean.value`", ), ( """ - name: missing.mirror.key type: int32_t value: 3 """, "missing `mirror` key for pref `missing.mirror.key`", ), ( """ - name: invalid.mirror.value type: bool value: true mirror: sometimes """, "invalid `mirror` value `sometimes` for pref `invalid.mirror.value`", ), ( """ - name: non-boolean.do_not_use_directly.value type: bool value: true mirror: always do_not_use_directly: 0 """, "non-boolean `do_not_use_directly` value `0` for pref " "`non-boolean.do_not_use_directly.value`", ), ( """ - name: do_not_use_directly.uselessly.set type: int32_t value: 0 mirror: never do_not_use_directly: true """, "`do_not_use_directly` uselessly set with `mirror` value `never` for " "pref `do_not_use_directly.uselessly.set`", ), ( """ - name: non-string.include.value type: bool value: true mirror: always include: 33 """, "non-string `include` value `33` for pref `non-string.include.value`", ), ( """ - name: include.value.starts.with type: float value: M_PI mirror: never include: ` for " "pref `include.value.starts.with`", ), ( """ - name: non-boolean.rust.value type: bool value: true mirror: always rust: 1 """, "non-boolean `rust` value `1` for pref `non-boolean.rust.value`", ), ( """ - name: rust.uselessly.set type: int32_t value: 0 mirror: never rust: true """, "`rust` uselessly set with `mirror` value `never` for pref " "`rust.uselessly.set`", ), ] class TestGenerateStaticPrefList(unittest.TestCase): """ Unit tests for generate_static_pref_list.py. """ def test_good(self): "Test various pieces of good input." inp = StringIO(good_input) pref_list = yaml.safe_load(inp) code = generate_code(pref_list, "(string input)") self.assertEqual(good["static_pref_list_all_h"], code["static_pref_list_all_h"]) self.assertEqual(good["static_prefs_all_h"], code["static_prefs_all_h"]) self.assertEqual( good["static_pref_list_group_h"]["my"], code["static_pref_list_group_h"]["my"], ) self.assertEqual( good["static_pref_list_group_h"]["my_dashed"], code["static_pref_list_group_h"]["my_dashed"], ) self.assertEqual( good["static_prefs_group_h"]["my"], code["static_prefs_group_h"]["my"] ) self.assertEqual( good["static_prefs_c_getters_cpp"], code["static_prefs_c_getters_cpp"] ) self.assertEqual(good["static_prefs_rs"], code["static_prefs_rs"]) def test_bad(self): "Test various pieces of bad input." for (input_string, expected) in bad_inputs: inp = StringIO(input_string) try: pref_list = yaml.safe_load(inp) generate_code(pref_list, "(string input") self.assertEqual(0, 1) except ValueError as e: self.assertEqual(str(e), expected) if __name__ == "__main__": mozunit.main()