1. package chroma
    
  2. 
    
  3. import (
    
  4. 	"math"
    
  5. 	"testing"
    
  6. 
    
  7. 	assert "github.com/alecthomas/assert/v2"
    
  8. )
    
  9. 
    
  10. func TestColourRGB(t *testing.T) {
    
  11. 	colour := ParseColour("#8913af")
    
  12. 	assert.Equal(t, uint8(0x89), colour.Red())
    
  13. 	assert.Equal(t, uint8(0x13), colour.Green())
    
  14. 	assert.Equal(t, uint8(0xaf), colour.Blue())
    
  15. }
    
  16. 
    
  17. func TestColourString(t *testing.T) {
    
  18. 	assert.Equal(t, "#8913af", ParseColour("#8913af").String())
    
  19. }
    
  20. 
    
  21. func distance(a, b uint8) uint8 {
    
  22. 	if a < b {
    
  23. 		return b - a
    
  24. 	}
    
  25. 	return a - b
    
  26. }
    
  27. 
    
  28. func TestColourBrighten(t *testing.T) {
    
  29. 	actual := NewColour(128, 128, 128).Brighten(0.5)
    
  30. 	// Closeish to what we expect is fine.
    
  31. 	assert.True(t, distance(192, actual.Red()) <= 2)
    
  32. 	assert.True(t, distance(192, actual.Blue()) <= 2)
    
  33. 	assert.True(t, distance(192, actual.Green()) <= 2)
    
  34. 	actual = NewColour(128, 128, 128).Brighten(-0.5)
    
  35. 	assert.True(t, distance(65, actual.Red()) <= 2)
    
  36. 	assert.True(t, distance(65, actual.Blue()) <= 2)
    
  37. 	assert.True(t, distance(65, actual.Green()) <= 2)
    
  38. }
    
  39. 
    
  40. func TestColourBrightess(t *testing.T) {
    
  41. 	actual := NewColour(128, 128, 128).Brightness()
    
  42. 	assert.True(t, distance(128, uint8(actual*255.0)) <= 2)
    
  43. }
    
  44. 
    
  45. // hue returns c's hue. See https://stackoverflow.com/a/23094494.
    
  46. func hue(c Colour) float64 {
    
  47. 	r := float64(c.Red()) / 255
    
  48. 	g := float64(c.Green()) / 255
    
  49. 	b := float64(c.Blue()) / 255
    
  50. 
    
  51. 	min := math.Min(math.Min(r, g), b)
    
  52. 	max := math.Max(math.Max(r, g), b)
    
  53. 
    
  54. 	switch {
    
  55. 	case r == min:
    
  56. 		return (g - b) / (max - min)
    
  57. 	case g == min:
    
  58. 		return 2 + (b-r)/(max-min)
    
  59. 	default:
    
  60. 		return 4 + (r-g)/(max-min)
    
  61. 	}
    
  62. }
    
  63. 
    
  64. func TestColourClampBrightness(t *testing.T) {
    
  65. 	// Start with a colour with a brightness close to 0.5.
    
  66. 	initial := NewColour(0, 128, 255)
    
  67. 	br := initial.Brightness()
    
  68. 	assertInDelta(t, 0.5, br)
    
  69. 
    
  70. 	// Passing a range that includes the colour's brightness should be a no-op.
    
  71. 	assert.Equal(t, initial.String(), initial.ClampBrightness(br-0.01, br+0.01).String())
    
  72. 
    
  73. 	// Clamping to [0, 0] or [1, 1] should produce black or white, respectively.
    
  74. 	assert.Equal(t, "#000000", initial.ClampBrightness(0, 0).String())
    
  75. 	assert.Equal(t, "#ffffff", initial.ClampBrightness(1, 1).String())
    
  76. 
    
  77. 	// Clamping to a brighter or darker range should produce the requested
    
  78. 	// brightness while preserving the colour's hue.
    
  79. 	brighter := initial.ClampBrightness(0.75, 1)
    
  80. 	assertInDelta(t, 0.75, brighter.Brightness())
    
  81. 	assertInDelta(t, hue(initial), hue(brighter))
    
  82. 
    
  83. 	darker := initial.ClampBrightness(0, 0.25)
    
  84. 	assertInDelta(t, 0.25, darker.Brightness())
    
  85. 	assertInDelta(t, hue(initial), hue(darker))
    
  86. }
    
  87. 
    
  88. func assertInDelta(t *testing.T, expected, actual float64) {
    
  89. 	const delta = 0.01 // used for brightness and hue comparisons
    
  90. 	assert.True(t, actual > (expected-delta) && actual < (expected+delta))
    
  91. }