android-priority-jobqueue icon indicating copy to clipboard operation
android-priority-jobqueue copied to clipboard

Robolectric exception on unit testing

Open zoharlevin opened this issue 9 years ago • 6 comments

I'm using Dagger for DI and I have a module that provides JobManager. When I run unit tests using Robolectric 3.0 when the test ends I'm getting this exception:

java.lang.RuntimeException: java.lang.InterruptedException
    at org.robolectric.shadows.ShadowSQLiteConnection$Connections.execute(ShadowSQLiteConnection.java:479)
    at org.robolectric.shadows.ShadowSQLiteConnection$Connections.close(ShadowSQLiteConnection.java:390)
    at org.robolectric.shadows.ShadowSQLiteConnection$Connections.reset(ShadowSQLiteConnection.java:402)
    at org.robolectric.shadows.ShadowSQLiteConnection.reset(ShadowSQLiteConnection.java:80)
    at org.robolectric.Shadows.reset(Shadows.java:1626)
    at org.robolectric.Robolectric.reset(Robolectric.java:22)
    at org.robolectric.internal.ParallelUniverse.resetStaticState(ParallelUniverse.java:43)
    at org.robolectric.RobolectricTestRunner$2.evaluate(RobolectricTestRunner.java:259)
    at org.robolectric.RobolectricTestRunner.runChild(RobolectricTestRunner.java:188)
    at org.robolectric.RobolectricTestRunner.runChild(RobolectricTestRunner.java:54)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.robolectric.RobolectricTestRunner$1.evaluate(RobolectricTestRunner.java:152)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:78)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:212)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:68)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:140)
Caused by: java.lang.InterruptedException
    at java.util.concurrent.FutureTask.awaitDone(FutureTask.java:400)
    at java.util.concurrent.FutureTask.get(FutureTask.java:187)
    at org.robolectric.shadows.ShadowSQLiteConnection$Connections.execute(ShadowSQLiteConnection.java:462)
    ... 25 more

I tried setting inTestMode on the configuration of JobManager, but that didn't make any difference.

My unit tests actually aren't using the code that depends on JobManager (but it is injected anyway. it's just not currently being used in runtime due to some internal configuration I have).

Am I missing something? It seems maybe I should be closing the persistent job queue somehow?

zoharlevin avatar Dec 14 '15 11:12 zoharlevin

Hmm i have no idea about this :/. JobQueue itself uses robolectric 3 for its own tests. I looks like I do a tearDown action. Maybe that will fix your issue:

https://github.com/yigit/android-priority-jobqueue/blob/master/jobqueue/src/test/java/com/path/android/jobqueue/test/jobmanager/JobManagerTestBase.java#L45

yigit avatar Dec 16 '15 03:12 yigit

Interesting. I've worked around this issue for now by injecting Lazy<JobManager> which means in the case of my test it won't be initialized so I don't have any problem at the end of the test. But I'm not sure where I could release the JobManager anyway since I'm not using it. I'll describe my case a bit more in detail, this is some code from my DI module (before I changed to Lazy):


@Provides
    AnalyticsWriter provideAnalyticsWriter(ConfigurationSet configurationSet, AnalyticsListenerApiWriter analyticsListenerApiWriter, JobAnalyticsWriter jobAnalyticsWriter)
    {
        if (configurationSet.getBoolean(ConfigurationKeys.persistAnalytics, true)) {
            return jobAnalyticsWriter;
        }

        return analyticsListenerApiWriter;
    }

    @Provides
    JobExecuter provideJobExecuter(JobManager jobManager)
    {
        return new JobQueueJobExecuter(jobManager);
    }

    @Provides
    @Singleton
    JobManager provideJobManager(ConfigurationSet configurationSet, @ForApplication Context context, @ForApplication Injector injector) {
        Configuration configuration = new Configuration.Builder(context)
                .maxConsumerCount(1)
                .customLogger(new CustomLogger() {
                    private final Logger logger = LoggerFactory.getLogger(JobManager.class);

                    @Override
                    public boolean isDebugEnabled() {
                        return logger.isDebugEnabled();
                    }

                    @Override
                    public void d(String text, Object... args) {
                        logger.debug(text, args);
                    }

                    @Override
                    public void e(Throwable t, String text, Object... args) {
                        logger.error(text, args, t);
                    }

                    @Override
                    public void e(String text, Object... args) {
                        logger.error(text, args);
                    }
                })
                .injector(new JobQueueDependencyInjector(injector))
                .jobSerializer(new JsonJobSerializer())
                .build();
        return new JobManager(context, configuration);
    }

I have a class called JobAnalyticsWriter which is dependant on JobManager (because it has a member of JobExecuter) and I also have a class called AnalyticsListenerApiWriter which has nothing to do with your job queue. They both implement the interface AnalyticsWriter. In my test I have a configuration set to persistAnalytics=false which means that the provider will return the analyticsListenerApiWriter which does not depend on JobManager. So in my test I don't actually have access to the instance of JobAnalyticsWriter and therefore I don't have access to JobManager so I don't know where I would be able to create a teardown action that would be relevant. (ideas?)

What I did was change the following function like so:


@Provides
    JobExecuter provideJobExecuter(Lazy<JobManager> jobManager)
    {
        return new JobQueueJobExecuter(jobManager);
    }

So I'm using Lazy<JobManager> in my code and it prevents the exception (in my test JobManager never gets initialized).

I'd be happy to hear any other thoughts/suggestions you may have for this. Thanks

zoharlevin avatar Dec 16 '15 09:12 zoharlevin

I know it is not very elegant but if you have a BaseTest, you can write a tear down function. Or if you are using JUnit4, you can setup a @Rule that will add a TestWatcher to stop the JobQueue at the end of the test.

Also, I don't see anything wrong in your JQ setup.

yigit avatar Dec 17 '15 05:12 yigit

hi,

i'm getting the following exception on my robolectric tests:

Exception in thread "job-manager" java.lang.RuntimeException: java.util.concurrent.ExecutionException: java.lang.IllegalStateException: Statement 7222 [SELECT * FROM (SELECT deadline FROM job_holder WHERE ( (deadline != 9223372036854775807 AND deadline <= ?) OR network_type <= ?) AND (cancelled IS NULL OR cancelled != 1) AND running_session_id != ? ORDER BY 1 ASC LIMIT 1) UNION SELECT * FROM (SELECT delay_until_ns FROM job_holder WHERE ( (deadline != 9223372036854775807 AND deadline <= ?) OR network_type <= ?) AND (cancelled IS NULL OR cancelled != 1) AND running_session_id != ? ORDER BY 1 ASC LIMIT 1) ORDER BY 1 ASC LIMIT 1]DB[175][C][D] is disposed
    at org.robolectric.shadows.ShadowSQLiteConnection$Connections.execute(ShadowSQLiteConnection.java:469)
    at org.robolectric.shadows.ShadowSQLiteConnection.nativeResetStatementAndClearBindings(ShadowSQLiteConnection.java:298)
    at android.database.sqlite.SQLiteConnection.nativeResetStatementAndClearBindings(SQLiteConnection.java)
    at android.database.sqlite.SQLiteConnection.releasePreparedStatement(SQLiteConnection.java:915)
    at android.database.sqlite.SQLiteConnection.executeForLong(SQLiteConnection.java:603)
    at android.database.sqlite.SQLiteSession.executeForLong(SQLiteSession.java:652)
    at android.database.sqlite.SQLiteStatement.simpleQueryForLong(SQLiteStatement.java:107)
    at com.birbit.android.jobqueue.persistentQueue.sqlite.SqliteJobQueue.getNextJobDelayUntilNs(SqliteJobQueue.java:328)
    at com.birbit.android.jobqueue.cachedQueue.CachedJobQueue.getNextJobDelayUntilNs(CachedJobQueue.java:88)
    at com.birbit.android.jobqueue.JobManagerThread.getNextWakeUpNs(JobManagerThread.java:612)
    at com.birbit.android.jobqueue.JobManagerThread$1.onIdle(JobManagerThread.java:272)
    at com.birbit.android.jobqueue.messaging.PriorityMessageQueue.next(PriorityMessageQueue.java:89)
    at com.birbit.android.jobqueue.messaging.PriorityMessageQueue.consume(PriorityMessageQueue.java:36)
    at com.birbit.android.jobqueue.JobManagerThread.run(JobManagerThread.java:222)
    at java.lang.Thread.run(Thread.java:745)
Caused by: java.util.concurrent.ExecutionException: java.lang.IllegalStateException: Statement 7222 [SELECT * FROM (SELECT deadline FROM job_holder WHERE ( (deadline != 9223372036854775807 AND deadline <= ?) OR network_type <= ?) AND (cancelled IS NULL OR cancelled != 1) AND running_session_id != ? ORDER BY 1 ASC LIMIT 1) UNION SELECT * FROM (SELECT delay_until_ns FROM job_holder WHERE ( (deadline != 9223372036854775807 AND deadline <= ?) OR network_type <= ?) AND (cancelled IS NULL OR cancelled != 1) AND running_session_id != ? ORDER BY 1 ASC LIMIT 1) ORDER BY 1 ASC LIMIT 1]DB[175][C][D] is disposed
    at java.util.concurrent.FutureTask.report(FutureTask.java:122)
    at java.util.concurrent.FutureTask.get(FutureTask.java:192)
    at com.google.common.util.concurrent.Uninterruptibles.getUninterruptibly(Uninterruptibles.java:143)
    at org.robolectric.shadows.ShadowSQLiteConnection$Connections.execute(ShadowSQLiteConnection.java:459)
    at org.robolectric.shadows.ShadowSQLiteConnection.nativeResetStatementAndClearBindings(ShadowSQLiteConnection.java:298)
    at android.database.sqlite.SQLiteConnection.nativeResetStatementAndClearBindings(SQLiteConnection.java)
    at android.database.sqlite.SQLiteConnection.$$robo$$releasePreparedStatement(SQLiteConnection.java:915)
    at android.database.sqlite.SQLiteConnection.releasePreparedStatement(SQLiteConnection.java)
    at android.database.sqlite.SQLiteConnection.$$robo$$executeForLong(SQLiteConnection.java:603)
    at android.database.sqlite.SQLiteConnection.executeForLong(SQLiteConnection.java)
    at android.database.sqlite.SQLiteSession.$$robo$$executeForLong(SQLiteSession.java:652)
    at android.database.sqlite.SQLiteSession.executeForLong(SQLiteSession.java)
    at android.database.sqlite.SQLiteStatement.$$robo$$simpleQueryForLong(SQLiteStatement.java:107)
    at android.database.sqlite.SQLiteStatement.simpleQueryForLong(SQLiteStatement.java)
    ... 8 more
Caused by: java.lang.IllegalStateException: Statement 7222 [SELECT * FROM (SELECT deadline FROM job_holder WHERE ( (deadline != 9223372036854775807 AND deadline <= ?) OR network_type <= ?) AND (cancelled IS NULL OR cancelled != 1) AND running_session_id != ? ORDER BY 1 ASC LIMIT 1) UNION SELECT * FROM (SELECT delay_until_ns FROM job_holder WHERE ( (deadline != 9223372036854775807 AND deadline <= ?) OR network_type <= ?) AND (cancelled IS NULL OR cancelled != 1) AND running_session_id != ? ORDER BY 1 ASC LIMIT 1) ORDER BY 1 ASC LIMIT 1]DB[175][C][D] is disposed
    at org.robolectric.shadows.ShadowSQLiteConnection$Connections.getStatement(ShadowSQLiteConnection.java:359)
    at org.robolectric.shadows.ShadowSQLiteConnection.stmt(ShadowSQLiteConnection.java:66)
    at org.robolectric.shadows.ShadowSQLiteConnection.access$000(ShadowSQLiteConnection.java:51)
    at org.robolectric.shadows.ShadowSQLiteConnection$16.call(ShadowSQLiteConnection.java:301)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    ... 1 more

When JobManager is initialised during tests, we're passing the Configuration with .inTestMode() , and every call to addJobInBackground we override the method to make it run synchronously.

the tests still pass, but its a bit boring to see this all the time.

do you know what it could be? thanks!

RicardoBelchior avatar Nov 11 '16 10:11 RicardoBelchior

@RicardoBelchior Are you solved this problem?

iandreyshev avatar Mar 14 '18 12:03 iandreyshev

I didn't solve it, but it's not a problem for me anymore, because I don't trigger any call to JobPriorityQueue during my tests. I've put all calls to this library under an interface and during tests , the implementation of that interface does nothing.

RicardoBelchior avatar Mar 14 '18 14:03 RicardoBelchior