script handling and string handling
This commit is contained in:
164
tests/unit/string_handling/test_byte_helpers.py
Normal file
164
tests/unit/string_handling/test_byte_helpers.py
Normal file
@@ -0,0 +1,164 @@
|
||||
"""
|
||||
PyTest: string_handling/byte_helpers
|
||||
"""
|
||||
|
||||
from corelibs.string_handling.byte_helpers import format_bytes
|
||||
|
||||
|
||||
class TestFormatBytes:
|
||||
"""Tests for format_bytes function"""
|
||||
|
||||
def test_string_input_returned_unchanged(self):
|
||||
"""Test that string inputs are returned as-is"""
|
||||
result = format_bytes("already formatted")
|
||||
assert result == "already formatted"
|
||||
|
||||
def test_empty_string_returned_unchanged(self):
|
||||
"""Test that empty strings are returned as-is"""
|
||||
result = format_bytes("")
|
||||
assert result == ""
|
||||
|
||||
def test_zero_int(self):
|
||||
"""Test zero integer returns 0 bytes"""
|
||||
result = format_bytes(0)
|
||||
assert result == "0.00 B"
|
||||
|
||||
def test_zero_float(self):
|
||||
"""Test zero float returns 0 bytes"""
|
||||
result = format_bytes(0.0)
|
||||
assert result == "0.00 B"
|
||||
|
||||
def test_none_value(self):
|
||||
"""Test None is treated as 0 bytes"""
|
||||
result = format_bytes(None) # type: ignore[arg-type]
|
||||
assert result == "0.00 B"
|
||||
|
||||
def test_bytes_less_than_1kb(self):
|
||||
"""Test formatting bytes less than 1KB"""
|
||||
result = format_bytes(512)
|
||||
assert result == "512.00 B"
|
||||
|
||||
def test_kilobytes(self):
|
||||
"""Test formatting kilobytes"""
|
||||
result = format_bytes(1024)
|
||||
assert result == "1.00 KB"
|
||||
|
||||
def test_kilobytes_with_decimals(self):
|
||||
"""Test formatting kilobytes with decimal values"""
|
||||
result = format_bytes(1536) # 1.5 KB
|
||||
assert result == "1.50 KB"
|
||||
|
||||
def test_megabytes(self):
|
||||
"""Test formatting megabytes"""
|
||||
result = format_bytes(1048576) # 1 MB
|
||||
assert result == "1.00 MB"
|
||||
|
||||
def test_megabytes_with_decimals(self):
|
||||
"""Test formatting megabytes with decimal values"""
|
||||
result = format_bytes(2621440) # 2.5 MB
|
||||
assert result == "2.50 MB"
|
||||
|
||||
def test_gigabytes(self):
|
||||
"""Test formatting gigabytes"""
|
||||
result = format_bytes(1073741824) # 1 GB
|
||||
assert result == "1.00 GB"
|
||||
|
||||
def test_terabytes(self):
|
||||
"""Test formatting terabytes"""
|
||||
result = format_bytes(1099511627776) # 1 TB
|
||||
assert result == "1.00 TB"
|
||||
|
||||
def test_petabytes(self):
|
||||
"""Test formatting petabytes"""
|
||||
result = format_bytes(1125899906842624) # 1 PB
|
||||
assert result == "1.00 PB"
|
||||
|
||||
def test_exabytes(self):
|
||||
"""Test formatting exabytes"""
|
||||
result = format_bytes(1152921504606846976) # 1 EB
|
||||
assert result == "1.00 EB"
|
||||
|
||||
def test_zettabytes(self):
|
||||
"""Test formatting zettabytes"""
|
||||
result = format_bytes(1180591620717411303424) # 1 ZB
|
||||
assert result == "1.00 ZB"
|
||||
|
||||
def test_yottabytes(self):
|
||||
"""Test formatting yottabytes"""
|
||||
result = format_bytes(1208925819614629174706176) # 1 YB
|
||||
assert result == "1.00 YB"
|
||||
|
||||
def test_negative_bytes(self):
|
||||
"""Test formatting negative byte values"""
|
||||
result = format_bytes(-512)
|
||||
assert result == "-512.00 B"
|
||||
|
||||
def test_negative_kilobytes(self):
|
||||
"""Test formatting negative kilobytes"""
|
||||
result = format_bytes(-1024)
|
||||
assert result == "-1.00 KB"
|
||||
|
||||
def test_negative_megabytes(self):
|
||||
"""Test formatting negative megabytes"""
|
||||
result = format_bytes(-1048576)
|
||||
assert result == "-1.00 MB"
|
||||
|
||||
def test_float_input_bytes(self):
|
||||
"""Test float input for bytes"""
|
||||
result = format_bytes(512.5)
|
||||
assert result == "512.50 B"
|
||||
|
||||
def test_float_input_kilobytes(self):
|
||||
"""Test float input for kilobytes"""
|
||||
result = format_bytes(1536.75)
|
||||
assert result == "1.50 KB"
|
||||
|
||||
def test_large_number_formatting(self):
|
||||
"""Test that large numbers use comma separators"""
|
||||
result = format_bytes(10240) # 10 KB
|
||||
assert result == "10.00 KB"
|
||||
|
||||
def test_very_large_byte_value(self):
|
||||
"""Test very large byte value (beyond ZB)"""
|
||||
result = format_bytes(1208925819614629174706176)
|
||||
assert result == "1.00 YB"
|
||||
|
||||
def test_boundary_1023_bytes(self):
|
||||
"""Test boundary case just below 1KB"""
|
||||
result = format_bytes(1023)
|
||||
assert result == "1,023.00 B"
|
||||
|
||||
def test_boundary_1024_bytes(self):
|
||||
"""Test boundary case at exactly 1KB"""
|
||||
result = format_bytes(1024)
|
||||
assert result == "1.00 KB"
|
||||
|
||||
def test_int_converted_to_float(self):
|
||||
"""Test that integer input is properly converted to float"""
|
||||
result = format_bytes(2048)
|
||||
assert result == "2.00 KB"
|
||||
assert "." in result # Verify decimal point is present
|
||||
|
||||
def test_small_decimal_value(self):
|
||||
"""Test small decimal byte value"""
|
||||
result = format_bytes(0.5)
|
||||
assert result == "0.50 B"
|
||||
|
||||
def test_precision_two_decimals(self):
|
||||
"""Test that result always has two decimal places"""
|
||||
result = format_bytes(1024)
|
||||
assert result == "1.00 KB"
|
||||
assert result.count('.') == 1
|
||||
decimal_part = result.split('.')[1].split()[0]
|
||||
assert len(decimal_part) == 2
|
||||
|
||||
def test_mixed_units_progression(self):
|
||||
"""Test progression through multiple unit levels"""
|
||||
# Start with bytes
|
||||
assert "B" in format_bytes(100)
|
||||
# Move to KB
|
||||
assert "KB" in format_bytes(100 * 1024)
|
||||
# Move to MB
|
||||
assert "MB" in format_bytes(100 * 1024 * 1024)
|
||||
# Move to GB
|
||||
assert "GB" in format_bytes(100 * 1024 * 1024 * 1024)
|
||||
524
tests/unit/string_handling/test_double_byte_format.py
Normal file
524
tests/unit/string_handling/test_double_byte_format.py
Normal file
@@ -0,0 +1,524 @@
|
||||
"""
|
||||
PyTest: string_handling/double_byte_string_format
|
||||
"""
|
||||
|
||||
import pytest
|
||||
from corelibs.string_handling.double_byte_string_format import DoubleByteFormatString
|
||||
|
||||
|
||||
class TestDoubleByteFormatStringInit:
|
||||
"""Tests for DoubleByteFormatString initialization"""
|
||||
|
||||
def test_basic_initialization(self):
|
||||
"""Test basic initialization with string and cut_length"""
|
||||
formatter = DoubleByteFormatString("Hello World", 10)
|
||||
assert formatter.string == "Hello World"
|
||||
assert formatter.cut_length == 10
|
||||
assert formatter.format_length == 10
|
||||
assert formatter.placeholder == ".."
|
||||
|
||||
def test_initialization_with_format_length(self):
|
||||
"""Test initialization with both cut_length and format_length"""
|
||||
formatter = DoubleByteFormatString("Hello World", 5, 15)
|
||||
assert formatter.cut_length == 5
|
||||
assert formatter.format_length == 15
|
||||
|
||||
def test_initialization_with_custom_placeholder(self):
|
||||
"""Test initialization with custom placeholder"""
|
||||
formatter = DoubleByteFormatString("Hello World", 10, placeholder="...")
|
||||
assert formatter.placeholder == "..."
|
||||
|
||||
def test_initialization_with_custom_format_string(self):
|
||||
"""Test initialization with custom format string"""
|
||||
formatter = DoubleByteFormatString("Hello", 10, format_string="{{:>{len}}}")
|
||||
assert formatter.format_string == "{{:>{len}}}"
|
||||
|
||||
def test_zero_cut_length_uses_string_width(self):
|
||||
"""Test that zero cut_length defaults to string width"""
|
||||
formatter = DoubleByteFormatString("Hello", 0)
|
||||
assert formatter.cut_length > 0
|
||||
# For ASCII string, width should equal length
|
||||
assert formatter.cut_length == 5
|
||||
|
||||
def test_negative_cut_length_uses_string_width(self):
|
||||
"""Test that negative cut_length defaults to string width"""
|
||||
formatter = DoubleByteFormatString("Hello", -5)
|
||||
assert formatter.cut_length > 0
|
||||
|
||||
def test_cut_length_adjusted_to_format_length(self):
|
||||
"""Test that cut_length is adjusted when larger than format_length"""
|
||||
formatter = DoubleByteFormatString("Hello World", 20, 10)
|
||||
assert formatter.cut_length == 10 # Should be min(20, 10)
|
||||
|
||||
def test_none_format_length(self):
|
||||
"""Test with None format_length"""
|
||||
formatter = DoubleByteFormatString("Hello", 10, None)
|
||||
assert formatter.format_length == 10 # Should default to cut_length
|
||||
|
||||
|
||||
class TestDoubleByteFormatStringWithAscii:
|
||||
"""Tests for ASCII (single-byte) string handling"""
|
||||
|
||||
def test_ascii_no_shortening_needed(self):
|
||||
"""Test ASCII string shorter than cut_length"""
|
||||
formatter = DoubleByteFormatString("Hello", 10)
|
||||
assert formatter.get_string_short() == "Hello"
|
||||
assert formatter.string_short_width == 0 # Not set because no shortening
|
||||
|
||||
def test_ascii_exact_cut_length(self):
|
||||
"""Test ASCII string equal to cut_length"""
|
||||
formatter = DoubleByteFormatString("Hello", 5)
|
||||
assert formatter.get_string_short() == "Hello"
|
||||
|
||||
def test_ascii_shortening_required(self):
|
||||
"""Test ASCII string requiring shortening"""
|
||||
formatter = DoubleByteFormatString("Hello World", 8)
|
||||
result = formatter.get_string_short()
|
||||
assert result == "Hello .."
|
||||
assert len(result) == 8
|
||||
|
||||
def test_ascii_with_custom_placeholder(self):
|
||||
"""Test ASCII shortening with custom placeholder"""
|
||||
formatter = DoubleByteFormatString("Hello World", 8, placeholder="...")
|
||||
result = formatter.get_string_short()
|
||||
assert result.endswith("...")
|
||||
assert len(result) == 8
|
||||
|
||||
def test_ascii_very_short_cut_length(self):
|
||||
"""Test ASCII with very short cut_length"""
|
||||
formatter = DoubleByteFormatString("Hello World", 3)
|
||||
result = formatter.get_string_short()
|
||||
assert result == "H.."
|
||||
assert len(result) == 3
|
||||
|
||||
def test_ascii_format_length_calculation(self):
|
||||
"""Test format_length calculation for ASCII strings"""
|
||||
formatter = DoubleByteFormatString("Hello", 10, 15)
|
||||
# String is not shortened, format_length should be 15
|
||||
assert formatter.get_format_length() == 15
|
||||
|
||||
|
||||
class TestDoubleByteFormatStringWithDoubleByte:
|
||||
"""Tests for double-byte (Asian) character handling"""
|
||||
|
||||
def test_japanese_characters(self):
|
||||
"""Test Japanese string handling"""
|
||||
formatter = DoubleByteFormatString("こんにちは", 10)
|
||||
# Each Japanese character is double-width
|
||||
# "こんにちは" = 5 chars * 2 width = 10 width
|
||||
assert formatter.get_string_short() == "こんにちは"
|
||||
|
||||
def test_japanese_shortening(self):
|
||||
"""Test Japanese string requiring shortening"""
|
||||
formatter = DoubleByteFormatString("こんにちは世界", 8)
|
||||
# Should fit 3 double-width chars (6 width) + placeholder (2 chars)
|
||||
result = formatter.get_string_short()
|
||||
assert result.endswith("..")
|
||||
assert len(result) <= 5 # 3 Japanese chars + 2 placeholder chars
|
||||
|
||||
def test_chinese_characters(self):
|
||||
"""Test Chinese string handling"""
|
||||
formatter = DoubleByteFormatString("你好世界", 8)
|
||||
# 4 Chinese chars = 8 width, should fit exactly
|
||||
assert formatter.get_string_short() == "你好世界"
|
||||
|
||||
def test_chinese_shortening(self):
|
||||
"""Test Chinese string requiring shortening"""
|
||||
formatter = DoubleByteFormatString("你好世界朋友", 8)
|
||||
# Should fit 3 double-width chars (6 width) + placeholder (2 chars)
|
||||
result = formatter.get_string_short()
|
||||
assert result.endswith("..")
|
||||
assert len(result) <= 5
|
||||
|
||||
def test_korean_characters(self):
|
||||
"""Test Korean string handling"""
|
||||
formatter = DoubleByteFormatString("안녕하세요", 10)
|
||||
# Korean characters are also double-width
|
||||
assert formatter.get_string_short() == "안녕하세요"
|
||||
|
||||
def test_mixed_ascii_japanese(self):
|
||||
"""Test mixed ASCII and Japanese characters"""
|
||||
formatter = DoubleByteFormatString("Hello世界", 10)
|
||||
# "Hello" = 5 width, "世界" = 4 width, total = 9 width
|
||||
assert formatter.get_string_short() == "Hello世界"
|
||||
|
||||
def test_mixed_ascii_japanese_shortening(self):
|
||||
"""Test mixed string requiring shortening"""
|
||||
formatter = DoubleByteFormatString("Hello世界Test", 10)
|
||||
# Should shorten to fit within 10 width
|
||||
result = formatter.get_string_short()
|
||||
assert result.endswith("..")
|
||||
# Total visual width should be <= 10
|
||||
|
||||
def test_fullwidth_ascii(self):
|
||||
"""Test fullwidth ASCII characters"""
|
||||
# Fullwidth ASCII characters (U+FF01 to U+FF5E)
|
||||
formatter = DoubleByteFormatString("HELLOworld", 10)
|
||||
result = formatter.get_string_short()
|
||||
assert result.endswith("..")
|
||||
|
||||
|
||||
class TestDoubleByteFormatStringGetters:
|
||||
"""Tests for getter methods"""
|
||||
|
||||
def test_get_string_short(self):
|
||||
"""Test get_string_short method"""
|
||||
formatter = DoubleByteFormatString("Hello World", 8)
|
||||
result = formatter.get_string_short()
|
||||
assert isinstance(result, str)
|
||||
assert result == "Hello .."
|
||||
|
||||
def test_get_format_length(self):
|
||||
"""Test get_format_length method"""
|
||||
formatter = DoubleByteFormatString("Hello", 5, 10)
|
||||
assert formatter.get_format_length() == 10
|
||||
|
||||
def test_get_cut_length(self):
|
||||
"""Test get_cut_length method"""
|
||||
formatter = DoubleByteFormatString("Hello", 8)
|
||||
assert formatter.get_cut_length() == 8
|
||||
|
||||
def test_get_requested_cut_length(self):
|
||||
"""Test get_requested_cut_length method"""
|
||||
formatter = DoubleByteFormatString("Hello", 15)
|
||||
assert formatter.get_requested_cut_length() == 15
|
||||
|
||||
def test_get_requested_format_length(self):
|
||||
"""Test get_requested_format_length method"""
|
||||
formatter = DoubleByteFormatString("Hello", 5, 20)
|
||||
assert formatter.get_requested_format_length() == 20
|
||||
|
||||
def test_get_string_short_formated_default(self):
|
||||
"""Test get_string_short_formated with default format"""
|
||||
formatter = DoubleByteFormatString("Hello", 5, 10)
|
||||
result = formatter.get_string_short_formated()
|
||||
assert isinstance(result, str)
|
||||
assert len(result) == 10 # Should be padded to format_length
|
||||
assert result.startswith("Hello")
|
||||
|
||||
def test_get_string_short_formated_custom(self):
|
||||
"""Test get_string_short_formated with custom format string"""
|
||||
formatter = DoubleByteFormatString("Hello", 5, 10)
|
||||
result = formatter.get_string_short_formated("{{:>{len}}}")
|
||||
assert isinstance(result, str)
|
||||
assert result.endswith("Hello") # Right-aligned
|
||||
|
||||
def test_get_string_short_formated_empty_format_string(self):
|
||||
"""Test get_string_short_formated with empty format string falls back to default"""
|
||||
formatter = DoubleByteFormatString("Hello", 5, 10)
|
||||
result = formatter.get_string_short_formated("")
|
||||
# Should use default format_string from initialization
|
||||
assert isinstance(result, str)
|
||||
|
||||
|
||||
class TestDoubleByteFormatStringFormatting:
|
||||
"""Tests for formatted output"""
|
||||
|
||||
def test_format_with_padding(self):
|
||||
"""Test formatted string with padding"""
|
||||
formatter = DoubleByteFormatString("Hello", 5, 10)
|
||||
result = formatter.get_string_short_formated()
|
||||
assert len(result) == 10
|
||||
assert result == "Hello " # Left-aligned with spaces
|
||||
|
||||
def test_format_shortened_string(self):
|
||||
"""Test formatted shortened string"""
|
||||
formatter = DoubleByteFormatString("Hello World", 8, 12)
|
||||
result = formatter.get_string_short_formated()
|
||||
# Should be "Hello .." padded to 12
|
||||
assert len(result) == 12
|
||||
assert result.startswith("Hello ..")
|
||||
|
||||
def test_format_with_double_byte_chars(self):
|
||||
"""Test formatting with double-byte characters"""
|
||||
formatter = DoubleByteFormatString("日本語", 6, 10)
|
||||
result = formatter.get_string_short_formated()
|
||||
# "日本語" = 3 chars * 2 width = 6 width
|
||||
# Format should account for visual width difference
|
||||
assert isinstance(result, str)
|
||||
|
||||
def test_format_shortened_double_byte(self):
|
||||
"""Test formatting shortened double-byte string"""
|
||||
formatter = DoubleByteFormatString("こんにちは世界", 8, 12)
|
||||
result = formatter.get_string_short_formated()
|
||||
assert isinstance(result, str)
|
||||
# Should be shortened and formatted
|
||||
|
||||
|
||||
class TestDoubleByteFormatStringProcess:
|
||||
"""Tests for process method"""
|
||||
|
||||
def test_process_called_on_init(self):
|
||||
"""Test that process is called during initialization"""
|
||||
formatter = DoubleByteFormatString("Hello World", 8)
|
||||
# process() should have been called, so string_short should be set
|
||||
assert formatter.string_short != ''
|
||||
|
||||
def test_manual_process_call(self):
|
||||
"""Test calling process manually"""
|
||||
formatter = DoubleByteFormatString("Hello World", 8)
|
||||
# Modify internal state
|
||||
formatter.string = "New String"
|
||||
# Call process again
|
||||
formatter.process()
|
||||
# Should recalculate based on new string
|
||||
assert formatter.string_short != ''
|
||||
|
||||
def test_process_with_empty_string(self):
|
||||
"""Test process with empty string"""
|
||||
formatter = DoubleByteFormatString("", 10)
|
||||
formatter.process()
|
||||
# Should handle empty string gracefully
|
||||
assert formatter.string_short == ''
|
||||
|
||||
|
||||
class TestDoubleByteFormatStringEdgeCases:
|
||||
"""Tests for edge cases"""
|
||||
|
||||
def test_empty_string(self):
|
||||
"""Test with empty string"""
|
||||
formatter = DoubleByteFormatString("", 10)
|
||||
assert formatter.get_string_short() == ""
|
||||
|
||||
def test_single_character(self):
|
||||
"""Test with single character"""
|
||||
formatter = DoubleByteFormatString("A", 5)
|
||||
assert formatter.get_string_short() == "A"
|
||||
|
||||
def test_single_double_byte_character(self):
|
||||
"""Test with single double-byte character"""
|
||||
formatter = DoubleByteFormatString("日", 5)
|
||||
assert formatter.get_string_short() == "日"
|
||||
|
||||
def test_placeholder_only_length(self):
|
||||
"""Test when cut_length equals placeholder length"""
|
||||
formatter = DoubleByteFormatString("Hello World", 2)
|
||||
result = formatter.get_string_short()
|
||||
assert result == ".."
|
||||
|
||||
def test_very_long_string(self):
|
||||
"""Test with very long string"""
|
||||
long_string = "A" * 1000
|
||||
formatter = DoubleByteFormatString(long_string, 10)
|
||||
result = formatter.get_string_short()
|
||||
assert len(result) == 10
|
||||
assert result.endswith("..")
|
||||
|
||||
def test_very_long_double_byte_string(self):
|
||||
"""Test with very long double-byte string"""
|
||||
long_string = "あ" * 500
|
||||
formatter = DoubleByteFormatString(long_string, 10)
|
||||
result = formatter.get_string_short()
|
||||
# Should be shortened to fit 10 visual width
|
||||
assert result.endswith("..")
|
||||
|
||||
def test_special_characters(self):
|
||||
"""Test with special characters"""
|
||||
formatter = DoubleByteFormatString("Hello!@#$%^&*()", 10)
|
||||
result = formatter.get_string_short()
|
||||
assert isinstance(result, str)
|
||||
|
||||
def test_newlines_and_tabs(self):
|
||||
"""Test with newlines and tabs"""
|
||||
formatter = DoubleByteFormatString("Hello\nWorld\t!", 10)
|
||||
result = formatter.get_string_short()
|
||||
assert isinstance(result, str)
|
||||
|
||||
def test_unicode_emoji(self):
|
||||
"""Test with Unicode emoji"""
|
||||
formatter = DoubleByteFormatString("Hello 👋 World 🌍", 15)
|
||||
result = formatter.get_string_short()
|
||||
assert isinstance(result, str)
|
||||
|
||||
def test_non_string_input_conversion(self):
|
||||
"""Test that non-string inputs are converted to string"""
|
||||
formatter = DoubleByteFormatString(12345, 10) # type: ignore[arg-type]
|
||||
assert formatter.string == "12345"
|
||||
assert formatter.get_string_short() == "12345"
|
||||
|
||||
def test_none_conversion(self):
|
||||
"""Test None conversion to string"""
|
||||
formatter = DoubleByteFormatString(None, 10) # type: ignore[arg-type]
|
||||
assert formatter.string == "None"
|
||||
|
||||
|
||||
class TestDoubleByteFormatStringWidthCalculation:
|
||||
"""Tests for width calculation accuracy"""
|
||||
|
||||
def test_ascii_width_calculation(self):
|
||||
"""Test width calculation for ASCII"""
|
||||
formatter = DoubleByteFormatString("Hello", 10)
|
||||
formatter.process()
|
||||
# ASCII characters should have width = length
|
||||
assert formatter.string_width_value == 5
|
||||
|
||||
def test_japanese_width_calculation(self):
|
||||
"""Test width calculation for Japanese"""
|
||||
formatter = DoubleByteFormatString("こんにちは", 20)
|
||||
formatter.process()
|
||||
# 5 Japanese characters * 2 width each = 10
|
||||
assert formatter.string_width_value == 10
|
||||
|
||||
def test_mixed_width_calculation(self):
|
||||
"""Test width calculation for mixed characters"""
|
||||
formatter = DoubleByteFormatString("Hello日本", 20)
|
||||
formatter.process()
|
||||
# "Hello" = 5 width, "日本" = 4 width, total = 9
|
||||
assert formatter.string_width_value == 9
|
||||
|
||||
def test_fullwidth_latin_calculation(self):
|
||||
"""Test width calculation for fullwidth Latin characters"""
|
||||
# Fullwidth Latin letters
|
||||
formatter = DoubleByteFormatString("ABC", 10)
|
||||
formatter.process()
|
||||
# 3 fullwidth characters * 2 width each = 6
|
||||
assert formatter.string_width_value == 6
|
||||
|
||||
|
||||
# Parametrized tests
|
||||
@pytest.mark.parametrize("string,cut_length,expected_short", [
|
||||
("Hello", 10, "Hello"),
|
||||
("Hello World", 8, "Hello .."),
|
||||
("Hello World Test", 5, "Hel.."),
|
||||
("", 5, ""),
|
||||
("A", 5, "A"),
|
||||
])
|
||||
def test_ascii_shortening_parametrized(string: str, cut_length: int, expected_short: str):
|
||||
"""Parametrized test for ASCII string shortening"""
|
||||
formatter = DoubleByteFormatString(string, cut_length)
|
||||
assert formatter.get_string_short() == expected_short
|
||||
|
||||
|
||||
@pytest.mark.parametrize("string,cut_length,format_length,expected_format_len", [
|
||||
("Hello", 5, 10, 10),
|
||||
("Hello", 10, 5, 5),
|
||||
("Hello World", 8, 12, 12),
|
||||
])
|
||||
def test_format_length_parametrized(
|
||||
string: str,
|
||||
cut_length: int,
|
||||
format_length: int,
|
||||
expected_format_len: int
|
||||
):
|
||||
"""Parametrized test for format length"""
|
||||
formatter = DoubleByteFormatString(string, cut_length, format_length)
|
||||
assert formatter.get_format_length() == expected_format_len
|
||||
|
||||
|
||||
@pytest.mark.parametrize("string,expected_width", [
|
||||
("Hello", 5),
|
||||
("こんにちは", 10), # 5 Japanese chars * 2
|
||||
("Hello日本", 9), # 5 + 4
|
||||
("", 0),
|
||||
("A", 1),
|
||||
("日", 2),
|
||||
])
|
||||
def test_width_calculation_parametrized(string: str, expected_width: int):
|
||||
"""Parametrized test for width calculation"""
|
||||
formatter = DoubleByteFormatString(string, 100) # Large cut_length to avoid shortening
|
||||
formatter.process()
|
||||
if string:
|
||||
assert formatter.string_width_value == expected_width
|
||||
else:
|
||||
assert formatter.string_width_value == 0
|
||||
|
||||
|
||||
@pytest.mark.parametrize("placeholder", [
|
||||
"..",
|
||||
"...",
|
||||
"—",
|
||||
">>>",
|
||||
"~",
|
||||
])
|
||||
def test_custom_placeholder_parametrized(placeholder: str):
|
||||
"""Parametrized test for custom placeholders"""
|
||||
formatter = DoubleByteFormatString("Hello World Test", 8, placeholder=placeholder)
|
||||
result = formatter.get_string_short()
|
||||
assert result.endswith(placeholder)
|
||||
assert len(result) == 8
|
||||
|
||||
|
||||
class TestDoubleByteFormatStringIntegration:
|
||||
"""Integration tests for complete workflows"""
|
||||
|
||||
def test_complete_workflow_ascii(self):
|
||||
"""Test complete workflow with ASCII string"""
|
||||
formatter = DoubleByteFormatString("Hello World", 8, 12)
|
||||
short = formatter.get_string_short()
|
||||
formatted = formatter.get_string_short_formated()
|
||||
|
||||
assert short == "Hello .."
|
||||
assert len(formatted) == 12
|
||||
assert formatted.startswith("Hello ..")
|
||||
|
||||
def test_complete_workflow_japanese(self):
|
||||
"""Test complete workflow with Japanese string"""
|
||||
formatter = DoubleByteFormatString("こんにちは世界", 8, 12)
|
||||
short = formatter.get_string_short()
|
||||
formatted = formatter.get_string_short_formated()
|
||||
|
||||
assert short.endswith("..")
|
||||
assert isinstance(formatted, str)
|
||||
|
||||
def test_complete_workflow_mixed(self):
|
||||
"""Test complete workflow with mixed characters"""
|
||||
formatter = DoubleByteFormatString("Hello世界World", 10, 15)
|
||||
short = formatter.get_string_short()
|
||||
formatted = formatter.get_string_short_formated()
|
||||
|
||||
assert short.endswith("..")
|
||||
assert isinstance(formatted, str)
|
||||
|
||||
def test_table_like_output(self):
|
||||
"""Test creating table-like output with multiple formatters"""
|
||||
items = [
|
||||
("Name", "Alice", 10, 15),
|
||||
("City", "Tokyo東京", 10, 15),
|
||||
("Country", "Japan日本国", 10, 15),
|
||||
]
|
||||
|
||||
results: list[str] = []
|
||||
for _label, value, cut, fmt in items:
|
||||
formatter = DoubleByteFormatString(value, cut, fmt)
|
||||
results.append(formatter.get_string_short_formated())
|
||||
|
||||
# All results should be formatted strings
|
||||
# Note: Due to double-byte character width adjustments,
|
||||
# the actual string length may differ from format_length
|
||||
assert all(isinstance(result, str) for result in results)
|
||||
assert all(len(result) > 0 for result in results)
|
||||
|
||||
def test_reprocess_after_modification(self):
|
||||
"""Test reprocessing after modifying formatter properties"""
|
||||
formatter = DoubleByteFormatString("Hello World", 8, 12)
|
||||
initial = formatter.get_string_short()
|
||||
|
||||
# Modify and reprocess
|
||||
formatter.string = "New String Test"
|
||||
formatter.process()
|
||||
modified = formatter.get_string_short()
|
||||
|
||||
assert initial != modified
|
||||
assert modified.endswith("..")
|
||||
|
||||
|
||||
class TestDoubleByteFormatStringRightAlignment:
|
||||
"""Tests for right-aligned formatting"""
|
||||
|
||||
def test_right_aligned_format(self):
|
||||
"""Test right-aligned formatting"""
|
||||
formatter = DoubleByteFormatString("Hello", 5, 10, format_string="{{:>{len}}}")
|
||||
result = formatter.get_string_short_formated()
|
||||
assert len(result) == 10
|
||||
# The format applies to the short string
|
||||
assert "Hello" in result
|
||||
|
||||
def test_center_aligned_format(self):
|
||||
"""Test center-aligned formatting"""
|
||||
formatter = DoubleByteFormatString("Hello", 5, 11, format_string="{{:^{len}}}")
|
||||
result = formatter.get_string_short_formated()
|
||||
assert len(result) == 11
|
||||
assert "Hello" in result
|
||||
|
||||
|
||||
# __END__
|
||||
328
tests/unit/string_handling/test_hash_helpers.py
Normal file
328
tests/unit/string_handling/test_hash_helpers.py
Normal file
@@ -0,0 +1,328 @@
|
||||
"""
|
||||
PyTest: string_handling/hash_helpers
|
||||
"""
|
||||
|
||||
import pytest
|
||||
from corelibs.string_handling.hash_helpers import (
|
||||
crc32b_fix, sha1_short
|
||||
)
|
||||
|
||||
|
||||
class TestCrc32bFix:
|
||||
"""Tests for crc32b_fix function"""
|
||||
|
||||
def test_basic_crc_fix(self):
|
||||
"""Test basic CRC32B byte order fix"""
|
||||
# Example: if input is "abcdefgh", it should become "ghefcdab"
|
||||
result = crc32b_fix("abcdefgh")
|
||||
assert result == "ghefcdab"
|
||||
|
||||
def test_short_crc_padding(self):
|
||||
"""Test that short CRC is left-padded with zeros"""
|
||||
# Input with 6 chars should be padded to 8: "00abcdef"
|
||||
# Split into pairs: "00", "ab", "cd", "ef"
|
||||
# Reversed: "ef", "cd", "ab", "00"
|
||||
result = crc32b_fix("abcdef")
|
||||
assert result == "efcdab00"
|
||||
assert len(result) == 8
|
||||
|
||||
def test_4_char_crc(self):
|
||||
"""Test CRC with 4 characters"""
|
||||
# Padded: "0000abcd"
|
||||
# Pairs: "00", "00", "ab", "cd"
|
||||
# Reversed: "cd", "ab", "00", "00"
|
||||
result = crc32b_fix("abcd")
|
||||
assert result == "cdab0000"
|
||||
assert len(result) == 8
|
||||
|
||||
def test_2_char_crc(self):
|
||||
"""Test CRC with 2 characters"""
|
||||
# Padded: "000000ab"
|
||||
# Pairs: "00", "00", "00", "ab"
|
||||
# Reversed: "ab", "00", "00", "00"
|
||||
result = crc32b_fix("ab")
|
||||
assert result == "ab000000"
|
||||
assert len(result) == 8
|
||||
|
||||
def test_1_char_crc(self):
|
||||
"""Test CRC with 1 character"""
|
||||
# Padded: "0000000a"
|
||||
# Pairs: "00", "00", "00", "0a"
|
||||
# Reversed: "0a", "00", "00", "00"
|
||||
result = crc32b_fix("a")
|
||||
assert result == "0a000000"
|
||||
assert len(result) == 8
|
||||
|
||||
def test_empty_crc(self):
|
||||
"""Test empty CRC string"""
|
||||
result = crc32b_fix("")
|
||||
assert result == "00000000"
|
||||
assert len(result) == 8
|
||||
|
||||
def test_numeric_crc(self):
|
||||
"""Test CRC with numeric characters"""
|
||||
result = crc32b_fix("12345678")
|
||||
assert result == "78563412"
|
||||
|
||||
def test_mixed_alphanumeric(self):
|
||||
"""Test CRC with mixed alphanumeric characters"""
|
||||
result = crc32b_fix("a1b2c3d4")
|
||||
assert result == "d4c3b2a1"
|
||||
|
||||
def test_lowercase_letters(self):
|
||||
"""Test CRC with lowercase letters"""
|
||||
result = crc32b_fix("aabbccdd")
|
||||
assert result == "ddccbbaa"
|
||||
|
||||
def test_with_numbers_and_letters(self):
|
||||
"""Test CRC with numbers and letters (typical hex)"""
|
||||
result = crc32b_fix("1a2b3c4d")
|
||||
assert result == "4d3c2b1a"
|
||||
|
||||
def test_all_zeros(self):
|
||||
"""Test CRC with all zeros"""
|
||||
result = crc32b_fix("00000000")
|
||||
assert result == "00000000"
|
||||
|
||||
def test_short_padding_all_numbers(self):
|
||||
"""Test padding with all numbers"""
|
||||
# Padded: "00123456"
|
||||
# Pairs: "00", "12", "34", "56"
|
||||
# Reversed: "56", "34", "12", "00"
|
||||
result = crc32b_fix("123456")
|
||||
assert result == "56341200"
|
||||
assert len(result) == 8
|
||||
|
||||
def test_typical_hex_values(self):
|
||||
"""Test with typical hexadecimal hash values"""
|
||||
result = crc32b_fix("a1b2c3d4")
|
||||
assert result == "d4c3b2a1"
|
||||
|
||||
def test_7_char_crc(self):
|
||||
"""Test CRC with 7 characters (needs 1 zero padding)"""
|
||||
# Padded: "0abcdefg"
|
||||
# Pairs: "0a", "bc", "de", "fg"
|
||||
# Reversed: "fg", "de", "bc", "0a"
|
||||
result = crc32b_fix("abcdefg")
|
||||
assert result == "fgdebc0a"
|
||||
assert len(result) == 8
|
||||
|
||||
|
||||
class TestSha1Short:
|
||||
"""Tests for sha1_short function"""
|
||||
|
||||
def test_basic_sha1_short(self):
|
||||
"""Test basic SHA1 short hash generation"""
|
||||
result = sha1_short("hello")
|
||||
assert len(result) == 9
|
||||
assert result.isalnum() # Should be hexadecimal
|
||||
|
||||
def test_consistent_output(self):
|
||||
"""Test that same input produces same output"""
|
||||
result1 = sha1_short("test")
|
||||
result2 = sha1_short("test")
|
||||
assert result1 == result2
|
||||
|
||||
def test_different_inputs_different_outputs(self):
|
||||
"""Test that different inputs produce different outputs"""
|
||||
result1 = sha1_short("hello")
|
||||
result2 = sha1_short("world")
|
||||
assert result1 != result2
|
||||
|
||||
def test_empty_string(self):
|
||||
"""Test SHA1 of empty string"""
|
||||
result = sha1_short("")
|
||||
assert len(result) == 9
|
||||
# SHA1 of empty string is known: "da39a3ee5e6b4b0d3255bfef95601890afd80709"
|
||||
assert result == "da39a3ee5"
|
||||
|
||||
def test_single_character(self):
|
||||
"""Test SHA1 of single character"""
|
||||
result = sha1_short("a")
|
||||
assert len(result) == 9
|
||||
# SHA1 of "a" is "86f7e437faa5a7fce15d1ddcb9eaeaea377667b8"
|
||||
assert result == "86f7e437f"
|
||||
|
||||
def test_long_string(self):
|
||||
"""Test SHA1 of long string"""
|
||||
long_string = "a" * 1000
|
||||
result = sha1_short(long_string)
|
||||
assert len(result) == 9
|
||||
assert result.isalnum()
|
||||
|
||||
def test_special_characters(self):
|
||||
"""Test SHA1 with special characters"""
|
||||
result = sha1_short("hello@world!")
|
||||
assert len(result) == 9
|
||||
assert result.isalnum()
|
||||
|
||||
def test_unicode_characters(self):
|
||||
"""Test SHA1 with unicode characters"""
|
||||
result = sha1_short("こんにちは")
|
||||
assert len(result) == 9
|
||||
assert result.isalnum()
|
||||
|
||||
def test_numbers(self):
|
||||
"""Test SHA1 with numeric string"""
|
||||
result = sha1_short("12345")
|
||||
assert len(result) == 9
|
||||
assert result.isalnum()
|
||||
|
||||
def test_whitespace(self):
|
||||
"""Test SHA1 with whitespace"""
|
||||
result1 = sha1_short("hello world")
|
||||
result2 = sha1_short("helloworld")
|
||||
assert result1 != result2
|
||||
assert len(result1) == 9
|
||||
assert len(result2) == 9
|
||||
|
||||
def test_newlines_and_tabs(self):
|
||||
"""Test SHA1 with newlines and tabs"""
|
||||
result = sha1_short("hello\nworld\ttab")
|
||||
assert len(result) == 9
|
||||
assert result.isalnum()
|
||||
|
||||
def test_mixed_case(self):
|
||||
"""Test SHA1 with mixed case (should be case sensitive)"""
|
||||
result1 = sha1_short("Hello")
|
||||
result2 = sha1_short("hello")
|
||||
assert result1 != result2
|
||||
|
||||
def test_hexadecimal_output(self):
|
||||
"""Test that output is valid hexadecimal"""
|
||||
result = sha1_short("test")
|
||||
# Should only contain 0-9 and a-f
|
||||
assert all(c in "0123456789abcdef" for c in result)
|
||||
|
||||
def test_known_value_verification(self):
|
||||
"""Test against known SHA1 values"""
|
||||
# SHA1 of "hello" is "aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d"
|
||||
result = sha1_short("hello")
|
||||
assert result == "aaf4c61dd"
|
||||
|
||||
def test_numeric_string_input(self):
|
||||
"""Test with numeric string"""
|
||||
result = sha1_short("123456789")
|
||||
assert len(result) == 9
|
||||
assert result.isalnum()
|
||||
|
||||
def test_emoji_input(self):
|
||||
"""Test with emoji characters"""
|
||||
result = sha1_short("😀🎉")
|
||||
assert len(result) == 9
|
||||
assert result.isalnum()
|
||||
|
||||
def test_multiline_string(self):
|
||||
"""Test with multiline string"""
|
||||
multiline = """This is
|
||||
a multiline
|
||||
string"""
|
||||
result = sha1_short(multiline)
|
||||
assert len(result) == 9
|
||||
assert result.isalnum()
|
||||
|
||||
|
||||
# Parametrized tests
|
||||
@pytest.mark.parametrize("input_crc,expected", [
|
||||
("abcdefgh", "ghefcdab"),
|
||||
("12345678", "78563412"),
|
||||
("aabbccdd", "ddccbbaa"),
|
||||
("00000000", "00000000"),
|
||||
("", "00000000"),
|
||||
("a", "0a000000"),
|
||||
("ab", "ab000000"),
|
||||
("abcd", "cdab0000"),
|
||||
("abcdef", "efcdab00"),
|
||||
])
|
||||
def test_crc32b_fix_parametrized(input_crc: str, expected: str):
|
||||
"""Parametrized test for crc32b_fix"""
|
||||
result = crc32b_fix(input_crc)
|
||||
assert len(result) == 8
|
||||
assert result == expected
|
||||
|
||||
|
||||
@pytest.mark.parametrize("input_string,expected_length", [
|
||||
("hello", 9),
|
||||
("world", 9),
|
||||
("", 9),
|
||||
("a" * 1000, 9),
|
||||
("test123", 9),
|
||||
("😀", 9),
|
||||
])
|
||||
def test_sha1_short_parametrized_length(input_string: str, expected_length: int):
|
||||
"""Parametrized test for sha1_short to verify consistent length"""
|
||||
result = sha1_short(input_string)
|
||||
assert len(result) == expected_length
|
||||
|
||||
|
||||
@pytest.mark.parametrize("input_string,expected_hash", [
|
||||
("", "da39a3ee5"),
|
||||
("a", "86f7e437f"),
|
||||
("hello", "aaf4c61dd"),
|
||||
("world", "7c211433f"),
|
||||
("test", "a94a8fe5c"),
|
||||
])
|
||||
def test_sha1_short_known_values(input_string: str, expected_hash: str):
|
||||
"""Parametrized test for sha1_short with known SHA1 values"""
|
||||
result = sha1_short(input_string)
|
||||
assert result == expected_hash
|
||||
|
||||
|
||||
# Edge case tests
|
||||
class TestEdgeCases:
|
||||
"""Test edge cases for hash helper functions"""
|
||||
|
||||
def test_crc32b_fix_with_max_length(self):
|
||||
"""Test crc32b_fix with exactly 8 characters"""
|
||||
result = crc32b_fix("ffffffff")
|
||||
assert result == "ffffffff"
|
||||
assert len(result) == 8
|
||||
|
||||
def test_sha1_short_very_long_input(self):
|
||||
"""Test sha1_short with very long input"""
|
||||
very_long = "x" * 10000
|
||||
result = sha1_short(very_long)
|
||||
assert len(result) == 9
|
||||
assert result.isalnum()
|
||||
|
||||
def test_sha1_short_binary_like_string(self):
|
||||
"""Test sha1_short with binary-like string"""
|
||||
result = sha1_short("\x00\x01\x02\x03")
|
||||
assert len(result) == 9
|
||||
assert result.isalnum()
|
||||
|
||||
def test_crc32b_fix_preserves_characters(self):
|
||||
"""Test that crc32b_fix only reorders, doesn't change characters"""
|
||||
input_crc = "12345678"
|
||||
result = crc32b_fix(input_crc)
|
||||
# All characters from input should be in output (after padding)
|
||||
for char in input_crc:
|
||||
assert char in result or '0' in result # 0 is for padding
|
||||
|
||||
|
||||
# Integration tests
|
||||
class TestIntegration:
|
||||
"""Integration tests for hash helper functions"""
|
||||
|
||||
def test_sha1_short_produces_valid_crc_input(self):
|
||||
"""Test that sha1_short output could be used as CRC input"""
|
||||
sha1_result = sha1_short("test")
|
||||
# SHA1 short is 9 chars, CRC expects up to 8, so take first 8
|
||||
crc_input = sha1_result[:8]
|
||||
crc_result = crc32b_fix(crc_input)
|
||||
assert len(crc_result) == 8
|
||||
|
||||
def test_multiple_sha1_short_consistency(self):
|
||||
"""Test that multiple calls to sha1_short are consistent"""
|
||||
results = [sha1_short("consistency_test") for _ in range(10)]
|
||||
assert all(r == results[0] for r in results)
|
||||
|
||||
def test_crc32b_fix_reversibility_concept(self):
|
||||
"""Test that applying crc32b_fix twice reverses the operation"""
|
||||
original = "abcdefgh"
|
||||
fixed_once = crc32b_fix(original)
|
||||
fixed_twice = crc32b_fix(fixed_once)
|
||||
assert fixed_twice == original
|
||||
|
||||
|
||||
# __END__
|
||||
516
tests/unit/string_handling/test_text_colors.py
Normal file
516
tests/unit/string_handling/test_text_colors.py
Normal file
@@ -0,0 +1,516 @@
|
||||
"""
|
||||
PyTest: string_handling/text_colors
|
||||
"""
|
||||
|
||||
import pytest
|
||||
from corelibs.string_handling.text_colors import Colors
|
||||
|
||||
|
||||
class TestColorsInitialState:
|
||||
"""Tests for Colors class initial state"""
|
||||
|
||||
def test_bold_initial_value(self):
|
||||
"""Test that bold has correct ANSI code"""
|
||||
assert Colors.bold == '\033[1m'
|
||||
|
||||
def test_underline_initial_value(self):
|
||||
"""Test that underline has correct ANSI code"""
|
||||
assert Colors.underline == '\033[4m'
|
||||
|
||||
def test_end_initial_value(self):
|
||||
"""Test that end has correct ANSI code"""
|
||||
assert Colors.end == '\033[0m'
|
||||
|
||||
def test_reset_initial_value(self):
|
||||
"""Test that reset has correct ANSI code"""
|
||||
assert Colors.reset == '\033[0m'
|
||||
|
||||
|
||||
class TestColorsNormal:
|
||||
"""Tests for normal color ANSI codes"""
|
||||
|
||||
def test_black_normal(self):
|
||||
"""Test black color code"""
|
||||
assert Colors.black == "\033[30m"
|
||||
|
||||
def test_red_normal(self):
|
||||
"""Test red color code"""
|
||||
assert Colors.red == "\033[31m"
|
||||
|
||||
def test_green_normal(self):
|
||||
"""Test green color code"""
|
||||
assert Colors.green == "\033[32m"
|
||||
|
||||
def test_yellow_normal(self):
|
||||
"""Test yellow color code"""
|
||||
assert Colors.yellow == "\033[33m"
|
||||
|
||||
def test_blue_normal(self):
|
||||
"""Test blue color code"""
|
||||
assert Colors.blue == "\033[34m"
|
||||
|
||||
def test_magenta_normal(self):
|
||||
"""Test magenta color code"""
|
||||
assert Colors.magenta == "\033[35m"
|
||||
|
||||
def test_cyan_normal(self):
|
||||
"""Test cyan color code"""
|
||||
assert Colors.cyan == "\033[36m"
|
||||
|
||||
def test_white_normal(self):
|
||||
"""Test white color code"""
|
||||
assert Colors.white == "\033[37m"
|
||||
|
||||
|
||||
class TestColorsBold:
|
||||
"""Tests for bold color ANSI codes"""
|
||||
|
||||
def test_black_bold(self):
|
||||
"""Test black bold color code"""
|
||||
assert Colors.black_bold == "\033[1;30m"
|
||||
|
||||
def test_red_bold(self):
|
||||
"""Test red bold color code"""
|
||||
assert Colors.red_bold == "\033[1;31m"
|
||||
|
||||
def test_green_bold(self):
|
||||
"""Test green bold color code"""
|
||||
assert Colors.green_bold == "\033[1;32m"
|
||||
|
||||
def test_yellow_bold(self):
|
||||
"""Test yellow bold color code"""
|
||||
assert Colors.yellow_bold == "\033[1;33m"
|
||||
|
||||
def test_blue_bold(self):
|
||||
"""Test blue bold color code"""
|
||||
assert Colors.blue_bold == "\033[1;34m"
|
||||
|
||||
def test_magenta_bold(self):
|
||||
"""Test magenta bold color code"""
|
||||
assert Colors.magenta_bold == "\033[1;35m"
|
||||
|
||||
def test_cyan_bold(self):
|
||||
"""Test cyan bold color code"""
|
||||
assert Colors.cyan_bold == "\033[1;36m"
|
||||
|
||||
def test_white_bold(self):
|
||||
"""Test white bold color code"""
|
||||
assert Colors.white_bold == "\033[1;37m"
|
||||
|
||||
|
||||
class TestColorsBright:
|
||||
"""Tests for bright color ANSI codes"""
|
||||
|
||||
def test_black_bright(self):
|
||||
"""Test black bright color code"""
|
||||
assert Colors.black_bright == '\033[90m'
|
||||
|
||||
def test_red_bright(self):
|
||||
"""Test red bright color code"""
|
||||
assert Colors.red_bright == '\033[91m'
|
||||
|
||||
def test_green_bright(self):
|
||||
"""Test green bright color code"""
|
||||
assert Colors.green_bright == '\033[92m'
|
||||
|
||||
def test_yellow_bright(self):
|
||||
"""Test yellow bright color code"""
|
||||
assert Colors.yellow_bright == '\033[93m'
|
||||
|
||||
def test_blue_bright(self):
|
||||
"""Test blue bright color code"""
|
||||
assert Colors.blue_bright == '\033[94m'
|
||||
|
||||
def test_magenta_bright(self):
|
||||
"""Test magenta bright color code"""
|
||||
assert Colors.magenta_bright == '\033[95m'
|
||||
|
||||
def test_cyan_bright(self):
|
||||
"""Test cyan bright color code"""
|
||||
assert Colors.cyan_bright == '\033[96m'
|
||||
|
||||
def test_white_bright(self):
|
||||
"""Test white bright color code"""
|
||||
assert Colors.white_bright == '\033[97m'
|
||||
|
||||
|
||||
class TestColorsDisable:
|
||||
"""Tests for Colors.disable() method"""
|
||||
|
||||
def setup_method(self):
|
||||
"""Reset colors before each test"""
|
||||
Colors.reset_colors()
|
||||
|
||||
def teardown_method(self):
|
||||
"""Reset colors after each test"""
|
||||
Colors.reset_colors()
|
||||
|
||||
def test_disable_bold_and_underline(self):
|
||||
"""Test that disable() sets bold and underline to empty strings"""
|
||||
Colors.disable()
|
||||
assert Colors.bold == ''
|
||||
assert Colors.underline == ''
|
||||
|
||||
def test_disable_end_and_reset(self):
|
||||
"""Test that disable() sets end and reset to empty strings"""
|
||||
Colors.disable()
|
||||
assert Colors.end == ''
|
||||
assert Colors.reset == ''
|
||||
|
||||
def test_disable_normal_colors(self):
|
||||
"""Test that disable() sets all normal colors to empty strings"""
|
||||
Colors.disable()
|
||||
assert Colors.black == ''
|
||||
assert Colors.red == ''
|
||||
assert Colors.green == ''
|
||||
assert Colors.yellow == ''
|
||||
assert Colors.blue == ''
|
||||
assert Colors.magenta == ''
|
||||
assert Colors.cyan == ''
|
||||
assert Colors.white == ''
|
||||
|
||||
def test_disable_bold_colors(self):
|
||||
"""Test that disable() sets all bold colors to empty strings"""
|
||||
Colors.disable()
|
||||
assert Colors.black_bold == ''
|
||||
assert Colors.red_bold == ''
|
||||
assert Colors.green_bold == ''
|
||||
assert Colors.yellow_bold == ''
|
||||
assert Colors.blue_bold == ''
|
||||
assert Colors.magenta_bold == ''
|
||||
assert Colors.cyan_bold == ''
|
||||
assert Colors.white_bold == ''
|
||||
|
||||
def test_disable_bright_colors(self):
|
||||
"""Test that disable() sets all bright colors to empty strings"""
|
||||
Colors.disable()
|
||||
assert Colors.black_bright == ''
|
||||
assert Colors.red_bright == ''
|
||||
assert Colors.green_bright == ''
|
||||
assert Colors.yellow_bright == ''
|
||||
assert Colors.blue_bright == ''
|
||||
assert Colors.magenta_bright == ''
|
||||
assert Colors.cyan_bright == ''
|
||||
assert Colors.white_bright == ''
|
||||
|
||||
def test_disable_all_colors_at_once(self):
|
||||
"""Test that all color attributes are empty after disable()"""
|
||||
Colors.disable()
|
||||
# Check that all public attributes are empty strings
|
||||
for attr in dir(Colors):
|
||||
if not attr.startswith('_') and attr not in ['disable', 'reset_colors']:
|
||||
assert getattr(Colors, attr) == '', f"{attr} should be empty after disable()"
|
||||
|
||||
|
||||
class TestColorsResetColors:
|
||||
"""Tests for Colors.reset_colors() method"""
|
||||
|
||||
def setup_method(self):
|
||||
"""Disable colors before each test"""
|
||||
Colors.disable()
|
||||
|
||||
def teardown_method(self):
|
||||
"""Reset colors after each test"""
|
||||
Colors.reset_colors()
|
||||
|
||||
def test_reset_bold_and_underline(self):
|
||||
"""Test that reset_colors() restores bold and underline"""
|
||||
Colors.reset_colors()
|
||||
assert Colors.bold == '\033[1m'
|
||||
assert Colors.underline == '\033[4m'
|
||||
|
||||
def test_reset_end_and_reset(self):
|
||||
"""Test that reset_colors() restores end and reset"""
|
||||
Colors.reset_colors()
|
||||
assert Colors.end == '\033[0m'
|
||||
assert Colors.reset == '\033[0m'
|
||||
|
||||
def test_reset_normal_colors(self):
|
||||
"""Test that reset_colors() restores all normal colors"""
|
||||
Colors.reset_colors()
|
||||
assert Colors.black == "\033[30m"
|
||||
assert Colors.red == "\033[31m"
|
||||
assert Colors.green == "\033[32m"
|
||||
assert Colors.yellow == "\033[33m"
|
||||
assert Colors.blue == "\033[34m"
|
||||
assert Colors.magenta == "\033[35m"
|
||||
assert Colors.cyan == "\033[36m"
|
||||
assert Colors.white == "\033[37m"
|
||||
|
||||
def test_reset_bold_colors(self):
|
||||
"""Test that reset_colors() restores all bold colors"""
|
||||
Colors.reset_colors()
|
||||
assert Colors.black_bold == "\033[1;30m"
|
||||
assert Colors.red_bold == "\033[1;31m"
|
||||
assert Colors.green_bold == "\033[1;32m"
|
||||
assert Colors.yellow_bold == "\033[1;33m"
|
||||
assert Colors.blue_bold == "\033[1;34m"
|
||||
assert Colors.magenta_bold == "\033[1;35m"
|
||||
assert Colors.cyan_bold == "\033[1;36m"
|
||||
assert Colors.white_bold == "\033[1;37m"
|
||||
|
||||
def test_reset_bright_colors(self):
|
||||
"""Test that reset_colors() restores all bright colors"""
|
||||
Colors.reset_colors()
|
||||
assert Colors.black_bright == '\033[90m'
|
||||
assert Colors.red_bright == '\033[91m'
|
||||
assert Colors.green_bright == '\033[92m'
|
||||
assert Colors.yellow_bright == '\033[93m'
|
||||
assert Colors.blue_bright == '\033[94m'
|
||||
assert Colors.magenta_bright == '\033[95m'
|
||||
assert Colors.cyan_bright == '\033[96m'
|
||||
assert Colors.white_bright == '\033[97m'
|
||||
|
||||
|
||||
class TestColorsDisableAndReset:
|
||||
"""Tests for disable and reset cycle"""
|
||||
|
||||
def setup_method(self):
|
||||
"""Reset colors before each test"""
|
||||
Colors.reset_colors()
|
||||
|
||||
def teardown_method(self):
|
||||
"""Reset colors after each test"""
|
||||
Colors.reset_colors()
|
||||
|
||||
def test_disable_then_reset_cycle(self):
|
||||
"""Test that colors can be disabled and then reset multiple times"""
|
||||
# Initial state
|
||||
original_red = Colors.red
|
||||
|
||||
# Disable
|
||||
Colors.disable()
|
||||
assert Colors.red == ''
|
||||
|
||||
# Reset
|
||||
Colors.reset_colors()
|
||||
assert Colors.red == original_red
|
||||
|
||||
# Disable again
|
||||
Colors.disable()
|
||||
assert Colors.red == ''
|
||||
|
||||
# Reset again
|
||||
Colors.reset_colors()
|
||||
assert Colors.red == original_red
|
||||
|
||||
def test_multiple_disables(self):
|
||||
"""Test that calling disable() multiple times is safe"""
|
||||
Colors.disable()
|
||||
Colors.disable()
|
||||
Colors.disable()
|
||||
assert Colors.red == ''
|
||||
assert Colors.blue == ''
|
||||
|
||||
def test_multiple_resets(self):
|
||||
"""Test that calling reset_colors() multiple times is safe"""
|
||||
Colors.reset_colors()
|
||||
Colors.reset_colors()
|
||||
Colors.reset_colors()
|
||||
assert Colors.red == "\033[31m"
|
||||
assert Colors.blue == "\033[34m"
|
||||
|
||||
|
||||
class TestColorsUsage:
|
||||
"""Tests for practical usage of Colors class"""
|
||||
|
||||
def setup_method(self):
|
||||
"""Reset colors before each test"""
|
||||
Colors.reset_colors()
|
||||
|
||||
def teardown_method(self):
|
||||
"""Reset colors after each test"""
|
||||
Colors.reset_colors()
|
||||
|
||||
def test_colored_string_with_reset(self):
|
||||
"""Test creating a colored string with reset"""
|
||||
result = f"{Colors.red}Error{Colors.end}"
|
||||
assert result == "\033[31mError\033[0m"
|
||||
|
||||
def test_bold_colored_string(self):
|
||||
"""Test creating a bold colored string"""
|
||||
result = f"{Colors.bold}{Colors.yellow}Warning{Colors.end}"
|
||||
assert result == "\033[1m\033[33mWarning\033[0m"
|
||||
|
||||
def test_underline_colored_string(self):
|
||||
"""Test creating an underlined colored string"""
|
||||
result = f"{Colors.underline}{Colors.blue}Info{Colors.end}"
|
||||
assert result == "\033[4m\033[34mInfo\033[0m"
|
||||
|
||||
def test_bold_underline_colored_string(self):
|
||||
"""Test creating a bold and underlined colored string"""
|
||||
result = f"{Colors.bold}{Colors.underline}{Colors.green}Success{Colors.end}"
|
||||
assert result == "\033[1m\033[4m\033[32mSuccess\033[0m"
|
||||
|
||||
def test_multiple_colors_in_string(self):
|
||||
"""Test using multiple colors in one string"""
|
||||
result = f"{Colors.red}Red{Colors.end} {Colors.blue}Blue{Colors.end}"
|
||||
assert result == "\033[31mRed\033[0m \033[34mBlue\033[0m"
|
||||
|
||||
def test_bright_color_usage(self):
|
||||
"""Test using bright color variants"""
|
||||
result = f"{Colors.cyan_bright}Bright Cyan{Colors.end}"
|
||||
assert result == "\033[96mBright Cyan\033[0m"
|
||||
|
||||
def test_bold_color_shortcut(self):
|
||||
"""Test using bold color shortcuts"""
|
||||
result = f"{Colors.red_bold}Bold Red{Colors.end}"
|
||||
assert result == "\033[1;31mBold Red\033[0m"
|
||||
|
||||
def test_disabled_colors_produce_plain_text(self):
|
||||
"""Test that disabled colors produce plain text without ANSI codes"""
|
||||
Colors.disable()
|
||||
result = f"{Colors.red}Error{Colors.end}"
|
||||
assert result == "Error"
|
||||
assert "\033[" not in result
|
||||
|
||||
def test_disabled_bold_underline_produce_plain_text(self):
|
||||
"""Test that disabled formatting produces plain text"""
|
||||
Colors.disable()
|
||||
result = f"{Colors.bold}{Colors.underline}{Colors.green}Success{Colors.end}"
|
||||
assert result == "Success"
|
||||
assert "\033[" not in result
|
||||
|
||||
|
||||
class TestColorsPrivateAttributes:
|
||||
"""Tests to ensure private attributes are not directly accessible"""
|
||||
|
||||
def test_private_bold_not_accessible(self):
|
||||
"""Test that __BOLD is private"""
|
||||
with pytest.raises(AttributeError):
|
||||
_ = Colors.__BOLD
|
||||
|
||||
def test_private_colors_not_accessible(self):
|
||||
"""Test that private color attributes are not accessible"""
|
||||
with pytest.raises(AttributeError):
|
||||
_ = Colors.__RED
|
||||
with pytest.raises(AttributeError):
|
||||
_ = Colors.__GREEN
|
||||
|
||||
|
||||
# Parametrized tests
|
||||
@pytest.mark.parametrize("color_attr,expected_code", [
|
||||
("black", "\033[30m"),
|
||||
("red", "\033[31m"),
|
||||
("green", "\033[32m"),
|
||||
("yellow", "\033[33m"),
|
||||
("blue", "\033[34m"),
|
||||
("magenta", "\033[35m"),
|
||||
("cyan", "\033[36m"),
|
||||
("white", "\033[37m"),
|
||||
])
|
||||
def test_normal_colors_parametrized(color_attr: str, expected_code: str):
|
||||
"""Parametrized test for normal colors"""
|
||||
Colors.reset_colors()
|
||||
assert getattr(Colors, color_attr) == expected_code
|
||||
|
||||
|
||||
@pytest.mark.parametrize("color_attr,expected_code", [
|
||||
("black_bold", "\033[1;30m"),
|
||||
("red_bold", "\033[1;31m"),
|
||||
("green_bold", "\033[1;32m"),
|
||||
("yellow_bold", "\033[1;33m"),
|
||||
("blue_bold", "\033[1;34m"),
|
||||
("magenta_bold", "\033[1;35m"),
|
||||
("cyan_bold", "\033[1;36m"),
|
||||
("white_bold", "\033[1;37m"),
|
||||
])
|
||||
def test_bold_colors_parametrized(color_attr: str, expected_code: str):
|
||||
"""Parametrized test for bold colors"""
|
||||
Colors.reset_colors()
|
||||
assert getattr(Colors, color_attr) == expected_code
|
||||
|
||||
|
||||
@pytest.mark.parametrize("color_attr,expected_code", [
|
||||
("black_bright", '\033[90m'),
|
||||
("red_bright", '\033[91m'),
|
||||
("green_bright", '\033[92m'),
|
||||
("yellow_bright", '\033[93m'),
|
||||
("blue_bright", '\033[94m'),
|
||||
("magenta_bright", '\033[95m'),
|
||||
("cyan_bright", '\033[96m'),
|
||||
("white_bright", '\033[97m'),
|
||||
])
|
||||
def test_bright_colors_parametrized(color_attr: str, expected_code: str):
|
||||
"""Parametrized test for bright colors"""
|
||||
Colors.reset_colors()
|
||||
assert getattr(Colors, color_attr) == expected_code
|
||||
|
||||
|
||||
@pytest.mark.parametrize("color_attr", [
|
||||
"bold", "underline", "end", "reset",
|
||||
"black", "red", "green", "yellow", "blue", "magenta", "cyan", "white",
|
||||
"black_bold", "red_bold", "green_bold", "yellow_bold",
|
||||
"blue_bold", "magenta_bold", "cyan_bold", "white_bold",
|
||||
"black_bright", "red_bright", "green_bright", "yellow_bright",
|
||||
"blue_bright", "magenta_bright", "cyan_bright", "white_bright",
|
||||
])
|
||||
def test_disable_all_attributes_parametrized(color_attr: str):
|
||||
"""Parametrized test that all color attributes are disabled"""
|
||||
Colors.reset_colors()
|
||||
Colors.disable()
|
||||
assert getattr(Colors, color_attr) == ''
|
||||
|
||||
|
||||
@pytest.mark.parametrize("color_attr", [
|
||||
"bold", "underline", "end", "reset",
|
||||
"black", "red", "green", "yellow", "blue", "magenta", "cyan", "white",
|
||||
"black_bold", "red_bold", "green_bold", "yellow_bold",
|
||||
"blue_bold", "magenta_bold", "cyan_bold", "white_bold",
|
||||
"black_bright", "red_bright", "green_bright", "yellow_bright",
|
||||
"blue_bright", "magenta_bright", "cyan_bright", "white_bright",
|
||||
])
|
||||
def test_reset_all_attributes_parametrized(color_attr: str):
|
||||
"""Parametrized test that all color attributes are reset"""
|
||||
Colors.disable()
|
||||
Colors.reset_colors()
|
||||
assert getattr(Colors, color_attr) != ''
|
||||
assert '\033[' in getattr(Colors, color_attr)
|
||||
|
||||
|
||||
# Edge case tests
|
||||
class TestColorsEdgeCases:
|
||||
"""Tests for edge cases and special scenarios"""
|
||||
|
||||
def setup_method(self):
|
||||
"""Reset colors before each test"""
|
||||
Colors.reset_colors()
|
||||
|
||||
def teardown_method(self):
|
||||
"""Reset colors after each test"""
|
||||
Colors.reset_colors()
|
||||
|
||||
def test_colors_class_is_not_instantiable(self):
|
||||
"""Test that Colors class can be instantiated (it's not abstract)"""
|
||||
# The class uses static methods, but can be instantiated
|
||||
instance = Colors()
|
||||
assert isinstance(instance, Colors)
|
||||
|
||||
def test_static_methods_work_on_instance(self):
|
||||
"""Test that static methods work when called on instance"""
|
||||
instance = Colors()
|
||||
instance.disable()
|
||||
assert Colors.red == ''
|
||||
instance.reset_colors()
|
||||
assert Colors.red == "\033[31m"
|
||||
|
||||
def test_concatenation_of_multiple_effects(self):
|
||||
"""Test concatenating multiple color effects"""
|
||||
result = f"{Colors.bold}{Colors.underline}{Colors.red_bright}Test{Colors.reset}"
|
||||
assert "\033[1m" in result # bold
|
||||
assert "\033[4m" in result # underline
|
||||
assert "\033[91m" in result # red bright
|
||||
assert "\033[0m" in result # reset
|
||||
|
||||
def test_empty_string_with_colors(self):
|
||||
"""Test applying colors to empty string"""
|
||||
result = f"{Colors.red}{Colors.end}"
|
||||
assert result == "\033[31m\033[0m"
|
||||
|
||||
def test_nested_color_changes(self):
|
||||
"""Test nested color changes in string"""
|
||||
result = f"{Colors.red}Red {Colors.blue}Blue{Colors.end} Red again{Colors.end}"
|
||||
assert result == "\033[31mRed \033[34mBlue\033[0m Red again\033[0m"
|
||||
|
||||
|
||||
# __END__
|
||||
Reference in New Issue
Block a user