You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
depot_tools/metadata/tests/validate_test.py

270 lines
10 KiB
Python

#!/usr/bin/env python3
# Copyright 2023 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import os
import sys
import unittest
import unittest.mock
_THIS_DIR = os.path.abspath(os.path.dirname(__file__))
# The repo's root directory.
_ROOT_DIR = os.path.abspath(os.path.join(_THIS_DIR, "..", ".."))
# Add the repo's root directory for clearer imports.
sys.path.insert(0, _ROOT_DIR)
import gclient_utils
import metadata.validate
import metadata.validation_result
import metadata.fields.known
# Common paths for tests.
_SOURCE_FILE_DIR = os.path.join(_THIS_DIR, "data")
_VALID_METADATA_FILEPATH = os.path.join(_THIS_DIR, "data",
"README.chromium.test.multi-valid")
_INVALID_METADATA_FILEPATH = os.path.join(_THIS_DIR, "data",
"README.chromium.test.multi-invalid")
class ValidateContentTest(unittest.TestCase):
"""Tests for the validate_content function."""
def test_empty(self):
# Validate empty content (should result in a validation error).
results = metadata.validate.validate_content(
content="",
source_file_dir=_SOURCE_FILE_DIR,
repo_root_dir=_THIS_DIR,
)
self.assertEqual(len(results), 1)
self.assertTrue(results[0].is_fatal())
def test_valid(self):
# Validate valid file content (no errors or warnings).
results = metadata.validate.validate_content(
content=gclient_utils.FileRead(_VALID_METADATA_FILEPATH),
source_file_dir=_SOURCE_FILE_DIR,
repo_root_dir=_THIS_DIR,
)
self.assertEqual(len(results), 0)
def test_invalid(self):
# Validate invalid file content (both errors and warnings).
results = metadata.validate.validate_content(
content=gclient_utils.FileRead(_INVALID_METADATA_FILEPATH),
source_file_dir=_SOURCE_FILE_DIR,
repo_root_dir=_THIS_DIR,
)
self.assertEqual(len(results), 9)
error_count = 0
warning_count = 0
for result in results:
if result.is_fatal():
error_count += 1
else:
warning_count += 1
self.assertEqual(error_count, 7)
self.assertEqual(warning_count, 2)
class ValidateFileTest(unittest.TestCase):
"""Tests for the validate_file function."""
def test_missing(self):
# Validate a file that does not exist.
results = metadata.validate.validate_file(
filepath=os.path.join(_THIS_DIR, "data", "MISSING.chromium"),
repo_root_dir=_THIS_DIR,
)
# There should be exactly 1 error returned.
self.assertEqual(len(results), 1)
self.assertTrue(results[0].is_fatal())
def test_valid(self):
# Validate a valid file (no errors or warnings).
results = metadata.validate.validate_file(
filepath=_VALID_METADATA_FILEPATH,
repo_root_dir=_THIS_DIR,
)
self.assertEqual(len(results), 0)
def test_invalid(self):
# Validate an invalid file (both errors and warnings).
results = metadata.validate.validate_file(
filepath=_INVALID_METADATA_FILEPATH,
repo_root_dir=_THIS_DIR,
)
self.assertEqual(len(results), 9)
error_count = 0
warning_count = 0
for result in results:
if result.is_fatal():
error_count += 1
else:
warning_count += 1
self.assertEqual(error_count, 7)
self.assertEqual(warning_count, 2)
class CheckFileTest(unittest.TestCase):
"""Tests for the check_file function."""
def test_missing(self):
# Check a file that does not exist.
errors, warnings = metadata.validate.check_file(
filepath=os.path.join(_THIS_DIR, "data", "MISSING.chromium"),
repo_root_dir=_THIS_DIR,
)
self.assertEqual(len(errors), 1)
self.assertEqual(len(warnings), 0)
def test_valid(self):
# Check file with valid content (no errors or warnings).
errors, warnings = metadata.validate.check_file(
filepath=_VALID_METADATA_FILEPATH,
repo_root_dir=_THIS_DIR,
)
self.assertEqual(len(errors), 0)
self.assertEqual(len(warnings), 0)
def test_invalid(self):
# Check file with invalid content (both errors and warnings).
errors, warnings = metadata.validate.check_file(
filepath=_INVALID_METADATA_FILEPATH,
repo_root_dir=_THIS_DIR,
)
self.assertEqual(len(errors), 7)
self.assertEqual(len(warnings), 2)
class ValidationResultTest(unittest.TestCase):
"""Tests ValidationResult handles strings correctly."""
def test_ordering(self):
ve = metadata.validation_result.ValidationError(
"abc",
["message1", "message2"],
)
vw = metadata.validation_result.ValidationError(
"def",
["message3", "message4"],
)
# Check errors preceeds warnings.
self.assertLess(ve, vw)
self.assertGreater(vw, ve)
self.assertEqual([ve, vw], list(sorted([vw, ve])))
def test_message_generation(self):
ve = metadata.validation_result.ValidationError(
"abc",
["message1", "message2"],
)
self.assertEqual(
("Third party metadata issue: abc message1 message2 Check "
"//third_party/README.chromium.template for details."),
ve.get_message())
self.assertEqual("abc message1 message2",
ve.get_message(prescript='', postscript=''))
def test_getters(self):
ve = metadata.validation_result.ValidationError(
"abc",
["message1", "message2"],
)
self.assertEqual("abc", ve.get_reason())
self.assertEqual(["message1", "message2"], ve.get_additional())
class ValidationWithLineNumbers(unittest.TestCase):
def test_reports_line_number(self):
"""Checks validate reports line number if available."""
filepath = os.path.join(_THIS_DIR, "data",
"README.chromium.test.validation-line-number")
content = gclient_utils.FileRead(filepath)
unittest.mock.patch(
'metadata.fields.known.LICENSE_FILE.validate_on_disk',
return_value=metadata.validation_result.ValidationError(
"File doesn't exist."))
results = metadata.validate.validate_content(content,
"chromium/src/test_dir",
"chromium/src")
for r in results:
if r.get_reason() == 'License File is invalid.':
self.assertEqual(r.get_lines(), [10])
elif r.get_reason(
) == "Required field 'License Android Compatible' is missing.":
# We can't add a line number to errors caused by missing fields.
self.assertEqual(r.get_lines(), [])
elif r.get_reason() == "Versioning fields are insufficient.":
# We can't add a line number to errors caused by missing fields.
self.assertEqual(r.get_lines(), [])
elif r.get_reason(
) == "License has a license not in the allowlist.":
self.assertEqual(r.get_lines(), [9])
elif r.get_reason() == "URL is invalid.":
self.assertEqual(r.get_lines(), [2, 3, 4])
elif r.get_reason() == "Shipped in Chromium is invalid":
self.assertEqual(r.get_lines(), [13])
class ValidateReciprocalLicenseTest(unittest.TestCase):
"""Tests that validate_content handles allowing reciprocal licenses correctly."""
def test_reciprocal_licenses(self):
# Test content with a reciprocal license (MPL-2.0).
reciprocal_license_metadata_filepath = os.path.join(_THIS_DIR, "data",
"README.chromium.test.reciprocal-license")
# Without is_open_source_project, should get a warning.
results = metadata.validate.validate_content(
content=gclient_utils.FileRead(reciprocal_license_metadata_filepath),
source_file_dir=_SOURCE_FILE_DIR,
repo_root_dir=_THIS_DIR,
is_open_source_project=False
)
license_errors = []
for result in results:
if not result.is_fatal() and "License has a license not in the allowlist" in result.get_reason():
license_errors.append(result)
self.assertEqual(len(license_errors), 1, "Should create an error when a reciprocal license is used in a non-open source project")
# With is_open_source_project=True, should be no warnings.
results = metadata.validate.validate_content(
content=gclient_utils.FileRead(reciprocal_license_metadata_filepath),
source_file_dir=_SOURCE_FILE_DIR,
repo_root_dir=_THIS_DIR,
is_open_source_project=True
)
license_errors = []
for result in results:
if not result.is_fatal() and "License has a license not in the allowlist" in result.get_reason():
license_errors.append(result)
self.assertEqual(len(license_errors), 0, "Should not create an error when a reciprocal license is used in an open source project")
class ValidateRestrictedLicenseTest(unittest.TestCase):
"""Tests that validate_content handles allowing restricted licenses correctly."""
# TODO(b/388620886): Warn when changing to a restricted license.
def test_restricted_licenses(self):
# Test content with a restricted license (GPL-2.0).
restricted_license_metadata_filepath = os.path.join(_THIS_DIR, "data",
"README.chromium.test.restricted-license")
results = metadata.validate.validate_content(
content=gclient_utils.FileRead(restricted_license_metadata_filepath),
source_file_dir=_SOURCE_FILE_DIR,
repo_root_dir=_THIS_DIR,
is_open_source_project=False
)
self.assertEqual(len(results), 0, "Should not create an error when a restricted license is used")
if __name__ == "__main__":
unittest.main()