|
| 1 | +// |
| 2 | +// HSLColorTests.swift |
| 3 | +// Rainbow |
| 4 | +// |
| 5 | +// Created by Rainbow on 16/7/25. |
| 6 | +// |
| 7 | + |
| 8 | +import XCTest |
| 9 | +@testable import Rainbow |
| 10 | + |
| 11 | +class HSLColorTests: XCTestCase { |
| 12 | + |
| 13 | + override func setUp() { |
| 14 | + super.setUp() |
| 15 | + Rainbow.outputTarget = .console |
| 16 | + Rainbow.enabled = true |
| 17 | + } |
| 18 | + |
| 19 | + func testHSLToRGBConversion() { |
| 20 | + // Test pure colors |
| 21 | + let redConverter = HSLColorConverter(hue: 0, saturation: 100, lightness: 50) |
| 22 | + let redRGB = redConverter.toRGB() |
| 23 | + XCTAssertEqual(redRGB.0, 255) |
| 24 | + XCTAssertEqual(redRGB.1, 0) |
| 25 | + XCTAssertEqual(redRGB.2, 0) |
| 26 | + |
| 27 | + let greenConverter = HSLColorConverter(hue: 120, saturation: 100, lightness: 50) |
| 28 | + let greenRGB = greenConverter.toRGB() |
| 29 | + XCTAssertEqual(greenRGB.0, 0) |
| 30 | + XCTAssertEqual(greenRGB.1, 255) |
| 31 | + XCTAssertEqual(greenRGB.2, 0) |
| 32 | + |
| 33 | + let blueConverter = HSLColorConverter(hue: 240, saturation: 100, lightness: 50) |
| 34 | + let blueRGB = blueConverter.toRGB() |
| 35 | + XCTAssertEqual(blueRGB.0, 0) |
| 36 | + XCTAssertEqual(blueRGB.1, 0) |
| 37 | + XCTAssertEqual(blueRGB.2, 255) |
| 38 | + |
| 39 | + // Test grayscale (saturation = 0) |
| 40 | + let grayConverter = HSLColorConverter(hue: 0, saturation: 0, lightness: 50) |
| 41 | + let grayRGB = grayConverter.toRGB() |
| 42 | + XCTAssertEqual(grayRGB.0, 128) |
| 43 | + XCTAssertEqual(grayRGB.1, 128) |
| 44 | + XCTAssertEqual(grayRGB.2, 128) |
| 45 | + |
| 46 | + let blackConverter = HSLColorConverter(hue: 0, saturation: 0, lightness: 0) |
| 47 | + let blackRGB = blackConverter.toRGB() |
| 48 | + XCTAssertEqual(blackRGB.0, 0) |
| 49 | + XCTAssertEqual(blackRGB.1, 0) |
| 50 | + XCTAssertEqual(blackRGB.2, 0) |
| 51 | + |
| 52 | + let whiteConverter = HSLColorConverter(hue: 0, saturation: 0, lightness: 100) |
| 53 | + let whiteRGB = whiteConverter.toRGB() |
| 54 | + XCTAssertEqual(whiteRGB.0, 255) |
| 55 | + XCTAssertEqual(whiteRGB.1, 255) |
| 56 | + XCTAssertEqual(whiteRGB.2, 255) |
| 57 | + |
| 58 | + // Test intermediate colors |
| 59 | + let orangeConverter = HSLColorConverter(hue: 30, saturation: 100, lightness: 50) |
| 60 | + let orangeRGB = orangeConverter.toRGB() |
| 61 | + XCTAssertEqual(orangeRGB.0, 255) |
| 62 | + XCTAssertEqual(orangeRGB.1, 128) |
| 63 | + XCTAssertEqual(orangeRGB.2, 0) |
| 64 | + |
| 65 | + let purpleConverter = HSLColorConverter(hue: 270, saturation: 50, lightness: 50) |
| 66 | + let purpleRGB = purpleConverter.toRGB() |
| 67 | + XCTAssertEqual(purpleRGB.0, 127) |
| 68 | + XCTAssertEqual(purpleRGB.1, 64) |
| 69 | + XCTAssertEqual(purpleRGB.2, 191) |
| 70 | + } |
| 71 | + |
| 72 | + func testHSLNormalization() { |
| 73 | + // Test hue wrapping |
| 74 | + let converter1 = HSLColorConverter(hue: 380, saturation: 100, lightness: 50) |
| 75 | + let converter2 = HSLColorConverter(hue: 20, saturation: 100, lightness: 50) |
| 76 | + let rgb1 = converter1.toRGB() |
| 77 | + let rgb2 = converter2.toRGB() |
| 78 | + XCTAssertEqual(rgb1.0, rgb2.0) |
| 79 | + XCTAssertEqual(rgb1.1, rgb2.1) |
| 80 | + XCTAssertEqual(rgb1.2, rgb2.2) |
| 81 | + |
| 82 | + let converter3 = HSLColorConverter(hue: -20, saturation: 100, lightness: 50) |
| 83 | + let converter4 = HSLColorConverter(hue: 340, saturation: 100, lightness: 50) |
| 84 | + let rgb3 = converter3.toRGB() |
| 85 | + let rgb4 = converter4.toRGB() |
| 86 | + XCTAssertEqual(rgb3.0, rgb4.0) |
| 87 | + XCTAssertEqual(rgb3.1, rgb4.1) |
| 88 | + XCTAssertEqual(rgb3.2, rgb4.2) |
| 89 | + |
| 90 | + // Test saturation clamping |
| 91 | + let converter5 = HSLColorConverter(hue: 0, saturation: 150, lightness: 50) |
| 92 | + let converter6 = HSLColorConverter(hue: 0, saturation: 100, lightness: 50) |
| 93 | + let rgb5 = converter5.toRGB() |
| 94 | + let rgb6 = converter6.toRGB() |
| 95 | + XCTAssertEqual(rgb5.0, rgb6.0) |
| 96 | + XCTAssertEqual(rgb5.1, rgb6.1) |
| 97 | + XCTAssertEqual(rgb5.2, rgb6.2) |
| 98 | + |
| 99 | + let converter7 = HSLColorConverter(hue: 0, saturation: -50, lightness: 50) |
| 100 | + let converter8 = HSLColorConverter(hue: 0, saturation: 0, lightness: 50) |
| 101 | + let rgb7 = converter7.toRGB() |
| 102 | + let rgb8 = converter8.toRGB() |
| 103 | + XCTAssertEqual(rgb7.0, rgb8.0) |
| 104 | + XCTAssertEqual(rgb7.1, rgb8.1) |
| 105 | + XCTAssertEqual(rgb7.2, rgb8.2) |
| 106 | + |
| 107 | + // Test lightness clamping |
| 108 | + let converter9 = HSLColorConverter(hue: 0, saturation: 100, lightness: 150) |
| 109 | + let converter10 = HSLColorConverter(hue: 0, saturation: 100, lightness: 100) |
| 110 | + let rgb9 = converter9.toRGB() |
| 111 | + let rgb10 = converter10.toRGB() |
| 112 | + XCTAssertEqual(rgb9.0, rgb10.0) |
| 113 | + XCTAssertEqual(rgb9.1, rgb10.1) |
| 114 | + XCTAssertEqual(rgb9.2, rgb10.2) |
| 115 | + } |
| 116 | + |
| 117 | + func testStringHSLMethods() { |
| 118 | + // Test foreground HSL |
| 119 | + let text = "Hello" |
| 120 | + |
| 121 | + // Pure red |
| 122 | + let redText = text.hsl(0, 100, 50, to: .bit24) |
| 123 | + XCTAssertTrue(redText.contains("\u{001B}[38;2;255;0;0m")) |
| 124 | + |
| 125 | + // Pure green |
| 126 | + let greenText = text.hsl(120, 100, 50, to: .bit24) |
| 127 | + XCTAssertTrue(greenText.contains("\u{001B}[38;2;0;255;0m")) |
| 128 | + |
| 129 | + // Gray |
| 130 | + let grayText = text.hsl(0, 0, 50, to: .bit24) |
| 131 | + XCTAssertTrue(grayText.contains("\u{001B}[38;2;128;128;128m")) |
| 132 | + |
| 133 | + // Test with HSL tuple |
| 134 | + let hslColor: HSL = (hue: 240, saturation: 100, lightness: 50) |
| 135 | + let blueText = text.hsl(hslColor, to: .bit24) |
| 136 | + XCTAssertTrue(blueText.contains("\u{001B}[38;2;0;0;255m")) |
| 137 | + } |
| 138 | + |
| 139 | + func testStringOnHSLMethods() { |
| 140 | + // Test background HSL |
| 141 | + let text = "Hello" |
| 142 | + |
| 143 | + // Pure red background |
| 144 | + let redBgText = text.onHsl(0, 100, 50, to: .bit24) |
| 145 | + XCTAssertTrue(redBgText.contains("\u{001B}[48;2;255;0;0m")) |
| 146 | + |
| 147 | + // Pure green background |
| 148 | + let greenBgText = text.onHsl(120, 100, 50, to: .bit24) |
| 149 | + XCTAssertTrue(greenBgText.contains("\u{001B}[48;2;0;255;0m")) |
| 150 | + |
| 151 | + // Gray background |
| 152 | + let grayBgText = text.onHsl(0, 0, 50, to: .bit24) |
| 153 | + XCTAssertTrue(grayBgText.contains("\u{001B}[48;2;128;128;128m")) |
| 154 | + |
| 155 | + // Test with HSL tuple |
| 156 | + let hslColor: HSL = (hue: 240, saturation: 100, lightness: 50) |
| 157 | + let blueBgText = text.onHsl(hslColor, to: .bit24) |
| 158 | + XCTAssertTrue(blueBgText.contains("\u{001B}[48;2;0;0;255m")) |
| 159 | + } |
| 160 | + |
| 161 | + func testHSL8BitApproximation() { |
| 162 | + // Test that 8-bit approximation works |
| 163 | + let text = "Hello" |
| 164 | + |
| 165 | + // This should use ColorApproximation to convert to 8-bit |
| 166 | + let redText = text.hsl(0, 100, 50) // default is .bit8Approximated |
| 167 | + // Should contain an 8-bit color code, not 24-bit |
| 168 | + XCTAssertFalse(redText.contains("38;2;")) |
| 169 | + XCTAssertTrue(redText.contains("\u{001B}[")) |
| 170 | + |
| 171 | + let bgText = text.onHsl(120, 100, 50) |
| 172 | + XCTAssertFalse(bgText.contains("48;2;")) |
| 173 | + XCTAssertTrue(bgText.contains("\u{001B}[")) |
| 174 | + } |
| 175 | + |
| 176 | + func testHSLChaining() { |
| 177 | + // Test that HSL methods can be chained with other methods |
| 178 | + let text = "Hello" |
| 179 | + |
| 180 | + let styledText = text.hsl(180, 100, 50).bold.underline |
| 181 | + // Should contain escape sequences for color, bold, and underline |
| 182 | + XCTAssertTrue(styledText.contains("\u{001B}[")) |
| 183 | + XCTAssertTrue(styledText.contains("1;")) // bold |
| 184 | + XCTAssertTrue(styledText.contains("4m")) // underline |
| 185 | + |
| 186 | + let complexText = text.hsl(60, 100, 50).onHsl(300, 100, 50, to: .bit24).italic |
| 187 | + // Should contain escape sequences for foreground, background, and italic |
| 188 | + XCTAssertTrue(complexText.contains("\u{001B}[")) |
| 189 | + XCTAssertTrue(complexText.contains("3m")) // italic |
| 190 | + XCTAssertTrue(complexText.contains("48;2;")) // 24-bit background color |
| 191 | + } |
| 192 | + |
| 193 | + func testHSLWithDisabledRainbow() { |
| 194 | + Rainbow.enabled = false |
| 195 | + |
| 196 | + let text = "Hello" |
| 197 | + let result = text.hsl(0, 100, 50) |
| 198 | + XCTAssertEqual(result, text) |
| 199 | + |
| 200 | + let bgResult = text.onHsl(120, 100, 50) |
| 201 | + XCTAssertEqual(bgResult, text) |
| 202 | + |
| 203 | + Rainbow.enabled = true |
| 204 | + } |
| 205 | + |
| 206 | + func testHSLEdgeCases() { |
| 207 | + // Test various edge cases |
| 208 | + let text = "Test" |
| 209 | + |
| 210 | + // Hue at boundaries |
| 211 | + let hue360 = text.hsl(360, 100, 50, to: .bit24) |
| 212 | + let hue0 = text.hsl(0, 100, 50, to: .bit24) |
| 213 | + XCTAssertEqual(hue360, hue0) // 360 degrees should equal 0 degrees |
| 214 | + |
| 215 | + // Low saturation, high lightness (pastel colors) |
| 216 | + let pastel = text.hsl(180, 25, 80, to: .bit24) |
| 217 | + XCTAssertTrue(pastel.contains("\u{001B}[38;2;")) |
| 218 | + |
| 219 | + // High saturation, low lightness (dark colors) |
| 220 | + let dark = text.hsl(60, 100, 20, to: .bit24) |
| 221 | + XCTAssertTrue(dark.contains("\u{001B}[38;2;")) |
| 222 | + } |
| 223 | +} |
0 commit comments