blob: 38d48ec3b253b0b772ba8dd781736e0c81864cff [file] [log] [blame]
# Copyright 2022 The ChromiumOS Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""This module tests the cros format command."""
import os
from pathlib import Path
from typing import List
from unittest import mock
from chromite.cli.cros import cros_format
from chromite.format import formatters
from chromite.lib import git
from chromite.lib import osutils
from chromite.scripts import cros
# pylint: disable=protected-access
def _call_cros_format(args: List[str]) -> int:
"""Call "cros format" with the given command line arguments.
Args:
args: The command line arguments.
Returns:
The return code of "cros format".
"""
return cros.main(["format"] + args)
def test_breakout_files_by_tool() -> None:
"""Check extension<->tool mapping."""
assert not cros_format._BreakoutFilesByTool([])
assert not cros_format._BreakoutFilesByTool([Path("foo"), Path("blah.xxx")])
tool_map = cros_format._BreakoutFilesByTool([Path("foo.md")])
# It's not easy to test the tool_map as the keys are functools partials
# which do not support equality tests.
items = list(tool_map.items())
assert len(items) == 1
key, value = items[0]
assert key is formatters.whitespace.Data
assert value == [Path("foo.md")]
def test_breakout_files_by_tool_order() -> None:
"""Verify we prefer names over extensions."""
tool_map = cros_format._BreakoutFilesByTool([Path("OWNERS.css")])
items = list(tool_map.items())
assert len(items) == 1
key, value = items[0]
assert key is formatters.whitespace.Data
assert value == [Path("OWNERS.css")]
@mock.patch.dict(
cros_format._TOOL_MAP,
{frozenset({"dir/foo.ZZZ"}): (mock.sentinel.tool,)},
)
def test_breakout_files_full_paths() -> None:
"""Verify we match files in named subdirs."""
source_files = sorted(
Path(x)
for x in (
"dir/foo.ZZZ",
"./dir/foo.ZZZ",
"../dir/foo.ZZZ",
"blah/dir/foo.ZZZ",
"/a/b/c/d/dir/foo.ZZZ",
)
)
tool_map = cros_format._BreakoutFilesByTool(source_files)
items = list(tool_map.items())
assert len(items) == 1
assert items[0][0] is mock.sentinel.tool
assert sorted(items[0][1]) == source_files
def test_cli_no_files(caplog) -> None:
"""Check cros format handling with no files."""
assert _call_cros_format([]) == 0
assert "No files found to process." in caplog.text
def test_cli_no_matched_files(caplog) -> None:
"""Check cros format handling with no matched files."""
assert _call_cros_format(["foo"]) == 0
assert "No files support formatting." in caplog.text
def test_cli_one_file(tmp_path) -> None:
"""Check behavior with one file."""
file = tmp_path / "foo.txt"
osutils.Touch(file)
assert _call_cros_format([str(file)]) == 0
def test_cli_dir(tmp_path) -> None:
"""Test the CLI expands directories when given one."""
files = [tmp_path / "foo.txt", tmp_path / "bar.txt"]
for file in files:
osutils.Touch(file)
assert _call_cros_format([str(tmp_path)]) == 0
def test_cli_many_files(tmp_path) -> None:
"""Check behavior with many files."""
files = []
for n in range(0, 10):
file = tmp_path / f"foo.{n}.txt"
osutils.Touch(file)
files.append(str(file))
assert _call_cros_format(files) == 0
def test_diff_file(tmp_path) -> None:
"""Check behavior with --diff file."""
file = tmp_path / "foo.txt"
file.write_text(" ", encoding="utf-8")
assert _call_cros_format(["--diff", str(file)]) == 1
assert " " == file.read_text(encoding="utf-8")
def test_check_file(tmp_path) -> None:
"""Check behavior with --check file."""
file = tmp_path / "foo.txt"
file.write_text(" ", encoding="utf-8")
for arg in ("-n", "--dry-run", "--check"):
assert _call_cros_format([arg, str(file)]) == 1
assert " " == file.read_text(encoding="utf-8")
def check_multiple_files(tmp_path, contents, expected_ret) -> None:
"""Helper to check behavior with --check with multiple files."""
files = []
for i, content in enumerate(contents):
file = tmp_path / f"foo.{i}.txt"
files.append(str(file))
file.write_text(content, encoding="utf-8")
for arg in ("-n", "--dry-run", "--check"):
assert _call_cros_format([arg, *files]) == expected_ret
def test_check_multiple_files_with_first_broken(tmp_path) -> None:
"""Check --check fails when the first supplied file is broken."""
check_multiple_files(tmp_path, [" ", ""], 1)
def test_check_multiple_files_with_last_broken(tmp_path) -> None:
"""Check --check fails when the last supplied file is broken."""
check_multiple_files(tmp_path, ["", " "], 1)
def test_stdout_file(tmp_path) -> None:
"""Check behavior with --stdout file."""
file = tmp_path / "foo.txt"
file.write_text(" ", encoding="utf-8")
assert _call_cros_format(["--stdout", str(file)]) == 1
assert " " == file.read_text(encoding="utf-8")
def test_inplace_file(tmp_path) -> None:
"""Check behavior with --inplace file."""
file = tmp_path / "foo.txt"
file.write_text(" ", encoding="utf-8")
assert _call_cros_format([str(file)]) == 0
assert "" == file.read_text(encoding="utf-8")
def test_missing_file(tmp_path) -> None:
"""Check behavior with missing files."""
file = tmp_path / "foo.py"
assert _call_cros_format([str(file)]) == os.EX_NOINPUT
def test_unicode_error(tmp_path) -> None:
"""Check binary files don't crash."""
file = tmp_path / "foo.txt"
file.write_bytes(b"\xff")
assert _call_cros_format([str(file)]) == os.EX_DATAERR
def test_parse_error_json(tmp_path) -> None:
"""Check JSON parsing errors don't crash."""
file = tmp_path / "foo.json"
file.write_bytes(b"{")
assert _call_cros_format([str(file)]) == os.EX_DATAERR
def test_parse_error_python(tmp_path) -> None:
"""Check Python parsing errors don't crash."""
file = tmp_path / "foo.py"
file.write_bytes(b"'")
assert _call_cros_format([str(file)]) == os.EX_DATAERR
def test_parse_error_xml(tmp_path) -> None:
"""Check XML parsing errors don't crash."""
file = tmp_path / "foo.xml"
file.write_bytes(b"<")
assert _call_cros_format([str(file)]) == os.EX_DATAERR
def _write_and_commit_space_file(file: Path) -> None:
"""Creates a git repository with a single, committed, file at getcwd()."""
repo_root = Path.cwd()
file.write_text(" ", encoding="utf-8")
git.Init(repo_root)
git.AddPath(file)
git.Commit(repo_root, message="test")
def test_commit_absolute_path(tmp_path, monkeypatch):
"""Check handling of --commit with an absolute file path."""
file = tmp_path / "foo.txt"
monkeypatch.chdir(tmp_path)
_write_and_commit_space_file(file)
assert _call_cros_format(["--commit", "HEAD", str(file)]) == 0
assert "" == file.read_text(encoding="utf-8")
def test_relative_path_in_root(tmp_path, monkeypatch):
"""Check handling of --commit with a relative path running in repo root."""
file = tmp_path / "foo.txt"
monkeypatch.chdir(tmp_path)
_write_and_commit_space_file(file)
assert _call_cros_format(["--commit", "HEAD", "foo.txt"]) == 0
assert "" == file.read_text(encoding="utf-8")
def test_relative_path_in_subdir(tmp_path, monkeypatch):
"""Check --commit with a relative path running in sibling path."""
dir1 = tmp_path / "dir1"
dir2 = tmp_path / "dir2"
dir1.mkdir()
dir2.mkdir()
file = dir1 / "foo.txt"
monkeypatch.chdir(tmp_path)
_write_and_commit_space_file(file)
monkeypatch.chdir(dir2)
assert _call_cros_format(["--commit", "HEAD", "../dir1/foo.txt"]) == 0
assert "" == file.read_text(encoding="utf-8")