diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 444b83e27..0e5386bad 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -60,11 +60,10 @@ jobs: - name: Add Dropbox API credentials shell: bash run: | - echo "dropbox.token = \"${{ secrets.DROPBOX_APP_TOKEN }}\"" >> app.properties + echo "dropbox.refresh_token = \"${{ secrets.DROPBOX_REFRESH_TOKEN }}\"" >> app.properties echo "dropbox.app_key = \"${{ secrets.DROPBOX_APP_KEY }}\"" >> app.properties - echo "dropbox.app_key_schema = \"db-${{ secrets.DROPBOX_APP_KEY }}\"" >> app.properties - - name: run tests + - name: Run tests uses: reactivecircus/android-emulator-runner@v2 with: api-level: 29 @@ -74,4 +73,5 @@ jobs: disable-animations: true disable-spellchecker: true profile: Nexus 6 - script: ./gradlew connectedCheck + # Run tests against the build with Dropbox included + script: ./gradlew connectedPremiumDebugAndroidTest --stacktrace diff --git a/app/src/androidTest/AndroidManifest.xml b/app/src/androidTest/AndroidManifest.xml index 80b8e5026..3b6d18ef0 100644 --- a/app/src/androidTest/AndroidManifest.xml +++ b/app/src/androidTest/AndroidManifest.xml @@ -5,6 +5,6 @@ xmlns:tools="http://schemas.android.com/tools"> - \ No newline at end of file + diff --git a/app/src/androidTest/java/com/orgzly/android/RetryTestRule.kt b/app/src/androidTest/java/com/orgzly/android/RetryTestRule.kt index 49564f437..c36faf04c 100644 --- a/app/src/androidTest/java/com/orgzly/android/RetryTestRule.kt +++ b/app/src/androidTest/java/com/orgzly/android/RetryTestRule.kt @@ -8,7 +8,7 @@ import org.junit.runners.model.Statement /** * Retry test rule used to retry test that failed. Retry failed test 3 times */ -class RetryTestRule(val retryCount: Int = 3, val sleepBetweenAttemptsMs: Long = 1000) : TestRule { +class RetryTestRule(val retryCount: Int = 3) : TestRule { private val TAG = RetryTestRule::class.java.simpleName @@ -30,7 +30,7 @@ class RetryTestRule(val retryCount: Int = 3, val sleepBetweenAttemptsMs: Long = } catch (t: Throwable) { caughtThrowable = t Log.e(TAG, description.displayName + ": run " + (i + 1) + " failed") - Thread.sleep(sleepBetweenAttemptsMs) + Thread.sleep(1000) } } diff --git a/app/src/androidTest/java/com/orgzly/android/query/QueryTest.kt b/app/src/androidTest/java/com/orgzly/android/query/QueryTest.kt index 58a44370c..af8047c24 100644 --- a/app/src/androidTest/java/com/orgzly/android/query/QueryTest.kt +++ b/app/src/androidTest/java/com/orgzly/android/query/QueryTest.kt @@ -28,9 +28,7 @@ class QueryTest(private val param: Parameter) : OrgzlyTest() { @Rule @JvmField - // Avoid sleeping between test attempts, as parameter creation vs test run - // timing is sensitive for many of these tests. - val mRetryTestRule: TestRule = RetryTestRule(3, 0) + val mRetryTestRule: TestRule = RetryTestRule(3) data class Parameter( val queryString: String, @@ -194,16 +192,6 @@ class QueryTest(private val param: Parameter) : OrgzlyTest() { expectedQueryString = "s.3d", expectedSqlSelection = "((scheduled_is_active = 1 AND scheduled_time_timestamp != 0 AND scheduled_time_timestamp < " + TimeUtils.timeFromNow(Calendar.DAY_OF_MONTH, 3+1) + "))" ), - Parameter( - queryString = "s.le.2h", - expectedQueryString = "s.2h", - expectedSqlSelection = "((scheduled_is_active = 1 AND scheduled_time_timestamp != 0 AND scheduled_time_timestamp < " + TimeUtils.timeFromNow(Calendar.HOUR_OF_DAY, 2+1) + "))" - ), - Parameter( - queryString = "s.le.+2h", - expectedQueryString = "s.2h", - expectedSqlSelection = "((scheduled_is_active = 1 AND scheduled_time_timestamp != 0 AND scheduled_time_timestamp < " + TimeUtils.timeFromNow(Calendar.HOUR_OF_DAY, 2+1) + "))" - ), Parameter( queryString = "d.tom", expectedQueryString = "d.tomorrow", @@ -219,11 +207,6 @@ class QueryTest(private val param: Parameter) : OrgzlyTest() { expectedQueryString = "c.ge.yesterday", expectedSqlSelection = "((closed_time_timestamp != 0 AND ${TimeUtils.timeFromNow(Calendar.DAY_OF_MONTH, -1)} <= closed_time_timestamp))" ), - Parameter( - queryString = "c.gt.-1h", - expectedQueryString = "c.gt.-1h", - expectedSqlSelection = "((closed_time_timestamp != 0 AND ${TimeUtils.timeFromNow(Calendar.HOUR_OF_DAY, 0)} <= closed_time_timestamp))" - ), Parameter( queryString = "p.a", expectedQueryString = "p.a", diff --git a/app/src/androidTest/java/com/orgzly/android/query/TimeSensitiveQueryTest.kt b/app/src/androidTest/java/com/orgzly/android/query/TimeSensitiveQueryTest.kt new file mode 100644 index 000000000..9cd583f7e --- /dev/null +++ b/app/src/androidTest/java/com/orgzly/android/query/TimeSensitiveQueryTest.kt @@ -0,0 +1,90 @@ +package com.orgzly.android.query + +import androidx.test.espresso.matcher.ViewMatchers +import com.orgzly.android.OrgzlyTest +import com.orgzly.android.query.sql.SqliteQueryBuilder +import com.orgzly.android.query.user.DottedQueryParser +import org.hamcrest.Matchers +import org.junit.Test +import java.util.Calendar + +/** + * Parameterization works poorly for these cases, because when there are many parameters, + * there may be a too long delay between parameter creation and test run, so that the + * timestamp is too old when the test is run. + */ +class TimeSensitiveQueryTest : OrgzlyTest() { + @Test + fun testScheduledWithinHours1() { + // Parse query + val queryString = "s.le.2h" + val expectedSqlSelection = "((scheduled_is_active = 1 AND scheduled_time_timestamp != 0 AND scheduled_time_timestamp < " + TimeUtils.timeFromNow( + Calendar.HOUR_OF_DAY, 2+1) + "))" + val parser = DottedQueryParser() + val query = parser.parse(queryString) + + // Build SQL + val sqlBuilder = SqliteQueryBuilder(context) + val sqlQuery = sqlBuilder.build(query) + + // Build query + val actualSqlSelection = sqlQuery.selection + + expectedSqlSelection.let { + ViewMatchers.assertThat( + queryString, + actualSqlSelection, + Matchers.`is`(expectedSqlSelection) + ) + } + } + + @Test + fun testScheduledWithinHours2() { + // Parse query + val queryString = "s.le.+2h" + val expectedSqlSelection = "((scheduled_is_active = 1 AND scheduled_time_timestamp != 0 AND scheduled_time_timestamp < " + TimeUtils.timeFromNow( + Calendar.HOUR_OF_DAY, 2+1) + "))" + val parser = DottedQueryParser() + val query = parser.parse(queryString) + + // Build SQL + val sqlBuilder = SqliteQueryBuilder(context) + val sqlQuery = sqlBuilder.build(query) + + // Build query + val actualSqlSelection = sqlQuery.selection + + expectedSqlSelection.let { + ViewMatchers.assertThat( + queryString, + actualSqlSelection, + Matchers.`is`(expectedSqlSelection) + ) + } + } + + @Test + fun testClosedRecently() { + // Parse query + val queryString = "c.gt.-1h" + val expectedSqlSelection = "((closed_time_timestamp != 0 AND ${TimeUtils.timeFromNow(Calendar.HOUR_OF_DAY, 0)} <= closed_time_timestamp))" + val parser = DottedQueryParser() + val query = parser.parse(queryString) + + // Build SQL + val sqlBuilder = SqliteQueryBuilder(context) + val sqlQuery = sqlBuilder.build(query) + + // Build query + val actualSqlSelection = sqlQuery.selection + + expectedSqlSelection.let { + ViewMatchers.assertThat( + queryString, + actualSqlSelection, + Matchers.`is`(expectedSqlSelection) + ) + } + } +} \ No newline at end of file