# 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 io
import os
import re
from manifestparser
import TestManifest
from manifestparser.toml
import DEFAULT_SECTION, alphabetize_toml_str, sort_paths
from mozlint
import result
from mozlint.pathutils
import expand_exclusions
from mozpack
import path
as mozpath
from tomlkit.items
import Array, Table
SECTION_REGEX = r
"^\[.*\]$"
DISABLE_REGEX = r
"^[ \t]*#[ \t]*\[.*\]"
def make_result(path, message, is_error=
False):
if is_error:
level =
"error"
else:
level =
"warning"
result = {
"path": path,
"lineno": 0,
# tomlkit does not report lineno/column
"column": 0,
"message": message,
"level": level,
}
return result
def lint(paths, config, fix=
None, **lintargs):
results = []
fixed = 0
topsrcdir = lintargs[
"root"]
file_names = list(expand_exclusions(paths, config, topsrcdir))
file_names = [os.path.normpath(f)
for f
in file_names]
section_rx = re.compile(SECTION_REGEX, flags=re.M)
disable_rx = re.compile(DISABLE_REGEX, flags=re.M)
for file_name
in file_names:
path = mozpath.relpath(file_name, topsrcdir)
os.path.basename(file_name)
parser = TestManifest(use_toml=
True, document=
True)
try:
parser.read(file_name)
except Exception:
r = make_result(path,
"The manifest is not valid TOML.",
True)
results.append(result.from_config(config, **r))
continue
manifest = parser.source_documents[file_name]
manifest_str = io.open(file_name,
"r", encoding=
"utf-8").read()
if not DEFAULT_SECTION
in manifest:
r = make_result(
path, f
"The manifest does not start with a [{DEFAULT_SECTION}] section."
)
if fix:
fixed += 1
results.append(result.from_config(config, **r))
sections = [k
for k
in manifest.keys()
if k != DEFAULT_SECTION]
sorted_sections = sort_paths(sections)
if sections != sorted_sections:
r = make_result(
path,
"The manifest sections are not in alphabetical order."
)
if fix:
fixed += 1
results.append(result.from_config(config, **r))
m = section_rx.findall(manifest_str)
if len(m) > 0:
for section_match
in m:
section = section_match[1:-1]
if section == DEFAULT_SECTION:
continue
if not section.startswith(
'"'):
r = make_result(
path, f
"The section name must be double quoted: [{section}]"
)
if fix:
fixed += 1
results.append(result.from_config(config, **r))
m = disable_rx.findall(manifest_str)
if len(m) > 0:
for disabled_section
in m:
r = make_result(
path,
f
"Use 'disabled = \"<reason>\
"' to disable a test instead of a comment: {disabled_section}",
True,
)
results.append(result.from_config(config, **r))
for section, keyvals
in manifest.body:
if section
is None:
continue
if not isinstance(keyvals, Table):
r = make_result(
path, f
"Bad assignment in preamble: {section} = {keyvals}",
True
)
results.append(result.from_config(config, **r))
else:
for k, v
in keyvals.items():
if k.endswith(
"-if"):
if not isinstance(v, Array):
r = make_result(
path,
f
'Value for conditional must be an array: {k} = "{v}"',
True,
)
results.append(result.from_config(config, **r))
else:
for e
in v:
if e.find(
"||") > 0
and e.find(
"&&") < 0:
r = make_result(
path,
f
'Value for conditional must not include explicit ||, instead put on multiple lines: {k} = [ ... "{e}" ... ]',
True,
)
results.append(result.from_config(config, **r))
if fix:
manifest_str = alphabetize_toml_str(manifest)
fp = io.open(file_name,
"w", encoding=
"utf-8", newline=
"\n")
fp.write(manifest_str)
fp.close()
return {
"results": results,
"fixed": fixed}