test_display_control.py 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. """Tests for daemon.display_control — DisplayControl brightness and blanking."""
  2. import time
  3. import pytest
  4. class TestDisplayControlNoBacklight:
  5. """DisplayControl behavior when no backlight is present."""
  6. def test_no_backlight_detected(self, monkeypatch, tmp_path):
  7. # Point BACKLIGHT_BASE to an empty directory (no backlight entries)
  8. import daemon.display_control as dc_mod
  9. empty_dir = tmp_path / "backlight"
  10. empty_dir.mkdir()
  11. monkeypatch.setattr(dc_mod, "BACKLIGHT_BASE", empty_dir)
  12. dc = dc_mod.DisplayControl()
  13. assert dc.has_backlight is False
  14. def test_no_backlight_dir_missing(self, monkeypatch, tmp_path):
  15. import daemon.display_control as dc_mod
  16. monkeypatch.setattr(dc_mod, "BACKLIGHT_BASE", tmp_path / "nonexistent")
  17. dc = dc_mod.DisplayControl()
  18. assert dc.has_backlight is False
  19. def test_set_brightness_noop_without_backlight(self, monkeypatch, tmp_path):
  20. import daemon.display_control as dc_mod
  21. empty_dir = tmp_path / "backlight"
  22. empty_dir.mkdir()
  23. monkeypatch.setattr(dc_mod, "BACKLIGHT_BASE", empty_dir)
  24. dc = dc_mod.DisplayControl()
  25. # Should not raise
  26. dc.set_brightness(50)
  27. dc.set_brightness(0)
  28. dc.set_brightness(100)
  29. class TestDisplayControlWithBacklight:
  30. """DisplayControl behavior with a mock sysfs backlight."""
  31. @pytest.fixture
  32. def display(self, monkeypatch, tmp_path):
  33. import daemon.display_control as dc_mod
  34. bl_dir = tmp_path / "backlight" / "rpi_backlight"
  35. bl_dir.mkdir(parents=True)
  36. (bl_dir / "brightness").write_text("200")
  37. (bl_dir / "max_brightness").write_text("255")
  38. monkeypatch.setattr(dc_mod, "BACKLIGHT_BASE", tmp_path / "backlight")
  39. return dc_mod.DisplayControl(), bl_dir
  40. def test_has_backlight_true(self, display):
  41. dc, _ = display
  42. assert dc.has_backlight is True
  43. def test_set_brightness_100(self, display):
  44. dc, bl_dir = display
  45. dc.set_brightness(100)
  46. assert (bl_dir / "brightness").read_text() == "255"
  47. def test_set_brightness_0(self, display):
  48. dc, bl_dir = display
  49. dc.set_brightness(0)
  50. assert (bl_dir / "brightness").read_text() == "0"
  51. def test_set_brightness_50(self, display):
  52. dc, bl_dir = display
  53. dc.set_brightness(50)
  54. value = int((bl_dir / "brightness").read_text())
  55. # 50% of 255 = 127 or 128 depending on rounding
  56. assert value == round(255 * 50 / 100)
  57. def test_set_brightness_clamped_above_100(self, display):
  58. dc, bl_dir = display
  59. dc.set_brightness(200)
  60. assert (bl_dir / "brightness").read_text() == "255"
  61. def test_set_brightness_clamped_below_0(self, display):
  62. dc, bl_dir = display
  63. dc.set_brightness(-50)
  64. assert (bl_dir / "brightness").read_text() == "0"
  65. def test_max_brightness_fallback_on_missing_file(self, monkeypatch, tmp_path):
  66. """If max_brightness file doesn't exist, defaults to 255."""
  67. import daemon.display_control as dc_mod
  68. bl_dir = tmp_path / "backlight" / "rpi_backlight"
  69. bl_dir.mkdir(parents=True)
  70. (bl_dir / "brightness").write_text("100")
  71. # No max_brightness file
  72. monkeypatch.setattr(dc_mod, "BACKLIGHT_BASE", tmp_path / "backlight")
  73. dc = dc_mod.DisplayControl()
  74. assert dc._max_brightness == 255
  75. class TestDisplayControlBlanking:
  76. """Blanking logic: timeout, wake, tick."""
  77. @pytest.fixture
  78. def display(self, monkeypatch, tmp_path):
  79. import daemon.display_control as dc_mod
  80. empty_dir = tmp_path / "backlight"
  81. empty_dir.mkdir()
  82. monkeypatch.setattr(dc_mod, "BACKLIGHT_BASE", empty_dir)
  83. return dc_mod.DisplayControl()
  84. def test_blank_timeout_default_disabled(self, display):
  85. assert display._blank_timeout == 0
  86. def test_set_blank_timeout(self, display):
  87. display.set_blank_timeout(30)
  88. assert display._blank_timeout == 30
  89. def test_set_blank_timeout_negative_clamped(self, display):
  90. display.set_blank_timeout(-10)
  91. assert display._blank_timeout == 0
  92. def test_tick_does_not_blank_when_disabled(self, display):
  93. display.set_blank_timeout(0)
  94. display.tick()
  95. assert display._blanked is False
  96. def test_tick_blanks_after_timeout(self, display, monkeypatch):
  97. display.set_blank_timeout(5)
  98. # Simulate idle for 10 seconds by backdating last_activity
  99. display._last_activity = time.monotonic() - 10
  100. display.tick()
  101. assert display._blanked is True
  102. def test_tick_does_not_blank_before_timeout(self, display):
  103. display.set_blank_timeout(60)
  104. display.wake() # Reset activity
  105. display.tick()
  106. assert display._blanked is False
  107. def test_wake_unblanks(self, display):
  108. display.set_blank_timeout(5)
  109. display._last_activity = time.monotonic() - 10
  110. display.tick()
  111. assert display._blanked is True
  112. display.wake()
  113. assert display._blanked is False
  114. def test_tick_unblanks_when_timeout_disabled_while_blanked(self, display):
  115. """If timeout is disabled while screen is blanked, tick should unblank."""
  116. display.set_blank_timeout(5)
  117. display._last_activity = time.monotonic() - 10
  118. display.tick()
  119. assert display._blanked is True
  120. display.set_blank_timeout(0)
  121. display.tick()
  122. assert display._blanked is False
  123. def test_wake_resets_activity_timer(self, display):
  124. display.set_blank_timeout(5)
  125. old_time = display._last_activity
  126. time.sleep(0.01)
  127. display.wake()
  128. assert display._last_activity > old_time