aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-android/app/src/test
diff options
context:
space:
mode:
authorChristophe Besson <cbesson@gmail.com>2026-10-10 15:11:52 +0200
committerChristophe Besson <cbesson@gmail.com>2026-10-10 15:11:52 +0200
commit1fd7bbcd038cddf40540bae7083b9adf4c728912 (patch)
tree00574a2ce5c3f592c71d5a339dde9fb263636eeb /packages/meshbay-android/app/src/test
parent3bad2b50187f55463588a2d4e74fa9df4beb35b8 (diff)
downloadmeshbay-1fd7bbcd038cddf40540bae7083b9adf4c728912.tar.gz
feat(android): back the videos of the chosen albums up beside the photos
A 'Videos too' option, off by default, sends them into the same YYYY/YYYY-MM folders. Files are read from the phone a ranged chunk at a time, so a large video is never whole in the page. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'packages/meshbay-android/app/src/test')
-rw-r--r--packages/meshbay-android/app/src/test/kotlin/org/meshbay/client/ByteRangeTest.kt31
-rw-r--r--packages/meshbay-android/app/src/test/kotlin/org/meshbay/client/PhotoSyncTest.kt30
2 files changed, 57 insertions, 4 deletions
diff --git a/packages/meshbay-android/app/src/test/kotlin/org/meshbay/client/ByteRangeTest.kt b/packages/meshbay-android/app/src/test/kotlin/org/meshbay/client/ByteRangeTest.kt
new file mode 100644
index 0000000..29d8528
--- /dev/null
+++ b/packages/meshbay-android/app/src/test/kotlin/org/meshbay/client/ByteRangeTest.kt
@@ -0,0 +1,31 @@
+package org.meshbay.client
+
+import org.junit.Assert.assertArrayEquals
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertNull
+import org.junit.Test
+import org.meshbay.client.photos.ByteRange
+
+class ByteRangeTest {
+ @Test fun `a range is read as the upload asks for it`() {
+ assertEquals(0L..1023L, ByteRange.parse("bytes=0-1023", 5000))
+ assertEquals(4096L..4999L, ByteRange.parse("bytes=4096-", 5000))
+ assertEquals("the last chunk may ask past the end", 4096L..4999L, ByteRange.parse("bytes=4096-8191", 5000))
+ }
+
+ @Test fun `what cannot be served is refused`() {
+ assertNull(ByteRange.parse(null, 10))
+ assertNull(ByteRange.parse("bytes=10-20", 10))
+ assertNull(ByteRange.parse("bytes=5-2", 10))
+ assertNull(ByteRange.parse("bytes=0-1,4-5", 10))
+ assertNull(ByteRange.parse("bytes=-5", 10))
+ assertNull(ByteRange.parse("items=0-1", 10))
+ }
+
+ @Test fun `a slice holds exactly those bytes`() {
+ val data = ByteArray(100_000) { (it % 251).toByte() }
+ val got = ByteRange.slice(data.inputStream(), 70_000L..99_999L).readBytes()
+ assertArrayEquals(data.copyOfRange(70_000, 100_000), got)
+ assertArrayEquals(data.copyOfRange(10, 12), ByteRange.slice(data.inputStream(), 10L..11L).readBytes())
+ }
+}
diff --git a/packages/meshbay-android/app/src/test/kotlin/org/meshbay/client/PhotoSyncTest.kt b/packages/meshbay-android/app/src/test/kotlin/org/meshbay/client/PhotoSyncTest.kt
index 7c0b794..a87bffb 100644
--- a/packages/meshbay-android/app/src/test/kotlin/org/meshbay/client/PhotoSyncTest.kt
+++ b/packages/meshbay-android/app/src/test/kotlin/org/meshbay/client/PhotoSyncTest.kt
@@ -25,11 +25,12 @@ class PhotoSyncTest {
private fun photo(id: Long, name: String = "IMG_$id.jpg", size: Long = 100, taken: Long = oct9,
added: Long = oct9 / 1000, modified: Long = oct9 / 1000, bucket: String = "cam",
- mime: String = "image/jpeg") =
- Photo(id, name, size, taken, added, modified, bucket, mime)
+ mime: String = "image/jpeg", video: Boolean = false) =
+ Photo(id, name, size, taken, added, modified, bucket, mime, video)
- private fun config(includeExisting: Boolean = true, since: Long = 0, albums: List<String> = listOf("cam")) =
- SyncConfig(albums, includeExisting, since)
+ private fun config(includeExisting: Boolean = true, since: Long = 0, albums: List<String> = listOf("cam"),
+ videos: Boolean = false) =
+ SyncConfig(albums, includeExisting, since, videos)
private val base = PhotoPlan.baseFor(Destination("bob", "g1", "Mine", "Backups"))
@@ -61,6 +62,27 @@ class PhotoSyncTest {
assertEquals(listOf(1L), out.map { it.photo.mediaId })
}
+ @Test fun `videos of the same albums go beside the photos, only when asked for`() {
+ val clip = photo(5, name = "PXL_20261009_120000.mp4", mime = "video/mp4", video = true)
+ assertTrue(plan(listOf(clip)).isEmpty())
+ val out = plan(listOf(photo(1), clip), config(videos = true))
+ assertEquals(setOf(1L, 5L), out.map { it.photo.mediaId }.toSet())
+ assertEquals("Backups/bob-photos/2026/2026-10", out.first { it.photo.video }.dir)
+ assertTrue("a video file is not taken for a photo", plan(listOf(photo(6, mime = "video/mp4")), config(videos = true)).isEmpty())
+ assertEquals("video-9.mp4", PhotoPlan.nameFor(photo(9, name = "_clip.mp4", mime = "video/mp4", video = true)))
+ assertEquals("video-9.mov", PhotoPlan.nameFor(photo(9, name = "", mime = "video/quicktime", video = true)))
+ }
+
+ @Test fun `ticking videos in keeps the starting point`() {
+ val o = JSONObject().put("albums", JSONArray(listOf("cam"))).put("includeExisting", false)
+ val first = SyncConfig.fromJson(o, 100)
+ val withVideos = SyncConfig.fromJson(o.put("includeVideos", true), 200, first)
+ assertEquals(100, withVideos.since)
+ assertTrue(withVideos.includeVideos)
+ assertEquals(withVideos, SyncConfig.parse(withVideos.toJson().toString()))
+ assertFalse(SyncConfig.parse("""{"albums":[],"since":1}""")!!.includeVideos)
+ }
+
@Test fun `a photo already sent is not sent again`() {
val p = photo(1)
assertTrue(plan(listOf(p), ledger = mapOf(1L to entry(p))).isEmpty())