Skip to content

Commit b981fff

Browse files
Merge pull request #16612 from nextcloud/ph/fix_instrumented_testing
Fix: Instrumented tests are executed in Drone CI again
2 parents 8663603 + 0bb255f commit b981fff

11 files changed

Lines changed: 190 additions & 60 deletions

File tree

app/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ android {
130130

131131
debug {
132132
enableUnitTestCoverage = project.hasProperty("coverage")
133+
enableAndroidTestCoverage = project.hasProperty("coverage")
133134
resConfigs("xxxhdpi")
134135
}
135136
}

app/src/androidTest/java/com/nextcloud/ui/SetOnlineStatusBottomSheetIT.kt

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,15 +37,14 @@ class SetOnlineStatusBottomSheetIT : AbstractIT() {
3737
launchActivity<FileDisplayActivity>().use { scenario ->
3838
onView(isRoot()).check(matches(isDisplayed()))
3939

40-
onView(withId(R.id.clearStatusAfterSpinner))
41-
.check(matches(isDisplayed()))
42-
4340
scenario.onActivity { activity ->
4441
val sut = SetOnlineStatusBottomSheet(
4542
Status(StatusType.DND, "Working hard…", "🤖", -1)
4643
)
4744
sut.show(activity.supportFragmentManager, "")
4845
}
46+
onView(withId(R.id.onlineStatus))
47+
.check(matches(isDisplayed()))
4948
}
5049
}
5150
}

app/src/androidTest/java/com/nextcloud/ui/SetStatusMessageBottomSheetIT.kt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,10 @@ import androidx.test.espresso.Espresso.onView
1414
import androidx.test.espresso.assertion.ViewAssertions.matches
1515
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
1616
import androidx.test.espresso.matcher.ViewMatchers.isRoot
17+
import androidx.test.espresso.matcher.ViewMatchers.withId
1718
import androidx.test.rule.GrantPermissionRule
1819
import com.owncloud.android.AbstractIT
20+
import com.owncloud.android.R
1921
import com.owncloud.android.lib.resources.users.ClearAt
2022
import com.owncloud.android.lib.resources.users.PredefinedStatus
2123
import com.owncloud.android.lib.resources.users.Status
@@ -41,7 +43,6 @@ class SetStatusMessageBottomSheetIT : AbstractIT() {
4143
user,
4244
Status(StatusType.DND, "Working hard…", "🤖", -1)
4345
)
44-
sut.show(activity.supportFragmentManager, "")
4546
val predefinedStatus: ArrayList<PredefinedStatus> = arrayListOf(
4647
PredefinedStatus("meeting", "📅", "In a meeting", ClearAt("period", "3600")),
4748
PredefinedStatus("commuting", "🚌", "Commuting", ClearAt("period", "1800")),
@@ -51,7 +52,11 @@ class SetStatusMessageBottomSheetIT : AbstractIT() {
5152
PredefinedStatus("vacationing", "🌴", "Vacationing", null)
5253
)
5354
sut.setPredefinedStatus(predefinedStatus)
55+
sut.show(activity.supportFragmentManager, "")
5456
}
57+
58+
onView(withId(R.id.predefinedStatusList))
59+
.check(matches(isDisplayed()))
5560
}
5661
}
5762
}

app/src/androidTest/java/com/nextcloud/utils/AutoRenameTests.kt

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
/*
22
* Nextcloud - Android Client
33
*
4+
* SPDX-FileCopyrightText: 2026 Philipp Hasper <vcs@hasper.info>
45
* SPDX-FileCopyrightText: 2024 Alper Ozturk <alper.ozturk@nextcloud.com>
56
* SPDX-License-Identifier: AGPL-3.0-or-later
67
*/
@@ -241,13 +242,52 @@ class AutoRenameTests : AbstractOnServerIT() {
241242
assert(result == expectedFilename) { "Expected $expectedFilename but got $result" }
242243
}
243244

245+
/**
246+
* For documentation see [com.nextcloud.utils.extensions.checkWCFRestrictions]
247+
*/
244248
@Test
245-
fun skipAutoRenameWhenWCFDisabled() {
246-
capability = capability.apply {
249+
fun testWCFDisabledOnNextcloud32ShouldSkipRestrictions() {
250+
val filename = " readme.txt "
251+
val nc32Capability = capability.apply {
252+
versionMayor = NextcloudVersion.nextcloud_32.majorVersionNumber
247253
isWCFEnabled = CapabilityBooleanType.FALSE
248254
}
255+
val result = AutoRename.rename(filename, nc32Capability, isFolderPath = true)
256+
assert(result == filename) { "Expected $filename but got $result" }
257+
}
258+
259+
@Test
260+
fun testWCFEnabledOnNextcloud32ShouldApplyRestrictions() {
249261
val filename = " readme.txt "
250-
val result = AutoRename.rename(filename, capability, isFolderPath = true)
262+
val nc32Capability = capability.apply {
263+
versionMayor = NextcloudVersion.nextcloud_32.majorVersionNumber
264+
isWCFEnabled = CapabilityBooleanType.TRUE
265+
}
266+
val result = AutoRename.rename(filename, nc32Capability, isFolderPath = true)
267+
val expectedFilename = "readme.txt"
268+
assert(result == expectedFilename) { "Expected $expectedFilename but got $result" }
269+
}
270+
271+
@Test
272+
fun testWCFDisabledOnNextcloud30to31ShouldStillApplyRestrictions() {
273+
val filename = " readme.txt "
274+
val nc30Capability = capability.apply {
275+
versionMayor = NextcloudVersion.nextcloud_30.majorVersionNumber
276+
isWCFEnabled = CapabilityBooleanType.FALSE
277+
}
278+
val result = AutoRename.rename(filename, nc30Capability, isFolderPath = true)
279+
val expectedFilename = "readme.txt"
280+
assert(result == expectedFilename) { "Expected $expectedFilename but got $result" }
281+
}
282+
283+
@Test
284+
fun testWCFOnNextcloudBelow30ShouldSkipRestrictions() {
285+
val filename = " readme.txt "
286+
val nc29Capability = capability.apply {
287+
versionMayor = NextcloudVersion.nextcloud_29.majorVersionNumber
288+
isWCFEnabled = CapabilityBooleanType.TRUE
289+
}
290+
val result = AutoRename.rename(filename, nc29Capability, isFolderPath = true)
251291
assert(result == filename) { "Expected $filename but got $result" }
252292
}
253293
}

app/src/androidTest/java/com/nextcloud/utils/UploadDateTests.kt

Lines changed: 114 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
/*
22
* Nextcloud - Android Client
33
*
4+
* SPDX-FileCopyrightText: 2026 Philipp Hasper <vcs@hasper.info>
45
* SPDX-FileCopyrightText: 2026 Alper Ozturk <alper.ozturk@nextcloud.com>
56
* SPDX-License-Identifier: AGPL-3.0-or-later
67
*/
@@ -13,14 +14,11 @@ import androidx.test.platform.app.InstrumentationRegistry
1314
import com.nextcloud.client.database.entity.UploadEntity
1415
import com.nextcloud.client.database.entity.toOCUpload
1516
import com.nextcloud.client.database.entity.toUploadEntity
17+
import com.nextcloud.utils.date.DateFormatPattern
1618
import com.owncloud.android.R
1719
import com.owncloud.android.utils.DisplayUtils
18-
import io.mockk.MockKAnnotations
19-
import io.mockk.every
20-
import io.mockk.mockkStatic
2120
import org.junit.Assert.assertEquals
2221
import org.junit.Assert.assertNotNull
23-
import org.junit.Assert.assertTrue
2422
import org.junit.Before
2523
import org.junit.Test
2624
import java.text.SimpleDateFormat
@@ -30,25 +28,22 @@ import java.util.Locale
3028
class UploadDateTests {
3129

3230
companion object {
33-
private const val JANUARY_27_2026 = 1769505718000
34-
private const val ONE_YEAR = 365L * 24 * 60 * 60 * 1000
35-
private const val ONE_MONTH = 30L * 24 * 60 * 60 * 1000
36-
private const val ONE_WEEK = 7 * 24 * 60 * 60 * 1000
37-
private const val TWO_HOURS = 2 * 60 * 60 * 1000
38-
private const val ONE_MINUTE = 60_000
39-
private const val THIRTY_SECONDS = 30_000
40-
41-
private const val DATE_FORMATTER_PATTERN = "MMM dd, yyyy, hh:mm:ss a"
31+
private const val THIRTY_SECONDS = 30_000L
32+
private const val ONE_MINUTE = 60_000L
33+
private const val ONE_HOUR = 60 * ONE_MINUTE
34+
private const val ONE_DAY = 24 * ONE_HOUR
35+
36+
private const val ONE_YEAR = 365L * ONE_DAY
37+
private const val ONE_MONTH = 30L * ONE_DAY
38+
private const val ONE_WEEK = 7L * ONE_DAY
39+
private const val TWO_HOURS = 2L * ONE_HOUR
4240
}
4341

4442
private lateinit var context: Context
4543

4644
@Before
4745
fun setup() {
48-
context = InstrumentationRegistry.getInstrumentation().context
49-
MockKAnnotations.init(this, relaxed = true)
50-
mockkStatic(System::class)
51-
every { System.currentTimeMillis() } returns JANUARY_27_2026
46+
context = InstrumentationRegistry.getInstrumentation().targetContext
5247
}
5348

5449
@Test
@@ -92,62 +87,140 @@ class UploadDateTests {
9287
fun getRelativeDateTimeStringReturnsSecondsAgoForRecentPast() {
9388
val result = DisplayUtils.getRelativeDateTimeString(
9489
context,
95-
JANUARY_27_2026 - THIRTY_SECONDS,
96-
DateUtils.SECOND_IN_MILLIS,
90+
System.currentTimeMillis() - THIRTY_SECONDS,
91+
DateUtils.MINUTE_IN_MILLIS,
9792
DateUtils.WEEK_IN_MILLIS,
9893
0
9994
)
100-
assertTrue(result.toString() == context.getString(R.string.file_list_seconds_ago))
95+
assertEquals(context.getString(R.string.file_list_seconds_ago), result.toString())
10196
}
10297

10398
@Test
104-
fun getRelativeDateTimeStringReturnsFutureAsHumanReadableWhenShowFutureIsFalse() {
105-
val formatter = SimpleDateFormat(DATE_FORMATTER_PATTERN, Locale.US)
106-
val time = JANUARY_27_2026 + ONE_MINUTE
107-
val expected = formatter.format(Date(time))
99+
fun getRelativeDateTimeStringReturnsFutureAsAbsoluteWhenShowFutureIsFalse() {
100+
val formatter = SimpleDateFormat("MMM d, yyyy h:mm:ss a", Locale.US)
101+
val expected = formatter.format(Date(System.currentTimeMillis() + ONE_MINUTE))
108102

109-
assertRelativeDateTimeString(time, expected, DateUtils.SECOND_IN_MILLIS)
103+
val result = DisplayUtils.getRelativeDateTimeString(
104+
context,
105+
System.currentTimeMillis() + ONE_MINUTE,
106+
DateUtils.SECOND_IN_MILLIS,
107+
DateUtils.WEEK_IN_MILLIS,
108+
0,
109+
false
110+
)
111+
assertEquals(expected, result.toString())
112+
}
113+
114+
@Test
115+
fun getRelativeDateTimeStringReturnsFutureAsRelativeWhenShowFutureIsTrue() {
116+
val expected = "In 1 minute"
117+
val time = System.currentTimeMillis() + ONE_MINUTE
118+
119+
assertRelativeDateTimeString(time, expected, DateUtils.MINUTE_IN_MILLIS, showFuture = true)
110120
}
111121

112122
@Test
113-
fun getRelativeDateTimeStringReturnsProperRelativeStringForHoursAgo() {
123+
fun getRelativeDateTimeStringReturnsRelativeStringForHoursAgo() {
114124
val expected = "2 hours ago"
115-
val time = JANUARY_27_2026 - TWO_HOURS
125+
val time = System.currentTimeMillis() - TWO_HOURS
116126

117-
assertRelativeDateTimeString(time, expected, DateUtils.MINUTE_IN_MILLIS)
127+
assertRelativeDateTimeString(time, expected, DateUtils.SECOND_IN_MILLIS)
118128
}
119129

120130
@Test
121-
fun getRelativeDateTimeStringReturnsRelativeStringForOneWeekAgo() {
122-
val expected = "Jan 20"
123-
val time = JANUARY_27_2026 - ONE_WEEK
131+
fun getRelativeDateTimeStringReturnsAbbreviatedStringForOneWeekAgo() {
132+
val time = System.currentTimeMillis() - ONE_WEEK
133+
val formatter = SimpleDateFormat(DateFormatPattern.MonthWithDate.pattern, Locale.US)
134+
val expected = formatter.format(Date(time))
124135

125136
assertRelativeDateTimeString(time, expected)
126137
}
127138

128139
@Test
129-
fun getRelativeDateTimeStringReturnsRelativeStringForOneMonthAgo() {
130-
val expected = "12/28/2025"
131-
val time = JANUARY_27_2026 - ONE_MONTH
140+
fun getRelativeDateTimeStringReturnsAbbreviatedStringForOneMonthAgo() {
141+
val time = System.currentTimeMillis() - ONE_MONTH
142+
val formatter = SimpleDateFormat(DateFormatPattern.MonthWithDate.pattern, Locale.US)
143+
val expected = formatter.format(Date(time))
132144

133-
assertRelativeDateTimeString(time, expected, DateUtils.DAY_IN_MILLIS)
145+
assertRelativeDateTimeString(time, expected, DateUtils.SECOND_IN_MILLIS)
134146
}
135147

136148
@Test
137-
fun getRelativeDateTimeStringReturnsRelativeStringForOneYearAgo() {
138-
val expected = "1/27/2025"
139-
val time = JANUARY_27_2026 - ONE_YEAR
149+
fun getRelativeDateTimeStringReturnsAbsoluteStringForOneYearAgo() {
150+
val time = System.currentTimeMillis() - ONE_YEAR
151+
val formatter = SimpleDateFormat("M/d/YYYY", Locale.US)
152+
val expected = formatter.format(Date(time))
140153

141-
assertRelativeDateTimeString(time, expected, DateUtils.DAY_IN_MILLIS)
154+
assertRelativeDateTimeString(time, expected, DateUtils.SECOND_IN_MILLIS)
155+
}
156+
157+
@Suppress("MagicNumber")
158+
@Test
159+
fun getRelativeDateTimeStringReturnsDaysForDayInMillis() {
160+
var testTimestamp = System.currentTimeMillis()
161+
var expected = "Today"
162+
var result = DisplayUtils.getRelativeDateTimeString(
163+
context,
164+
testTimestamp,
165+
DateUtils.DAY_IN_MILLIS,
166+
DateUtils.WEEK_IN_MILLIS,
167+
0,
168+
false
169+
)
170+
assertEquals(expected, result)
171+
172+
testTimestamp = System.currentTimeMillis() - DateUtils.DAY_IN_MILLIS
173+
expected = "Yesterday"
174+
result = DisplayUtils.getRelativeDateTimeString(
175+
context,
176+
testTimestamp,
177+
DateUtils.DAY_IN_MILLIS,
178+
DateUtils.WEEK_IN_MILLIS,
179+
0,
180+
false
181+
)
182+
assertEquals(expected, result)
183+
184+
testTimestamp = System.currentTimeMillis() - 2 * DateUtils.DAY_IN_MILLIS
185+
expected = "2 days ago"
186+
result = DisplayUtils.getRelativeDateTimeString(
187+
context,
188+
testTimestamp,
189+
DateUtils.DAY_IN_MILLIS,
190+
DateUtils.WEEK_IN_MILLIS,
191+
0,
192+
false
193+
)
194+
assertEquals(expected, result)
195+
196+
testTimestamp = System.currentTimeMillis() - 7 * DateUtils.DAY_IN_MILLIS
197+
expected = SimpleDateFormat(DateFormatPattern.MonthWithDate.pattern, Locale.US).format(testTimestamp)
198+
result = DisplayUtils.getRelativeDateTimeString(
199+
context,
200+
testTimestamp,
201+
DateUtils.DAY_IN_MILLIS,
202+
DateUtils.WEEK_IN_MILLIS,
203+
0,
204+
false
205+
)
206+
assertEquals(expected, result)
142207
}
143208

144209
private fun assertRelativeDateTimeString(
145210
time: Long,
146211
expected: String,
147212
minResolution: Long = DateUtils.MINUTE_IN_MILLIS,
148-
transitionResolution: Long = DateUtils.WEEK_IN_MILLIS
213+
transitionResolution: Long = DateUtils.WEEK_IN_MILLIS,
214+
showFuture: Boolean = false
149215
) {
150-
val result = DisplayUtils.getRelativeDateTimeString(context, time, minResolution, transitionResolution, 0)
216+
val result = DisplayUtils.getRelativeDateTimeString(
217+
context,
218+
time,
219+
minResolution,
220+
transitionResolution,
221+
0,
222+
showFuture
223+
)
151224
assertEquals(expected.normalizeResult(), result.toString().normalizeResult())
152225
}
153226

app/src/androidTest/java/com/owncloud/android/datamodel/UploadStorageManagerTest.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -113,10 +113,12 @@ public void largeTest() {
113113
}
114114

115115
OCUpload[] storedUploads = uploadsStorageManager.getAllStoredUploads();
116-
assertEquals(size, uploadsStorageManager.getAllStoredUploads().length);
116+
assertEquals(size, storedUploads.length);
117+
assertEquals(uploads.size(), storedUploads.length);
117118

118119
for (int i = 0; i < size; i++) {
119-
assertTrue(contains(uploads, storedUploads[i]));
120+
assertTrue("Upload " + (i+1) + "/" + size + " not found in stored uploads: " + storedUploads[i].getLocalPath(),
121+
contains(uploads, storedUploads[i]));
120122
}
121123
}
122124

@@ -138,7 +140,7 @@ public void testIsSame() {
138140

139141
private boolean contains(ArrayList<OCUpload> uploads, OCUpload storedUpload) {
140142
for (int i = 0; i < uploads.size(); i++) {
141-
if (storedUpload.isSame(uploads.get(i))) {
143+
if (storedUpload.isSame(uploads.get(i), true)) {
142144
return true;
143145
}
144146
}

app/src/main/java/com/nextcloud/ui/SetStatusMessageBottomSheet.kt

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -354,7 +354,10 @@ class SetStatusMessageBottomSheet(val user: User, val currentStatus: Status?) :
354354
@SuppressLint("NotifyDataSetChanged")
355355
@VisibleForTesting
356356
fun setPredefinedStatus(predefinedStatus: ArrayList<PredefinedStatus>) {
357-
adapter.list = predefinedStatus
358-
binding.predefinedStatusList.adapter?.notifyDataSetChanged()
357+
this.predefinedStatus = predefinedStatus
358+
if (this::adapter.isInitialized) {
359+
adapter.list = predefinedStatus
360+
binding.predefinedStatusList.adapter?.notifyDataSetChanged()
361+
}
359362
}
360363
}

0 commit comments

Comments
 (0)