+ def 'Module sync advised cm handles starts with no available threads.'() {
+ given: 'sync utilities returns a advise cm handles'
+ mockSyncUtils.getAdvisedCmHandles() >> createDataNodes(1)
+ and: 'the executor first has no threads but has one thread on the second attempt'
+ spiedAsyncTaskExecutor.getAsyncTaskParallelismLevel() >>> [ 0, 1 ]
+ when: ' module sync is started'
+ objectUnderTest.moduleSyncAdvisedCmHandles()
+ then: 'it performs one task'
+ 1 * spiedAsyncTaskExecutor.executeTask(*_)
+ }
+
+ def 'Module sync advised cm handles already handled.'() {
+ given: 'sync utilities returns a advise cm handles'
+ mockSyncUtils.getAdvisedCmHandles() >> createDataNodes(1)
+ and: 'the executor has a thread available'
+ spiedAsyncTaskExecutor.getAsyncTaskParallelismLevel() >> 1
+ and: 'the semaphore cache indicates the cm handle is already being processed'
+ mockModuleSyncStartedOnCmHandles.putIfAbsent(*_) >> 'Started'
+ when: ' module sync is started'
+ objectUnderTest.moduleSyncAdvisedCmHandles()
+ then: 'it does NOT execute a task to process the (empty) batch'
+ 0 * spiedAsyncTaskExecutor.executeTask(*_)
+ }
+
+ def 'Module sync with previous cm handle(s) left in work queue.'() {
+ given: 'there is still a cm handle in the queue'
+ moduleSyncWorkQueue.offer(new DataNode())
+ and: 'sync utilities returns many advise cm handles'
+ mockSyncUtils.getAdvisedCmHandles() >> createDataNodes(500)
+ and: 'the executor has plenty threads available'
+ spiedAsyncTaskExecutor.getAsyncTaskParallelismLevel() >> 10
+ when: ' module sync is started'
+ objectUnderTest.moduleSyncAdvisedCmHandles()
+ then: 'it does executes only one task to process the remaining handle in the queue'
+ 1 * spiedAsyncTaskExecutor.executeTask(*_)
+ }
+