# 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/. from pathlib import Path import mozunit import pytest LINTER = "test-manifest-toml" fixed = 0 ERROR = "error" WARNING = "warning" def test_valid(lint, paths): results = lint(paths("valid.toml")) assert len(results) == 0 def test_invalid(lint, paths): results = lint(paths("invalid.toml")) assert len(results) == 1 assert results[0].message.startswith("The manifest is not valid TOML") assert results[0].level == ERROR def test_assignment_in_preamble(lint, paths): # NOTE: The tomlkit parser will throw an error for this legal TOML # so our linter never gets to find the preamble assignment results = lint(paths("assignment-in-preamble.toml")) assert len(results) == 1 assert ( results[0].message == "The manifest is not valid TOML: 'bool' object has no attribute 'keys'" ) assert results[0].level == ERROR def test_no_default(lint, paths): """Test verifying [DEFAULT] section.""" results = lint(paths("no-default.toml")) assert len(results) == 1 assert results[0].message == "The manifest does not start with a [DEFAULT] section." assert results[0].level == WARNING def test_no_default_fix(lint, paths, create_temp_file): """Test fixing missing [DEFAULT] section.""" contents = "# this Manifest has no DEFAULT section\n" path = create_temp_file(contents, "no-default.toml") results = lint([path], fix=True) assert len(results) == 1 assert results[0].message == "The manifest does not start with a [DEFAULT] section." assert results[0].level == WARNING assert fixed == 1 expected = contents + "[DEFAULT]\n" assert Path(path).read_text() == expected def test_non_double_quote_sections_fix(lint, paths, create_temp_file): """Test and fix sections that do not have double quotes""" basename = "non-double-quote-sections" orig, fix = paths(f"{basename}.toml", f"{basename}-fix.toml") original = Path(orig).read_text() expected = Path(fix).read_text() path = create_temp_file(original, f"{basename}.toml") results = lint([path], fix=True) assert len(results) == 2 assert results[0].message == "The section name must be double quoted: [aaa.js]" assert results[0].level == WARNING assert results[0].lineno == 3 assert results[1].message == "The section name must be double quoted: ['bbb.js']" assert results[1].level == WARNING assert results[1].lineno == 6 assert Path(path).read_text() == expected def test_unsorted_fix(lint, paths, create_temp_file): """Test and fix sections in alpha order.""" basename = "unsorted" orig, fix = paths(f"{basename}.toml", f"{basename}-fix.toml") original = Path(orig).read_text() expected = Path(fix).read_text() path = create_temp_file(original, f"{basename}.toml") results = lint([path], fix=True) assert len(results) == 5 i: int = 0 assert results[i].message == "The manifest sections are not in alphabetical order." assert results[i].level == WARNING i += 1 assert ( results[i].message == "linux condition requires display == 'x11' or 'wayland'" ) assert results[i].level == WARNING i += 1 assert ( results[i].message == 'instead of "!debug" use three conditions: "asan", "opt", "tsan"' ) assert results[i].level == WARNING i += 1 assert ( results[i].message == "linux condition requires display == 'x11' or 'wayland'" ) assert results[i].level == WARNING i += 1 assert ( results[i].message == "non canonical condition: variable os (rank 1) should appear before variable ccov (rank 7) in condition: ccov && os == 'linux' && os_version == '22.04'" ) assert results[i].level == ERROR i += 1 assert Path(path).read_text() == expected def test_comment_section(lint, paths): """Test for commented sections.""" results = lint(paths("comment-section.toml")) assert len(results) == 2 assert results[0].message.startswith( "Use 'disabled = \"\"' to disable a test instead of a comment:" ) assert results[0].level == ERROR def test_skip_if_not_array(lint, paths): """Test for non-array skip-if value.""" results = lint(paths("skip-if-not-array.toml")) assert len(results) == 1 assert results[0].message.startswith("Value for conditional must be an array:") assert results[0].level == ERROR assert results[0].lineno == 3 def test_skip_if_explicit_or(lint, paths): """Test for explicit || in skip-if.""" results = lint(paths("skip-if-explicit-or.toml")) assert len(results) == 1 assert results[0].message.startswith( "Value for conditional must not include explicit ||, instead put on multiple lines:" ) assert results[0].level == ERROR assert results[0].lineno == 4 def test_missing_include(lint, paths): """Test for missing include""" results = lint(paths("missing-include.toml")) assert len(results) == 1 assert "non-existent.toml' does not exist" in results[0].message assert results[0].level == ERROR def test_non_idiomatic_fix(lint, paths, create_temp_file): """Test and fix non-idiomatic conditions.""" basename = "non-idiomatic" orig, fix = paths(f"{basename}.toml", f"{basename}-fix.toml") original = Path(orig).read_text() expected = Path(fix).read_text() path = create_temp_file(original, f"{basename}.toml") results = lint([path], fix=True) assert len(results) == 15 i: int = 0 assert ( results[i].message == "non canonical condition: unknown var 'bits' in comparison: bits == 64" ) assert results[i].level == ERROR i += 1 assert results[i].message == "using 'bits' is not idiomatic, use 'arch' instead" assert results[i].level == WARNING i += 1 assert ( results[i].message == "non canonical condition: unknown var 'processor' in comparison: processor == 'aarch64'" ) assert results[i].level == ERROR i += 1 assert results[i].message == "using 'bits' is not idiomatic, use 'arch' instead" assert results[i].level == WARNING i += 1 assert ( results[i].message == "using 'processor' is not idiomatic, use 'arch' instead" ) assert results[i].level == WARNING i += 1 assert ( results[i].message == "non canonical condition: unknown var 'android_version' in comparison: android_version == '36'" ) assert results[i].level == ERROR i += 1 assert ( results[i].message == "using 'android_version' is not idiomatic, use 'os_version' instead (see testing/mozbase/mozinfo/mozinfo/platforminfo.py)" ) assert results[i].level == WARNING i += 1 assert ( results[i].message == "linux os_version == '24.04' is only supported on display == 'x11'" ) assert results[i].level == WARNING i += 1 assert ( results[i].message == "non canonical condition: unknown var 'processor' in comparison: processor == 'x86_64'" ) assert results[i].level == ERROR i += 1 assert ( results[i].message == "using 'processor' is not idiomatic, use 'arch' instead" ) assert results[i].level == WARNING i += 1 assert ( results[i].message == "linux condition requires display == 'x11' or 'wayland'" ) assert results[i].level == WARNING i += 1 assert ( results[i].message == 'instead of "!debug" use three conditions: "asan", "opt", "tsan"' ) assert results[i].level == WARNING i += 1 assert ( results[i].message == "linux condition requires display == 'x11' or 'wayland'" ) assert results[i].level == WARNING i += 1 assert ( results[i].message == "use boolean variables directly instead of testing for literal values" ) assert results[i].level == WARNING i += 1 assert ( results[i].message == "use boolean variables directly instead of testing for literal values" ) assert results[i].level == WARNING i += 1 # wait to disable 18.04 until the Thunderbird migration is complete # assert results[i].message == "linux os_version == '18.04' is no longer used" # assert results[i].level == WARNING # i += 1 assert Path(path).read_text() == expected def test_android_os_mismatch(lint, paths, create_temp_file): """Test an android_version os_version mismatch""" contents = ( "[DEFAULT]\nskip-if = [\"android_version == '36' && os_version == '15'\"]" ) path = create_temp_file(contents, "android_mismatch.toml") with pytest.raises(Exception) as e: _results = lint([path], fix=True) assert str(e.value) == "android_version == '36' conflicts with os_version == '15'" def test_unknown_android_version(lint, paths, create_temp_file): """Test unknown android_version""" contents = "[DEFAULT]\nskip-if = [\"android_version == '37'\"]" path = create_temp_file(contents, "unknown_android.toml") with pytest.raises(Exception) as e: _results = lint([path], fix=True) assert ( str(e.value) == "Unknown Android API version '37'. Supported versions are dict_values(['24', '25', '26', '27', '28', '29', '30', '31', '32', '33', '34', '35', '36'])." ) def test_invalid_combinations(lint, paths, create_temp_file): """Test invalid combinations of platforms and build-types""" skip_if = "os == 'win' && os_version == '11.26100' && arch == 'x86' && tsan" contents = f'[DEFAULT]\nskip-if = ["{skip_if}"]' path = create_temp_file(contents, "combination.toml") results = lint([path], fix=True) assert len(results) == 1 assert results[0].message == "tsan build-type is not tested on Windows" assert results[0].level == ERROR skip_if = "os == 'linux' && os_version == '22.04' && arch == 'x86_64' && display == 'wayland' && asan" contents = f'[DEFAULT]\nskip-if = ["{skip_if}"]' path = create_temp_file(contents, "combination.toml") results = lint([path], fix=True) assert len(results) == 1 assert results[0].message == "asan build-type is not tested on Linux 22.04" assert results[0].level == ERROR skip_if = "os == 'linux' && os_version == '22.04' && arch == 'x86_64' && display == 'wayland' && tsan" contents = f'[DEFAULT]\nskip-if = ["{skip_if}"]' path = create_temp_file(contents, "combination.toml") results = lint([path], fix=True) assert len(results) == 1 assert results[0].message == "tsan build-type is not tested on Linux 22.04" assert results[0].level == ERROR if __name__ == "__main__": mozunit.main()