Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions .github/workflows/testing.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
# SPDX-License-Identifier: MIT

name: Android Tests

on:
pull_request:
push:
branches: [ main, stable-* ]

permissions:
contents: read

jobs:
validation:
name: Validate Gradle Wrapper
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.0
with:
distribution: 'temurin'
java-version: '17'
check-latest: true
cache: 'gradle'
- name: Validate Gradle Wrapper
uses: gradle/actions/wrapper-validation@ed408507eac070d1f99cc633dbcf757c94c7933a # v4.4.3

test:
name: Unit tests
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.0
with:
distribution: 'temurin'
java-version: '17'
check-latest: true
cache: 'gradle'
- name: Unit tests
run: bash ./gradlew test --stacktrace --no-configuration-cache
6 changes: 6 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ buildscript {
ext {
daggerVersion = "2.57.1"
kotlinVersion = "2.2.20"
jUnitVersion = "4.13.2"
androidXTestVersion = "1.3.0"
androidXEspressoTestVersion = "3.7.0"
mockkVersion = "1.14.5"
mockitoVersion = "5.20.0"
mockitoKotlinVersion = "6.0.0"
}
}

Expand Down
18 changes: 15 additions & 3 deletions core/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@ android {
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}

testOptions {
unitTests {
includeAndroidResources = true
}
}

compileOptions {
sourceCompatibility JavaVersion.VERSION_17
targetCompatibility JavaVersion.VERSION_17
Expand All @@ -41,9 +47,15 @@ dependencies {
implementation 'androidx.core:core-ktx:1.16.0'
implementation 'androidx.appcompat:appcompat:1.7.1'
implementation 'com.google.android.material:material:1.13.0'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.3.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.7.0'

testImplementation "junit:junit:$jUnitVersion"
testImplementation 'androidx.test:core:1.7.0'
testImplementation 'org.robolectric:robolectric:4.16'
testImplementation "org.mockito:mockito-core:$mockitoVersion"
testImplementation "org.mockito.kotlin:mockito-kotlin:$mockitoKotlinVersion"
testImplementation "io.mockk:mockk:$mockkVersion"
androidTestImplementation "androidx.test.ext:junit:$androidXTestVersion"
androidTestImplementation "androidx.test.espresso:espresso-core:$androidXEspressoTestVersion"
}

afterEvaluate {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
/**
/*
* Nextcloud Android Common Library
*
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: MIT
*/

package com.nextcloud.android.common.core.utils

import android.content.Context
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/*
* Nextcloud Android Common Library
*
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: MIT
*/

package com.nextcloud.android.common.core.utils

import android.content.Context
import android.os.Build
import androidx.test.core.app.ApplicationProvider
import com.nextcloud.android.common.core.R
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
import org.robolectric.annotation.Config
import java.text.SimpleDateFormat
import java.util.Calendar
import java.util.Locale

/**
* DateFormatter tests checking the different formats relative/absolute date formatting.
*/
@RunWith(RobolectricTestRunner::class)
@Config(sdk = [Build.VERSION_CODES.Q])
class DateFormatterTest {
private lateinit var dateFormatter: DateFormatter
private lateinit var context: Context

@Before
fun setUp() {
context = ApplicationProvider.getApplicationContext()
// Force a specific locale to make tests independent of the test environment's locale
val config = context.resources.configuration
config.setLocale(Locale.US)
val localizedContext = context.createConfigurationContext(config)
dateFormatter = DateFormatter(localizedContext)
}

@Test
fun `Test 'now' formatting`() {
val calendar = Calendar.getInstance()
val formattedDate = dateFormatter.getConditionallyRelativeFormattedTimeSpan(calendar)
val expected = context.getString(R.string.date_formatting_now)
assertEquals(expected, formattedDate)
}

@Test
fun `Test relative minutes formatting`() {
val calendar = Calendar.getInstance()
calendar.add(Calendar.MINUTE, -5)
val formattedDate = dateFormatter.getConditionallyRelativeFormattedTimeSpan(calendar)
val expected = context.getString(R.string.date_formatting_relative_minutes, 5)
assertEquals(expected, formattedDate)
}

@Test
fun `Test relative hours formatting`() {
val calendar = Calendar.getInstance()
calendar.add(Calendar.HOUR, -3)
val formattedDate = dateFormatter.getConditionallyRelativeFormattedTimeSpan(calendar)
val expected = context.resources.getQuantityString(R.plurals.date_formatting_relative_hours, 3, 3)
assertEquals(expected, formattedDate)
}

@Test
fun `Test day of the week formatting`() {
val calendar = Calendar.getInstance()
calendar.add(Calendar.DAY_OF_YEAR, -4)
val sdf = SimpleDateFormat("EEE", Locale.US)
val expected = sdf.format(calendar.time)
val formattedDate = dateFormatter.getConditionallyRelativeFormattedTimeSpan(calendar)
assertEquals(expected, formattedDate)
}

@Test
fun `Test month and day formatting`() {
val calendar = Calendar.getInstance()
calendar.add(Calendar.DAY_OF_YEAR, -100)
val sdf = SimpleDateFormat("MMM d", Locale.US)
val expected = sdf.format(calendar.time)
val formattedDate = dateFormatter.getConditionallyRelativeFormattedTimeSpan(calendar)
assertEquals(expected, formattedDate)
}

@Test
fun `Test full date formatting`() {
val calendar = Calendar.getInstance()
calendar.add(Calendar.YEAR, -2)
val sdf = SimpleDateFormat("MMM d, yyyy", Locale.US)
val expected = sdf.format(calendar.time)
val formattedDate = dateFormatter.getConditionallyRelativeFormattedTimeSpan(calendar)
assertEquals(expected, formattedDate)
}
}
Loading
Loading