From b145fc9d506875b7d1ad6f2786d6c3c6708b7407 Mon Sep 17 00:00:00 2001 From: Konrad Pozniak Date: Tue, 1 Mar 2022 21:29:05 +0100 Subject: [PATCH] fix String.inc() and String.dec() not being inverse operations (#2355) --- .../viewmodel/CachedTimelineViewModel.kt | 2 +- .../keylesspalace/tusky/util/StringUtils.kt | 42 ++++++------ .../keylesspalace/tusky/StringUtilsTest.kt | 23 +++++-- .../keylesspalace/tusky/db/TimelineDaoTest.kt | 66 +++++++++++-------- 4 files changed, 80 insertions(+), 53 deletions(-) diff --git a/app/src/main/java/com/keylesspalace/tusky/components/timeline/viewmodel/CachedTimelineViewModel.kt b/app/src/main/java/com/keylesspalace/tusky/components/timeline/viewmodel/CachedTimelineViewModel.kt index 1da37bf41..53858d1b3 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/timeline/viewmodel/CachedTimelineViewModel.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/timeline/viewmodel/CachedTimelineViewModel.kt @@ -143,7 +143,7 @@ class CachedTimelineViewModel @Inject constructor( val nextPlaceholderId = timelineDao.getNextPlaceholderIdAfter(activeAccount.id, placeholderId) - val response = api.homeTimeline(maxId = placeholderId.inc(), sinceId = nextPlaceholderId, limit = 20).await() + val response = api.homeTimeline(maxId = placeholderId.inc(), sinceId = nextPlaceholderId, limit = LOAD_AT_ONCE).await() val statuses = response.body() if (!response.isSuccessful || statuses == null) { diff --git a/app/src/main/java/com/keylesspalace/tusky/util/StringUtils.kt b/app/src/main/java/com/keylesspalace/tusky/util/StringUtils.kt index 57b87f92c..f28e09c57 100644 --- a/app/src/main/java/com/keylesspalace/tusky/util/StringUtils.kt +++ b/app/src/main/java/com/keylesspalace/tusky/util/StringUtils.kt @@ -17,27 +17,39 @@ fun randomAlphanumericString(count: Int): String { } // We sort statuses by ID. Something we need to invent some ID for placeholder. -// Not sure if inc()/dec() should be made `operator` or not /** - * "Increment" string so that during sorting it's bigger than [this]. + * "Increment" string so that during sorting it's bigger than [this]. Inverse operation to [dec]. */ fun String.inc(): String { - // We assume that we will stay in the safe range for now val builder = this.toCharArray() - builder[lastIndex] = builder[lastIndex].inc() - return String(builder) + var i = builder.lastIndex + + while (i >= 0) { + if (builder[i] < 'z') { + builder[i] = builder[i].inc() + return String(builder) + } else { + builder[i] = '0' + } + i-- + } + return String( + CharArray(builder.size + 1) { index -> + if (index == 0) '0' else builder[index - 1] + } + ) } /** - * "Decrement" string so that during sorting it's smaller than [this]. + * "Decrement" string so that during sorting it's smaller than [this]. Inverse operation to [inc]. */ fun String.dec(): String { if (this.isEmpty()) return this val builder = this.toCharArray() var i = builder.lastIndex - while (i > 0) { + while (i >= 0) { if (builder[i] > '0') { builder[i] = builder[i].dec() return String(builder) @@ -46,12 +58,7 @@ fun String.dec(): String { } i-- } - return if (builder[0] > '1') { - builder[0] = builder[0].dec() - String(builder) - } else { - String(builder.copyOfRange(1, builder.size)) - } + return String(builder.copyOfRange(1, builder.size)) } /** @@ -71,15 +78,6 @@ fun String.isLessThan(other: String): Boolean { } } -fun String.idCompareTo(other: String): Int { - return when { - this === other -> 0 - this.length < other.length -> -1 - this.length > other.length -> 1 - else -> this.compareTo(other) - } -} - fun Spanned.trimTrailingWhitespace(): Spanned { var i = length do { diff --git a/app/src/test/java/com/keylesspalace/tusky/StringUtilsTest.kt b/app/src/test/java/com/keylesspalace/tusky/StringUtilsTest.kt index 5966cc39e..30cc971a6 100644 --- a/app/src/test/java/com/keylesspalace/tusky/StringUtilsTest.kt +++ b/app/src/test/java/com/keylesspalace/tusky/StringUtilsTest.kt @@ -27,22 +27,37 @@ class StringUtilsTest { @Test fun inc() { listOf( + "10786565059022968z" to "107865650590229690", "122" to "123", "12A" to "12B", - "1" to "2" + "11z" to "120", + "0zz" to "100", + "zz" to "000", + "4zzbz" to "4zzc0", + "" to "0", + "1" to "2", + "0" to "1", + "AGdxwSQqT3pW4xrLJA" to "AGdxwSQqT3pW4xrLJB", + "AGdfqi1HnlBFVl0tkz" to "AGdfqi1HnlBFVl0tl0" ).forEach { (l, r) -> assertEquals("$l + 1 = $r", r, l.inc()) } } @Test fun dec() { listOf( + "" to "", + "107865650590229690" to "10786565059022968z", "123" to "122", "12B" to "12A", "120" to "11z", - "100" to "zz", + "100" to "0zz", + "000" to "zz", + "4zzc0" to "4zzbz", "0" to "", - "" to "", - "2" to "1" + "2" to "1", + "1" to "0", + "AGdxwSQqT3pW4xrLJB" to "AGdxwSQqT3pW4xrLJA", + "AGdfqi1HnlBFVl0tl0" to "AGdfqi1HnlBFVl0tkz" ).forEach { (l, r) -> assertEquals("$l - 1 = $r", r, l.dec()) } } } diff --git a/app/src/test/java/com/keylesspalace/tusky/db/TimelineDaoTest.kt b/app/src/test/java/com/keylesspalace/tusky/db/TimelineDaoTest.kt index 687b5dcb0..1ddda3e01 100644 --- a/app/src/test/java/com/keylesspalace/tusky/db/TimelineDaoTest.kt +++ b/app/src/test/java/com/keylesspalace/tusky/db/TimelineDaoTest.kt @@ -144,7 +144,8 @@ class TimelineDaoTest { makeStatus(statusId = 1) ) - timelineDao.deleteRange(1, newStatuses.last().first.serverId, newStatuses.first().first.serverId) + val deletedCount = timelineDao.deleteRange(1, newStatuses.last().first.serverId, newStatuses.first().first.serverId) + assertEquals(3, deletedCount) for ((status, author, reblogAuthor) in newStatuses) { timelineDao.insertAccount(author) @@ -169,9 +170,11 @@ class TimelineDaoTest { fun deleteRange() = runBlocking { val statuses = listOf( makeStatus(statusId = 100), + makeStatus(statusId = 50), makeStatus(statusId = 15), makeStatus(statusId = 14), makeStatus(statusId = 13), + makeStatus(statusId = 13, accountId = 2), makeStatus(statusId = 12), makeStatus(statusId = 11), makeStatus(statusId = 9) @@ -185,20 +188,31 @@ class TimelineDaoTest { timelineDao.insertStatus(status) } - timelineDao.deleteRange(1, "12", "14") + assertEquals(3, timelineDao.deleteRange(1, "12", "14")) + assertEquals(0, timelineDao.deleteRange(1, "80", "80")) + assertEquals(0, timelineDao.deleteRange(1, "60", "80")) + assertEquals(0, timelineDao.deleteRange(1, "5", "8")) + assertEquals(0, timelineDao.deleteRange(1, "101", "1000")) + assertEquals(1, timelineDao.deleteRange(1, "50", "50")) - val pagingSource = timelineDao.getStatusesForAccount(1) - val loadResult = pagingSource.load(PagingSource.LoadParams.Refresh(null, 100, false)) - val loadedStatuses = (loadResult as PagingSource.LoadResult.Page).data + val loadParams: PagingSource.LoadParams = PagingSource.LoadParams.Refresh(null, 100, false) - val remainingStatuses = listOf( + val statusesAccount1 = (timelineDao.getStatusesForAccount(1).load(loadParams) as PagingSource.LoadResult.Page).data + val statusesAccount2 = (timelineDao.getStatusesForAccount(2).load(loadParams) as PagingSource.LoadResult.Page).data + + val remainingStatusesAccount1 = listOf( makeStatus(statusId = 100), makeStatus(statusId = 15), makeStatus(statusId = 11), makeStatus(statusId = 9) ) - assertStatuses(remainingStatuses, loadedStatuses) + val remainingStatusesAccount2 = listOf( + makeStatus(statusId = 13, accountId = 2) + ) + + assertStatuses(remainingStatusesAccount1, statusesAccount1) + assertStatuses(remainingStatusesAccount2, statusesAccount2) } @Test @@ -325,7 +339,7 @@ class TimelineDaoTest { } assertEquals("99", timelineDao.getNextPlaceholderIdAfter(1, "1000")) - assertEquals("94", timelineDao.getNextPlaceholderIdAfter(1, "97")) + assertEquals("94", timelineDao.getNextPlaceholderIdAfter(1, "99")) assertNull(timelineDao.getNextPlaceholderIdAfter(1, "90")) } @@ -364,28 +378,28 @@ class TimelineDaoTest { domain: String = "mastodon.example" ): Triple { val author = TimelineAccountEntity( - authorServerId, - accountId, - "localUsername@$domain", - "username@$domain", - "displayName", - "blah", - "avatar", - "[\"tusky\": \"http://tusky.cool/emoji.jpg\"]", - false + serverId = authorServerId, + timelineUserId = accountId, + localUsername = "localUsername@$domain", + username = "username@$domain", + displayName = "displayName", + url = "blah", + avatar = "avatar", + emojis = "[\"tusky\": \"http://tusky.cool/emoji.jpg\"]", + bot = false ) val reblogAuthor = if (reblog) { TimelineAccountEntity( - "R$authorServerId", - accountId, - "RlocalUsername", - "Rusername", - "RdisplayName", - "Rblah", - "Ravatar", - "[]", - false + serverId = "R$authorServerId", + timelineUserId = accountId, + localUsername = "RlocalUsername", + username = "Rusername", + displayName = "RdisplayName", + url = "Rblah", + avatar = "Ravatar", + emojis = "[]", + bot = false ) } else null