feat: Add digital signage mode (URL rotation)
CI / build (push) Has been cancelled
CI / lint (push) Has been cancelled

- SignageManager.kt: Handles URL rotation with configurable interval
- Admin UI: Toggle, URL list (one per line), interval setting
- SignageManagerTest.kt: 9 unit tests for rotation logic

Features:
- Enable/disable signage mode
- Configure list of URLs to rotate
- Set rotation interval (min 5 seconds)
- Pauses on user interaction (resumes after 1 min)
- Resets to first URL on idle timeout

Note: MainActivity integration pending
This commit is contained in:
2026-02-03 19:53:54 +00:00
parent d1a637e8e4
commit 4f33255417
3 changed files with 292 additions and 0 deletions
@@ -0,0 +1,81 @@
package farm.hobbs.kioskbrowser
import org.junit.Assert.*
import org.junit.Test
/**
* Unit tests for SignageManager URL rotation logic.
* Note: These test the data/state logic, not the Handler timing.
*/
class SignageManagerTest {
@Test
fun `getUrlCount returns correct count`() {
val urls = listOf("https://a.com", "https://b.com", "https://c.com")
assertEquals(3, urls.size)
}
@Test
fun `empty URL list returns zero count`() {
val urls = emptyList<String>()
assertEquals(0, urls.size)
}
@Test
fun `URL list filters blank entries`() {
val urls = listOf("https://a.com", "", "https://b.com", " ", "https://c.com")
.filter { it.isNotBlank() }
assertEquals(3, urls.size)
}
@Test
fun `index wraps around at end of list`() {
val urls = listOf("a", "b", "c")
var index = 0
// Advance through all URLs
index = (index + 1) % urls.size // 1
index = (index + 1) % urls.size // 2
index = (index + 1) % urls.size // 0 (wrapped)
assertEquals(0, index)
assertEquals("a", urls[index])
}
@Test
fun `interval coerced to minimum 5 seconds`() {
val inputSeconds = 2
val intervalMs = (inputSeconds * 1000L).coerceAtLeast(5000L)
assertEquals(5000L, intervalMs)
}
@Test
fun `interval accepts values above minimum`() {
val inputSeconds = 30
val intervalMs = (inputSeconds * 1000L).coerceAtLeast(5000L)
assertEquals(30000L, intervalMs)
}
@Test
fun `single URL list stays on same URL`() {
val urls = listOf("https://only.com")
var index = 0
index = (index + 1) % urls.size
assertEquals(0, index) // Still at 0
}
@Test
fun `getCurrentUrl returns null for empty list`() {
val urls = emptyList<String>()
val index = 0
assertNull(urls.getOrNull(index))
}
@Test
fun `getCurrentUrl returns correct URL`() {
val urls = listOf("https://first.com", "https://second.com")
assertEquals("https://first.com", urls.getOrNull(0))
assertEquals("https://second.com", urls.getOrNull(1))
}
}